iOS Camera Availability:
A Focus on Assumptions

Written by: on July 2

No matter how many strange issues you encounter, or how many times you think you’ve seen it all, it seems like there’s always one more scenario that you somehow haven’t encountered yet.

We recently ran into just such a situation with an app for one of our customers; a crash on launch in fact. Naturally, after we figured out the problem, we were eager to see if any apps in the App Store also had this issue, and were surprised to find that we weren’t alone.

A customer recently came to us reporting that in one of the latest ad hoc builds, the app was crashing on launch on one of their devices. We immediately installed the latest build on an iPad, launched, and…nothing. It ran fine. So we began going down the usual list of checks. Was this device properly provisioned? Was the profile valid? Was it something specific to their iOS version? The usual suspects checked out fine.

This app in particular accesses the camera on launch. Normally you can specify in the Info.plist what hardware your apps requires, preventing, for example, devices with no camera from installing an app that requires a camera. This app’s Info.plist did have the following specification:

    <key>UIRequiredDeviceCapabilities</key>
    <array>
        <string>still-camera</string>
    </array>

However, in our case, we were distributing over the air ad hoc builds, which don’t run that same check. Maybe the device didn’t have a camera? That wasn’t it either. It was an iPad 2. We also checked HockeyApp for any crash logs, but nothing was coming in, presumably because the app was crashing too early on for HockeyApp to get as far as submitting a crash log. Fortunately the customer was geographically close enough to bring the problematic iPad by for us to have a peek.

Once we built to the device from Xcode, we were able to see the following stack trace:

    Last Exception Backtrace:
    0   CoreFoundation                  0x311713e2 __exceptionPreprocess + 158
    1   libobjc.A.dylib                 0x38e6c95e objc_exception_throw + 26
    2   AVFoundation                    0x302f46e0 -[AVCaptureSession _addInputWithNoConnections:] + 980
    3   AVFoundation                    0x302f3e00 -[AVCaptureSession addInput:] + 160

That certainly looks like it has something to do with the camera. But this device clearly has a camera. Could the hardware be faulty? We went to launch the Camera.app to test the hardware only to discover… there was no Camera.app! What? How does a device with a camera not have a Camera.app? Ohhhhhh… And that is when it hit us.

Our fleeting hunch early on about the device not having a camera being an issue was partially right, just not in a way that we had thought about. We now had a new theory that we could quickly validate with a short trip to Settings. Under Settings > General > Restrictions, we found that the camera had been disabled. Technically there was iOS camera availability, but the app wasn’t allowed to access it, which was causing the crash.

Normally this sort of thing would have been caught by asking the device if it supported taking photos through the camera like so:

[UIImagePickerController isSourceTypeAvailable:UIImagePickerControllerSourceTypeCamera]

However, in this case the app was depending on the Info.plist to require that the app could only be installed on hardware that supported a camera. That, coupled with the fact that the app was using AVFoundation to take the picture, was causing the problem.

Now that the cause of the problem had been identified we went to work on fixing it. The section of code that was responsible for taking the picture is powered by the Zebra Crossing library. The code for setting up the AVCaptureSession starts out like this:

AVCaptureDevice *inputDevice = [AVCaptureDevice defaultDeviceWithMediaType:AVMediaTypeVideo];
AVCaptureDeviceInput *captureInput = [AVCaptureDeviceInput deviceInputWithDevice:inputDevice error:nil];
...
[self.captureSession addInput:captureInput];

Can you spot the problem there? If you answered NSError, you are correct. Not only is this code passing a nil instead of an NSError, it is not checking the returned AVCaptureDeviceInput before using it in the AVCaptureSession. To protect against this disabled camera scenario the code would have to look more like this:

NSError *error = nil;
AVCaptureDevice *inputDevice = [AVCaptureDevice defaultDeviceWithMediaType:AVMediaTypeVideo];
AVCaptureDeviceInput *captureInput = [AVCaptureDeviceInput deviceInputWithDevice:inputDevice error:&amp;error];
if (!captureInput) {
// Handle the NSError
}

We have submitted a patch to Zebra Crossing.

If your app makes use of the camera, have a look and see how it handles the camera restriction. Or if you just want to watch other people’s apps blow up, take a stroll through the Photo & Video category of the App Store and see what you can find.

Jay Graves

Jay is the Chief Technology Officer for Double Encore, Inc., a leading mobile development company. Jay’s expertise developing apps for some of the world's top brands has made him a respected leader in the space, with his work being featured on television, in iTunes and on devices inside Apple retail stores.

Article


Add your voice to the discussion: