iOS 7 introduced APIs to read bar codes using the camera. I could have easily overlooked it without this excellent post from doubleencore.

NSHipster provided sample code, but it missed some details to work. Here is a sample which does.

Sample code

@import AVFoundation; @interface CEViewController () <AVCaptureMetadataOutputObjectsDelegate> @property (strong) AVCaptureSession *captureSession; @end @implementation CEViewController - (void)viewDidLoad { [super viewDidLoad]; self.captureSession = [[AVCaptureSession alloc] init]; AVCaptureDevice *videoCaptureDevice = [AVCaptureDevice defaultDeviceWithMediaType:AVMediaTypeVideo]; NSError *error = nil; AVCaptureDeviceInput *videoInput = [AVCaptureDeviceInput deviceInputWithDevice:videoCaptureDevice error:&error]; if(videoInput) [self.captureSession addInput:videoInput]; else NSLog(@"Error: %@", error); AVCaptureMetadataOutput *metadataOutput = [[AVCaptureMetadataOutput alloc] init]; [self.captureSession addOutput:metadataOutput]; [metadataOutput setMetadataObjectsDelegate:self queue:dispatch_get_main_queue()]; [metadataOutput setMetadataObjectTypes:@[AVMetadataObjectTypeQRCode, AVMetadataObjectTypeEAN13Code]]; AVCaptureVideoPreviewLayer *previewLayer = [[AVCaptureVideoPreviewLayer alloc] initWithSession:self.captureSession]; previewLayer.frame = self.view.layer.bounds; [self.view.layer addSublayer:previewLayer]; [self.captureSession startRunning]; } #pragma mark AVCaptureMetadataOutputObjectsDelegate - (void)captureOutput:(AVCaptureOutput *)captureOutput didOutputMetadataObjects:(NSArray *)metadataObjects fromConnection:(AVCaptureConnection *)connection { for(AVMetadataObject *metadataObject in metadataObjects) { AVMetadataMachineReadableCodeObject *readableObject = (AVMetadataMachineReadableCodeObject *)metadataObject; if([metadataObject.type isEqualToString:AVMetadataObjectTypeQRCode]) { NSLog(@"QR Code = %@", readableObject.stringValue); } else if ([metadataObject.type isEqualToString:AVMetadataObjectTypeEAN13Code]) { NSLog(@"EAN 13 = %@", readableObject.stringValue); } } } @end

Pitfalls

The issue I had with NSHipster’s sample code is the delegate method was not called at all. I quickly understood this was because the AVCaptureMetadataOutput must be configured to tell which metadata to recognise.

But, and this was not obvious to me, -[AVCaptureMetadataOuput setMetadataObjectTypes:] must be called after -[AVCaptureSession addOutput:]. Otherwise, the following message shows in the console:

*** Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: '*** -[AVCaptureMetadataOutput setMetadataObjectTypes:] - unsupported type found. Use -availableMetadataObjectTypes.'

I did try to look at the output of -availableMetadataObjectTypes, and it returns an empty array.

Therefore, -addOuput: must be called before setMetadataObjectTypes. In retrospect, it makes sense: the output object must know it is linked to a video session to know which metadata it may provide.