The iPhone OS started off with a single device, the original iPhone, ala, iPhone 1,1. In the last three years, Apple has been innovating hard and introduced multiple devices running the same operating system with different capabilities. For example, the original iPod touch, iPod 1,1 wasn’t able to record audio input, and till date iPod Touch cannot vibrate as a result of an alert. There are many such subtle differences between different iOS devices and it’s the developer’s effort to make sure that the code runs properly on all devices.

Problems using the device model

Most developers have resorted to reading the hw.machine parameter to check if the device is an iPhone or iPod touch. Another lousy way is to read the [UIDevice currentDevice].model. The problem with these methods are that, you are essentially comparing the device name with a hard-coded value. As such, code like


if(![[UIDevice currentDevice].model isEqualToString:@"iPhone"])
{
UIAlertView *alertView = [UIAlertView alloc] initWithTitle:@"Error" 
message:@"Microphone not present" 
delegate:self 
cancelButtonTitle:@"Dismiss" 
otherButtonTitles: nil];
[alertView show];
[alertView release];
}

will fail miserably on iPad and on iPod Touch if an external microphone is connected. On similar lines, you cannot assert something like,

if(![[UIDevice currentDevice].model isEqualToString:@"iPhone 4,1"])
{
// lets start a video conferencing session here.
}

Today, this code will work perfectly, but with next release of the iPod Touch or the iPad that might probably have a front facing camera, your app (or the video conferencing feature) will no longer work on the new device though it’s (might be) capable of video conferencing.

There are Apple defined ways to check the hardware capabilities rather than assuming features based on the model string. In this tutorial, we will walk you through the various pitfalls that any iOS developer may encounter and provide solutions on how to combat them. Also provided is a complete source code and an elegant, drop dead simple to use device capabilities helper class.

Gonna Vibrate the device to alert the user? Read this first

Apple has documented two functions that would vibrate the iPhone. But vibration hardware is present only on iPhones (at least currently). So how will you alert your user who uses the app on iPad or iPod touches? Clearly, checking the model is not the way to go. Apple has provided an elegant solution to this. There are two seemingly similar functions that take a parameter kSystemSoundID_Vibrate

1) AudioServicesPlayAlertSound(kSystemSoundID_Vibrate);
2) AudioServicesPlaySystemSound(kSystemSoundID_Vibrate);

Both the functions vibrate the iPhone. But when you use the first function on devices that don’t support vibration, it plays a beep sound. The second function on the other hand does nothing on unsupported devices. So if you are going to vibrate the device continuously, as a alert, common sense says, use function 2.

Phone Call Capable?

Checking if a device is capable of making a phone call is as easy as making a function call to check if the device can handle URLs of type “tel://”

Again, in future if there is a 7″ iProduct that sits somewhere in between the iPad and iPhone and has call capabilities, this method will work on that device as well.

As a side note on usability, avoid showing an error like,

Sorry, you are not running on iPhone, This feature requires an iPhone.


if your app uses this feature to detect a phone from an iPod. In most cases, you show a telphone number and have a call button to make a phone call. On iPod Touch or other devices, just hide that button. That gives a better user experience than a error message. The user already knows that he doesn’t have an iPhone and his device cannot make a phone call. You don’t have to repeat that.

Email capability

Email is a bit tricky thing. You cannot always assume the all devices will support in-app email. If you read my previous tutorial on in-app email, I would have mandated to check the email capability using

[MFMailComposeViewController canSendMail]

This class is available from iOS 3.0. You might think that, because I always set the iPhone OS Deployment target to 3.0, I don’t have to check this and can directly present the modal email sheet.  But the code will crash when the user has no email configuration setup in the Mail app. As such, you should always check for this capability before you show the in-app email sheet.

SMS capability

For SMS support, you can either use [MFMessageComposeViewController canSendText] or better check if the device can handle URLs of type “sms://”

The reason I would suggest using the latter is, MFMessageComposeViewController isn’t available on iPhone OS 3.2 and below. You app might crash if you try to send a non-existant message to a non-existant class.

Camera/Flash/Video support

Most of the camera related availability support is exposed through the UIImagePickerController. The UIImagePickerController has class methods that can be used to check if there is a camera hardware present in the device. Do note that, it’s important to check even for the availability of the Photo Library before you ask the user to pick photos. For example, if the user’s iPhone is new and he doesn’t have any photos in his Photo Library, it’s better to show an error rather than crash the application. As such, the method below returns NO when there is no photos in the user’s library.

[UIImagePickerController isSourceTypeAvailable:UIImagePickerControllerSourceTypePhotoLibrary];

A bit tricker thing is detection of Video Camera. You can detect the presence of a video camera in a iOS device using the following method.

- (BOOL) isVideoCameraAvailable
{
	UIImagePickerController *picker = [[UIImagePickerController alloc] init];
	NSArray *sourceTypes = [UIImagePickerController availableMediaTypesForSourceType:picker.sourceType];
	[picker release];
 
	if (![sourceTypes containsObject:(NSString *)kUTTypeMovie ]){
 
		return NO;
	}
 
	return YES;
}

Gyroscope support

With iPhone 4, Apple added yet another hardware to the iPhone, namely the Gyroscope. Using Gyroscope. The Gyroscope related functions are grouped together under the CoreMotion framework. The CMMotionManager has a property called gyroAvailable to check if the device has gyroscope. Good news here is that, even if you make calls to CMMotionManager related to Gyroscope, the SDK converts them no-op on unsupported devices.

- (BOOL) isGyroscopeAvailable
{
#ifdef __IPHONE_4_0
	CMMotionManager *motionManager = [[CMMotionManager alloc] init];
	BOOL gyroAvailable = motionManager.gyroAvailable;
	[motionManager release];
	return gyroAvailable;
#else
	return NO;
#endif
 
}

Retina Display?

Use the [UIScreen mainScreen].scale to check if the display is retina capable. The scale property isn’t available on all SDKs. Use respondsToSelector methods for checking the availability

+ (BOOL) isRetinaDisplay
{
	int scale = 1.0;
	UIScreen *screen = [UIScreen mainScreen];
	if([screen respondsToSelector:@selector(scale)])
		scale = screen.scale;
 
	if(scale == 2.0f) return YES;
	else return NO;
}

Multitasking support

Not all devices support multitasking even on iOS 4. The iPhone 3G, and the iPod touch 2nd generation can be upgraded to iOS 4, but still will not get any of the multitasking or fast app switching benefits. I’m just re-writing Apple’s own code here

- (BOOL) isMultitaskingCapable
{
	UIDevice* device = [UIDevice currentDevice];
	BOOL backgroundSupported = NO;
	if ([device respondsToSelector:@selector(isMultitaskingSupported)])
		backgroundSupported = device.multitaskingSupported;
 
	return backgroundSupported;
}

Source Code

To make life easier, I’ve put together a sample application and a helper class that demonstrates most of these capabilities. You can download it from here or go to github. Again, it’s a singleton class based on my previous xcode singleton template

MKDeviceHelper.zip

Using this Class


Just drag the two files MKDeviceHelper.h and it’s counterpart. Add the frameworks, AVFoundation, MessageUI, AudioToolbox, CoreLocation, MobileCoreServices and CoreMotion frameworks. Build your app and you are good to go. If you can’t find any of the XCode. The current version works fine on XCode 3.2.3

Note that these frameworks are dynamically linked. They will in no way bloat your app.

Licensing

My only request is don’t pass of this code as your own. Fork it or do whatever you want, commercial/non-commercial but retain a reference to this blog post in the code and don’t remove the URL in the source code files.

Hope you find this helpful


Mugunth

Follow me on Twitter

  • http://www.kulnova.vn Thang Tran

    I have to say thank you very much for the tutorial, as I am working on a similar project that needs these skills.

    Thanks so so much ;)

  • amine

    will fail miserably on iPad and on iPod Touch if an external microphone is connected.
    —>
    We have microphone in iPad !!
    (first model on market 16Gb Wifi)
    I used skype on iPad, and it works perfectly

  • Pingback: Testar capacidade de um aparelho iOS

  • http://www.exquisitewatches.co.uk TW Steel watches

    Very good i am very thankful for the knowledge. A inspirational example of information.

  • Partha

    Hi, Maybe I am missing something here. At the time of instantiating the class(MKHelper), there is no reference to autorelease. Also the constructor does not have init as the prefix(though this is recommended this is not necessary). I wonder how would the object be release though there would be only one instance. Even the template seem to have the format.
    Also what would be the advantage of declaring the method isRetinaDisplay as a class method (unlike others which are defined as instance methods)
    Since the local variable _instance is a static one, what additional benefit we get by synchronizing the thread (I think it might be none in iOS while it might bring value with Mac ).
    I am newbie to the Mac /iOS/Objective C world and hence my questions be naive.

  • Abhay

    Hello Everyone!!!!!!!! Could any programmer tell me the way to extract the header information of images taken by the camera so that i can incorporate that in my image.

  • Mike

    Here’s a question: what do you mean by “handle” in this sentence: as easy as making a function call to check if the device can handle URLs of type “tel://”? How do I check that?

    Thanks for this, it is very helpful.

    • http://twitter.com/Lukas_CZ_dev Lukáš Petr

      He probably means this:             [[UIApplication sharedApplication] canOpenURL:[NSURL URLWithString:@"tel://"]];

  • http://wrightcode.com JWright

    I’ve been told that the isRetinaDisplay solution will also respond as TRUE for the iPad when it’s running iPhone apps at 2x.

  • http://www.facebook.com/coffelius Gabriel Ortega

    isVideoCameraAvailable returns YES in iPad 1. Since iOS 4.0 we can use isCameraDeviceAvailable class method of UIImagePickerController:

    [UIImagePickerController
    isCameraDeviceAvailable:UIImagePickerControllerCameraDeviceFront];

  • Pingback: How do I distinguish iPad 1 from iPad 2 in Cocoa? - Applerr.com All about Apple Products - Applerr.com All about Apple Products

  • Pingback: Creating a Dual-Screen AirPlay Experience for iOS and Apple TV | Redfin Developers' Blog

  • Bioduds

    NO! Wrong!

  • Michael Archbold

    Is there a way to test if the device has the vibrate hardware, other than just assuming iPods/iPads don’t?