MRZ


Examples Source Code

The source code of all example use cases of the ID Plugin can be found in the iOS SDK Bundle

Plugin Description

Simultaneous Barcode Scanning

Starting from SDK 3.8 Anyline supports simultaneous barcode scanning for any Plugin. Additional Information on how to implement this on iOS can be found at Simultaneous Barcode Scanning

The Anyline ID Plugin provides the functionality to scan passports and other IDs holding a Machine Readable Zone (MRZ) and Austrian Driving Licenses.

For each scan result, the Plugin generates an Identification object, containing all relevant information as well as the image of the scanned document.

The description of all parameters can be found at Plugins > ID

How to implement the ID Plugins

This is a step by step guide on how to implement the ID Plugins in iOS.

Add the Anyline ID Plugins to your UIViewController

The first step is to add the three main components of every Anyline use case: the Scan Plugin (handling the scanning part), the Scan View Plugin (handling the UI) and the Scan View (handling camera, flash and overall communication between Anyline components).

Add the Anyline ID Plugins
// The Anyline module used to scan machine readable zones
@property (nonatomic, strong) ALIDScanViewPlugin *mrzScanViewPlugin;
@property (nonatomic, strong) ALIDScanPlugin *mrzScanPlugin;
@property (nullable, nonatomic, strong) ALScanView *scanView;

Anyline ScanPlugin

Initialise the Scan Plugin

You have to initialise the plugin with your Anyline License Key Generation, an ID (String) and the delegate inside viewDidLoad.

Initialise the Scan Plugin
    NSError *error = nil;

    self.mrzScanPlugin = [[ALIDScanPlugin alloc] initWithPluginID:@"ModuleID" licenseKey:kMRZLicenseKey delegate:self idConfig:mrzConfig error:&error];
    NSAssert(self.mrzScanPlugin, @"Setup Error: %@", error.debugDescription);

Anyline ScanViewPlugin

Initialise the Scan View Plugin

After initialising the scanPlugin, the next step is to create the scanViewPlugin with the scanPlugin you just created. The ScanViewPlugin will handle and display the UI used for scanning.

Initialise the Scan View Plugin
    self.mrzScanViewPlugin = [[ALIDScanViewPlugin alloc] initWithScanPlugin:self.mrzScanPlugin];

Set the ScanViewPluginConfig

The View Configuration configurates the look and feel of the scanning process. You can set it the following way:

Set the ScanViewPluginConfig
NSString *confPath = [[NSBundle mainBundle] pathForResource:@"mrz_capture_config" ofType:@"json"];
ALScanViewPluginConfig *scanViewPluginConfig = [ALScanViewPluginConfig configurationFromJsonFilePath:confPath];

self.idScanViewPlugin = [[ALIDScanViewPlugin alloc] initWithScanPlugin:self.idScanPlugin
                                              scanViewPluginConfig:scanViewPluginConfig];

Limitations for the ID Plugin

The Ratio is fixed and optimised for the best results

Anyline ScanView

Initialise the Scan View

The last Anyline Object you have to create is the so called scanView, it will handle the camera, the flash and manage the previously created ScanViewPlugin and ScanPlugin. You need to instantiate the scanView with the previous created scanViewPlugin and the frame it should occupy. Usually, that’s the bounds of the screen.

Initialise the Scan View
    self.scanView = [[ALScanView alloc] initWithFrame:frame scanViewPlugin:self.mrzScanViewPlugin];

Adding the Scan View to the View Hierarchy and start the Camera Feed

To add the scanView to your view hierarchy add the following in viewDidLoad:

Adding the view and starting the camera
    // After setup is complete we add the module to the view of this view controller
    [self.view addSubview:self.scanView];
    //Start Camera:
    [self.scanView startCamera];

Final steps to configure and use the ID Plugin

New in version 3.23.0.

Enabling StrictMode

By default the strict mode is disabled. If the strict mode is enabled, The Anyline SDK will only return a result when allCheckDigitsValid is true.

Enable Strict Mode
ALMRZConfig *mrzConfig = [[ALMRZConfig alloc] init];
[mrzConfig setStrictMode:YES];
//or
ALMRZConfig *mrzConfig = [[ALMRZConfig alloc] init];
mrzConfig.strictMode = true;

//Instantiate the ID Plugin with this config

Hint

The strictMode property is optional and will be only used by Anyline if it is set when instantiating the ID Plugin idScanPlugin initWithPluginID:licenseKey:delegate:idConfig:error:.

New in version 3.25.1.

Enabling CropAndTransformID

By default cropAndTransformID is disabled. If the cropAndTransformID is enabled, Anyline will return the cropped and transformed image of the scanned identification document by anylineIDScanPlugin:didFindResult: instead of the usual cutout image. See mrz_module_crop_and_transform_id for more information.

Enable cropAndTransformID`
ALMRZConfig *mrzConfig = [[ALMRZConfig alloc] init];
[mrzConfig setCropAndTransformID:YES];
//or
ALMRZConfig *mrzConfig = [[ALMRZConfig alloc] init];
mrzConfig.cropAndTransformID = true;

//Instantiate the ID Plugin with this config

The ID Delegate

In the MRZ Plugin of Anyline SDK, you will be provided the final results via delegate calls.

In case of the ID Plugin, the protocol is called ALIDPluginDelegate. This section describes the ALIDPluginDelegate callbacks in detail.

anylineIDScanPlugin:didFindResult:

This is the main callback method for the ID Plugin. It is called once a valid result has been found.

The callback returns the result as ALIDResult, which contains an ` object, the allCheckDigitsValid flag, as well as an image of the result.

Result Delegate
- (ALResultEntry *)resultEntryWithDate:(NSDate *)date dateString:(NSString *)dateString title:(NSString *)title {
    if (![self checkDateIfAvailable:date dateString:dateString]) {
        return [[ALResultEntry alloc] initWithTitle:title value:nil];
    }
    
    NSString *value = [self formatDate:date];
    return [[ALResultEntry alloc] initWithTitle:title value:value isAvailable:(date)];
}

- (BOOL)checkDateIfAvailable:(NSDate *)date dateString:(NSString *)dateString {
    if (!date && dateString.length == 0) {
  • ALIDResult scanResult
Field Type Nullable Description
result ALMRZIdentification The detected result as an object of typ ALIdentification
image UIImage The image the result was found on. The image is cropped to the cutout
fullImage UIImage The full image the result was found on.
confidence NSInteger The confidence of the SDK in the detected result
allCheckDigitsValid BOOL Returns if all check digits in the MRZ are valid.
Identification

The detected MRZ fields are grouped within an ALMRZIdentification object.
The object contains the following fields:

documentType
Type Description Parameter Explanation
NSString The Type of the MROTD Document Type
documentNumber
Type Description Parameter Explanation
NSString The Number of the MROTD Document Number
surNames
Type Description Parameter Explanation
NSString The surname(s) of the Document holder Surnames
givenNames
Type Description Parameter Explanation
NSString The first name(s) of the Document holder Given Names
issuingCountryCode
Type Description Parameter Explanation
NSString The issuing country of the MROTD Issuing Country Code
nationalityCountryCode
Type Description Parameter Explanation
NSString The nationality of the Document holder Nationality Country Code
dayOfBirth
Type Description Parameter Explanation
NSString The day of birth of the Document holder Day of Birth
dayOfBirthDateObject
The following assumption are made: dayOfBirthDateObject will return 1900 century if the parsed returned dateOfBirth string is bigger then TODAY, otherwise it will have 2000 century.
Type Description Parameter Explanation
NSDate The day of birth of the Document holder parsed as a date object Day of Birth
expirationDate
Type Description Parameter Explanation
NSString The Expriation Date of the MROTD Expiration Date
expirationDateObject
The expirationDateObject will return 2000 century if it is smaller than 1970 (the year when countries began to issue MRZ) or if the expirationDate is before dateOfBirth, otherwise 1900 century will be returned.
Type Description Parameter Explanation
NSDate The expiration date of the Document holder parsed as a date object Expiration Date
sex
Type Description Parameter Explanation
NSString The Sex of the Document holder Sex
personalNumber
Type Description Parameter Explanation
NSString Additional information Personal Number
personalNumber2
Type Description Parameter Explanation
NSString Additional information Personal Number 2
checkdigitNumber
Type Description Parameter Explanation
NSString Validates the documentNumber Checkdigit for the Document Number
checkdigitExpirationDate
Type Description Parameter Explanation
NSString Validates the expirationDate Checkdigit for Expiration Date
checkdigitDayOfBirth
Type Description Parameter Explanation
NSString Validates the dayOfBirth Checkdigit for Day of Birth
checkdigitFinal
Type Description Parameter Explanation
NSString Validates the the upper and middle (TD1), or the lower line (TD2, TD3) of the MROTD Checkdigit Final
checkDigitPersonalNumber
Type Description Parameter Explanation
NSString Validates the personalNumber Checkdigit for the Personal Number
mrzString
Type Description Parameter Explanation
NSString The full MRZ String including line breaks MRZ String

New in version 5.

address
Type Description Parameter Explanation
String Address presented in GERMAN ID Address on German IDs

New in version 6.

issuingDate
Type Description Parameter Explanation
NSString The issuing date of the Document holder Issue Date on supported Passports
issuingDateObject
Type Description Parameter Explanation
NSDate The issuing date of the Document holder parsed as a date object Issue Date on supported Passports

New in version 3.13.0.

Enabling or Disabling the Reporting

The reporting of the (intermediate) results is turned off for this Plugin.

Starting Scanning

Hint

Use [scanView startCamera] within viewDidload before starting Anyline.

After everything is initialised, you can start the scanning process, by calling startAndReturnError: on the plugin.

It is advised to place this call in the viewDidAppear: lifecycle method of the UIViewController.

Starting the plugin
    NSError *error;
    BOOL success = [self.mrzScanViewPlugin startAndReturnError:&error];
    if( !success ) {
        // Something went wrong. The error object contains the error description
        [[[UIAlertView alloc] initWithTitle:@"Start Scanning Error"
                                    message:error.debugDescription
                                   delegate:self
                          cancelButtonTitle:@"OK"
                          otherButtonTitles:nil] show];
    }

Stop Scanning

To stop the scanning process, call stopAndReturnError: on the plugin.

To make sure the SDK is properly stopped upon leaving the Activity, make sure to place stopAndReturnError: in the viewWillDisappear: lifecycle method of the UIViewController.

Stopping the plugin
/*
 Cancel scanning to allow the module to clean up
 */
- (void)viewWillDisappear:(BOOL)animated {
    [self.mrzScanViewPlugin stopAndReturnError:nil];
}

Full Example Activity

Full MRZ iOS Example
#import "ALMRZScanViewController.h"
#import "ALIdentificationView.h"
#import <Anyline/Anyline.h>
#import "NSUserDefaults+ALExamplesAdditions.h"
#import "ALAppDemoLicenses.h"
#import "ALResultViewController.h"

// This is the license key for the examples project used to set up Aynline below
NSString * const kMRZLicenseKey = kDemoAppLicenseKey;
// The controller has to conform to <AnylineMRZModuleDelegate> to be able to receive results
@interface ALMRZScanViewController ()<ALIDPluginDelegate, ALInfoDelegate>

// The Anyline module used to scan machine readable zones
@property (nonatomic, strong) ALIDScanViewPlugin *mrzScanViewPlugin;
@property (nonatomic, strong) ALIDScanPlugin *mrzScanPlugin;
@property (nullable, nonatomic, strong) ALScanView *scanView;

@end

@implementation ALMRZScanViewController
/*
 We will do our main setup in viewDidLoad. Its called once the view controller is getting ready to be displayed.
 */
- (void)viewDidLoad {
    [super viewDidLoad];
    // Set the background color to black to have a nicer transition
    self.view.backgroundColor = [UIColor blackColor];
    self.title = @"MRZ";
    
    // Initializing the module. Its a UIView subclass. We set the frame to fill the whole screen
    CGRect frame = [[UIScreen mainScreen] applicationFrame];
    frame = CGRectMake(frame.origin.x, frame.origin.y + self.navigationController.navigationBar.frame.size.height, frame.size.width, frame.size.height - self.navigationController.navigationBar.frame.size.height);
    
    ALMRZConfig *mrzConfig = [[ALMRZConfig alloc] init];
    
    NSError *error = nil;

    self.mrzScanPlugin = [[ALIDScanPlugin alloc] initWithPluginID:@"ModuleID" licenseKey:kMRZLicenseKey delegate:self idConfig:mrzConfig error:&error];
    NSAssert(self.mrzScanPlugin, @"Setup Error: %@", error.debugDescription);
    [self.mrzScanPlugin addInfoDelegate:self];
    
    self.mrzScanViewPlugin = [[ALIDScanViewPlugin alloc] initWithScanPlugin:self.mrzScanPlugin];
    NSAssert(self.mrzScanViewPlugin, @"Setup Error: %@", error.debugDescription);
    
    self.scanView = [[ALScanView alloc] initWithFrame:frame scanViewPlugin:self.mrzScanViewPlugin];
    
    self.scanView.flashButtonConfig.flashAlignment = ALFlashAlignmentTopLeft;
    self.mrzScanViewPlugin.translatesAutoresizingMaskIntoConstraints = NO;
    
    self.controllerType = ALScanHistoryMrz;
    
    // After setup is complete we add the module to the view of this view controller
    [self.view addSubview:self.scanView];
    //Start Camera:
    [self.scanView startCamera];
    
    [self.view sendSubviewToBack:self.scanView];
    
    [[self view] addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"H:|[scanView]|" options:0 metrics:nil views:@{@"scanView" : self.scanView}]];
    
    id topGuide = self.topLayoutGuide;
    [[self view] addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"V:[topGuide]-0-[scanView]|" options:0 metrics:nil views:@{@"scanView" : self.scanView, @"topGuide" : topGuide}]];
    
    [self startListeningForMotion];
}

/*
 This method will be called once the view controller and its subviews have appeared on screen
 */
-(void)viewDidAppear:(BOOL)animated {
    [super viewDidAppear:animated];

    // We use this subroutine to start Anyline. The reason it has its own subroutine is
    // so that we can later use it to restart the scanning process.
    [self startAnyline];
    
    //Update Position of Warning Indicator
    [self updateWarningPosition:
     self.mrzScanViewPlugin.cutoutRect.origin.y +
     self.mrzScanViewPlugin.cutoutRect.size.height +
     self.mrzScanViewPlugin.frame.origin.y +
     120];
}

/*
 Cancel scanning to allow the module to clean up
 */
- (void)viewWillDisappear:(BOOL)animated {
    [self.mrzScanViewPlugin stopAndReturnError:nil];
}


/*
   This method is used to tell Anyline to start scanning. It gets called in 
   viewDidAppear to start scanning the moment the view appears. Once a result 
   is found scanning will stop automatically (you can change this behaviour 
   with cancelOnResult:). When the user dismisses self.identificationView this
   method will get called again.
 */
- (void)startAnyline { 
    NSError *error;
    BOOL success = [self.mrzScanViewPlugin startAndReturnError:&error];
    if( !success ) {
        // Something went wrong. The error object contains the error description
        [[[UIAlertView alloc] initWithTitle:@"Start Scanning Error"
                                    message:error.debugDescription
                                   delegate:self
                          cancelButtonTitle:@"OK"
                          otherButtonTitles:nil] show];
    }
    
    self.startTime = CACurrentMediaTime();
}

#pragma mark -- AnylineMRZModuleDelegate

/*
 This is the main delegate method Anyline uses to report its results
 */
- (void)anylineIDScanPlugin:(ALIDScanPlugin *)anylineIDScanPlugin didFindResult:(ALIDResult *)scanResult {
    [self.mrzScanViewPlugin stopAndReturnError:nil];
    NSMutableString * result = [NSMutableString string];
    [result appendString:[NSString stringWithFormat:@"Document Type: %@\n", [scanResult.result documentType]]];
    [result appendString:[NSString stringWithFormat:@"Document Number: %@\n", [scanResult.result documentNumber]]];
    [result appendString:[NSString stringWithFormat:@"Surnames: %@\n", [scanResult.result surNames]]];
    [result appendString:[NSString stringWithFormat:@"Given Names: %@\n", [scanResult.result givenNames]]];
    [result appendString:[NSString stringWithFormat:@"Issuing Country Code: %@\n", [scanResult.result issuingCountryCode]]];
    [result appendString:[NSString stringWithFormat:@"Nationality Country Code: %@\n", [scanResult.result nationalityCountryCode]]];
    [result appendString:[NSString stringWithFormat:@"Day Of Birth: %@\n", [scanResult.result dayOfBirth]]];
    [result appendString:[NSString stringWithFormat:@"Expiration Date: %@\n", [scanResult.result expirationDate]]];
    [result appendString:[NSString stringWithFormat:@"Sex: %@\n", [scanResult.result sex]]];
    [result appendString:[NSString stringWithFormat:@"Check Digit Number: %@\n", [scanResult.result checkdigitNumber]]];
    [result appendString:[NSString stringWithFormat:@"Check Digit Expiration Date: %@\n", [scanResult.result checkdigitExpirationDate]]];
    [result appendString:[NSString stringWithFormat:@"Check Digit Day Of Birth: %@\n", [scanResult.result checkdigitDayOfBirth]]];
    [result appendString:[NSString stringWithFormat:@"Check Digit Final: %@\n", [scanResult.result checkdigitFinal]]];
    [result appendString:[NSString stringWithFormat:@"Personal Number: %@\n", [scanResult.result personalNumber]]];
    [result appendString:[NSString stringWithFormat:@"Check Digit Personal Number: %@\n", [scanResult.result checkDigitPersonalNumber]]];
    
    [super anylineDidFindResult:result barcodeResult:@"" image:scanResult.image scanPlugin:anylineIDScanPlugin viewPlugin:self.mrzScanViewPlugin completion:^{
        
        NSMutableArray <ALResultEntry*> *resultData = [[NSMutableArray alloc] init];
        [resultData addObject:[[ALResultEntry alloc] initWithTitle:@"Given Name" value:[scanResult.result givenNames]]];
        [resultData addObject:[[ALResultEntry alloc] initWithTitle:@"Surname" value:[scanResult.result surNames]]];
        [resultData addObject:[[ALResultEntry alloc] initWithTitle:@"Sex" value:[scanResult.result sex]]];
        [resultData addObject:[self resultEntryWithDate:[scanResult.result dayOfBirthDateObject] dateString:[scanResult.result dayOfBirth] title:@"Date of Birth"]];
        [resultData addObject:[[ALResultEntry alloc] initWithTitle:@"Document Type" value:[scanResult.result documentType]]];
        [resultData addObject:[[ALResultEntry alloc] initWithTitle:@"Document Number" value:[scanResult.result documentNumber]]];
        [resultData addObject:[self resultEntryWithDate:[scanResult.result expirationDateObject] dateString:[((ALMRZIdentification *)scanResult.result) expirationDate] title:@"Expiration Date"]];
        [resultData addObject:[[ALResultEntry alloc] initWithTitle:@"Nationality" value:[scanResult.result nationalityCountryCode]]];
        
        ALResultViewController *vc = [[ALResultViewController alloc] initWithResultData:resultData image:scanResult.image optionalImageTitle:@"Detected Face Image" optionalImage:[scanResult.result faceImage]];
        [self.navigationController pushViewController:vc animated:YES];
    }];
}

- (ALResultEntry *)resultEntryWithDate:(NSDate *)date dateString:(NSString *)dateString title:(NSString *)title {
    if (![self checkDateIfAvailable:date dateString:dateString]) {
        return [[ALResultEntry alloc] initWithTitle:title value:nil];
    }
    
    NSString *value = [self formatDate:date];
    return [[ALResultEntry alloc] initWithTitle:title value:value isAvailable:(date)];
}

- (BOOL)checkDateIfAvailable:(NSDate *)date dateString:(NSString *)dateString {
    if (!date && dateString.length == 0) {
        return NO;
    }
    return YES;
}

- (NSString *)formatDate:(NSDate *)date {
    if (!date) {
        return @"Date not valid";
    }
    return [NSDateFormatter localizedStringFromDate:date dateStyle:NSDateFormatterMediumStyle timeStyle:NSDateFormatterNoStyle];
}

@end