In a previous post, Better Code: Add email functionality to your Apple iOS apps, we saw how to add email functionality to an app to let your users contact you for support. Because apps can behave differently on different device types and iOS levels, it’s often very useful to know exactly what these specifics are when a user reports a problem. In this post we’ll see how to programmatically determine the device type and iOS version your app is running on. Combined with the code from the last post, you can enable your apps to generate support emails with this important information automatically included.
Project
Open Xcode and from the File menu, select New, and then New Project. A new workspace window will appear, and you will be presented with several application templates to choose from.
On the left-hand side, select Application from the iOS section. Select Single View Application and click the Next button. (Figure A)
Figure A
On the next pane, enter SupportInfo for the Product Name and com.myappcompany as the Company Identifier. (Feel free to substitute your own product name and company identifier).
Leave the Class Prefix as is. It should be blank – XYZ is just a placeholder hint.
Make sure only Use Automatic Reference Counting and Use Storyboards are checked.
Set Device Family to iPhone, and click Next. (Figure B)
Figure B
A new pane will appear asking you where you would like to save the project. XCode will create a SupportInfo project folder inside the directory you select.
Once the project is created, Xcode will open a workspace window with your new project.
Creating the User Interface
Click on MainStoryboard.storyboard in the Files and Groups pane. After it opens in Interface Builder, select the View object, click on the Attributes Inspector and then change the view’s background color to a color of your choosing.
Next go to the Object Library located on the lower right-hand side of the workspace and drag a pair of Label objects onto the view arranged as shown in Figure C. Change the text of the left-most label to “Device Type:”
Figure C
Drag another pair of labels onto the view directly beneath the first and change the text of the left-most label to “iOS Version:”. Figure D shows the situation so far.
Figure D
Next, click on the Assistant Editor in the upper right corner of the Xcode Interface. (Figure E)
Figure E
The Assistant Editor will display ViewController.h in an adjacent pane as shown in Figure F. Click on the label next to “Device Type:” to select it, then press the Control key and drag from the label to just under the word “@interface” in ViewController.h. When you release your finger from the mouse, the Connection dialog will appear. Type “deviceLabel” into the Name field and press Enter. You have now created an outlet named deviceLabel and connected it to the UILabel in the storyboard. Do the same for the label next to “iOS Version:” using the name “iOSLabel”.
Figure F
Click on the Standard Editor button to close the Assistant Editor.
Adding the code
For our first attempt, let’s start by modifying viewDidLoad: as follows:
- (void)viewDidLoad
{
[super viewDidLoad];
NSString *iOSVersion = [[UIDevice currentDevice] systemVersion];
self.iOSLabel.text = iOSVersion;
NSString *model = [[UIDevice currentDevice] model];
self.deviceLabel.text = model;
}
Build and run the app. The results will be more interesting if you target a real device rather than the simulator. Figure G shows the results on an iPhone 4 and iPhone 5.
Figure G
iPhone 4 and 5 results
UIDevice gives us fairly exact information about the iOS version, but notice that it can’t tell the difference between the two devices, which clearly won’t do for support purposes. Fortunately we can do better! Add the following two methods directly under viewDidLoad:
- (NSString *)platformRawString {
size_t size;
sysctlbyname("hw.machine", NULL, &size, NULL, 0);
char *machine = malloc(size);
sysctlbyname("hw.machine", machine, &size, NULL, 0);
NSString *platform = [NSString stringWithUTF8String:machine];
free(machine);
return platform;
}
- (NSString *)platformNiceString {
NSString *platform = [self platformRawString];
if ([platform isEqualToString:@"iPhone1,1"]) return @"iPhone 1G";
if ([platform isEqualToString:@"iPhone1,2"]) return @"iPhone 3G";
if ([platform isEqualToString:@"iPhone2,1"]) return @"iPhone 3GS";
if ([platform isEqualToString:@"iPhone3,1"]) return @"iPhone 4";
if ([platform isEqualToString:@"iPhone3,3"]) return @"Verizon iPhone 4";
if ([platform isEqualToString:@"iPhone4,1"]) return @"iPhone 4S";
if ([platform isEqualToString:@"iPhone5,1"]) return @"iPhone 5";
if ([platform isEqualToString:@"iPod1,1"]) return @"iPod Touch 1G";
if ([platform isEqualToString:@"iPod2,1"]) return @"iPod Touch 2G";
if ([platform isEqualToString:@"iPod3,1"]) return @"iPod Touch 3G";
if ([platform isEqualToString:@"iPod4,1"]) return @"iPod Touch 4G";
if ([platform isEqualToString:@"iPad1,1"]) return @"iPad 1";
if ([platform isEqualToString:@"iPad2,1"]) return @"iPad 2 (WiFi)";
if ([platform isEqualToString:@"iPad2,2"]) return @"iPad 2 (GSM)";
if ([platform isEqualToString:@"iPad2,3"]) return @"iPad 2 (CDMA)";
if ([platform isEqualToString:@"iPad3,1"]) return @"iPad 3 (WiFi)";
if ([platform isEqualToString:@"iPad3,2"]) return @"iPad 3 (4G,2)";
if ([platform isEqualToString:@"iPad3,3"]) return @"iPad 3 (4G,3)";
if ([platform isEqualToString:@"i386"]) return @"Simulator";
if ([platform isEqualToString:@"x86_64"]) return @"Simulator";
return platform;
}
Now modify viewDidLoad: as follows:
- (void)viewDidLoad
{
[super viewDidLoad];
NSString *iOSVersion = [[UIDevice currentDevice] systemVersion];
self.iOSLabel.text = iOSVersion;
self.deviceLabel.text = [self platformRawString];
}
And finally, add the following import statement at the top of the file.
#import <sys/sysctl.h>
When we build and run the app again the results are now much more interesting, as shown in Figure H. By using the low-level sysctlbyname system call in platformRawString, we get much more precise device information, but depending on the device, it tends to be a bit arcane.
Figure H
We can make the raw device information more useful by passing it through the platformNiceString method. The list of devices that this method can interpret is fairly complete, lacking only information for the latest 9″ iPad and iPad Mini models.
Modify viewDidLoad: as follows and build and run the app one more time:
- (void)viewDidLoad
{
[super viewDidLoad];
NSString *iOSVersion = [[UIDevice currentDevice] systemVersion];
self.iOSLabel.text = iOSVersion;
self.deviceLabel.text = [self platformNiceString];
}
As shown in Figure I, we have now exactly what we need.
Figure I
Unfortunately not everything an iOS programmer needs can be found in the iOS SDK. Low-level system calls like sysctlbyname are a great example of why it’s important to always be learning from a variety of sources like TechRepublic, Stack Overflow, and many other great sites on the web.
Hopefully this little tidbit will be helpful to you in supporting users of your iOS apps.