Migration Guide: MRZScanner SDK to barKoder SDK (iOS)

This guide explains how to replace the MRZScanner SDK with the barKoder SDK in an existing iOS app. It assumes MRZScanner is already integrated and you’re now migrating to barKoder while preserving as much of your current app structure and UI as possible.

The goal is to show you:

  • The key conceptual differences between MRZScanner and barKoder
  • How the MRZ result model changes
  • Which parts of your code you need to update


1. Migration checklist #


  1. Remove MRZScanner SDK frameworks and imports.
  2. Add barKoder (Barkoder.xcframework and BarkoderSDK.xcframework) (either download it from the website and add it manually or install it via SPM), set Enable Bitcode = NO, keep camera permission in Info.plist.
  3. Replace MRZScannerController with a BarkoderView property in your view controller.
  4. Configure BarkoderView with:

• BarkoderConfig(licenseKey:)

• decoderConfig.idDocument.enabled = true for MRZ/ID documents.

• Optional: config.vibrateOnSuccessEnabled, config.imageResultEnabled, etc. 


5. Update your VC to conform to BarkoderResultDelegate and implement scanningFinished(decoderResults:thumbnails:image:). 
6. Start scanning with try barkoderView.startScanning(self). 
7. Update all places that used MRZResultDataModel to now read MRZ fields from DecoderResult.extra ([AnyHashable: Any]).

2. Initialization & Licensing #

Before - MRZScannerSDK #

                import MRZScannerSDK

class MRZViewController: UIViewController, MRZScannerDelegate {
    private var mrzScannerController: MRZScannerController?

    override func viewDidLoad() {
        super.viewDidLoad()
        mrzScannerController = MRZScannerController()
        MRZScannerController.registerLicense(withKey: "YOUR_LICENSE_KEY")
        mrzScannerController!.delegate = self
        mrzScannerController!.setContinuousScanningEnabled(false)
        mrzScannerController!.setIgnoreDuplicates(true)
        mrzScannerController!.setScannerType(TYPE_MRZ)
        MRZScannerController.setIDActive(true)
        MRZScannerController.setPassportActive(true)
        MRZScannerController.setVisaActive(true)
        MRZScannerController.enableVibration(onSuccess: true)

        let currentVC = (navigationController != nil) ? navigationController : self
        currentVC?.addChild(mrzScannerController!)
        mrzScannerController?.initUI(currentVC) // fullscreen UI
    }

    func successfulScan(withResult result: MRZResultDataModel!) {
        // ...
    }
}
            

After - barKoder SDK #

                import UIKit
import BarkoderSDK

class BarkoderViewController: UIViewController, BarkoderResultDelegate {
    
    private let barkoderView: BarkoderView = {
        let view = BarkoderView()
        return view
    }()

    override func viewDidLoad() {
        super.viewDidLoad()
        setupUI()
        createBarkoderConfig()
        setActiveBarcodeTypes()
        setBarkoderSettings()
    }

    override func viewDidAppear(_ animated: Bool) {
        super.viewDidAppear(animated)
        do {
            try barkoderView.startScanning(self) // you can also call startScanning on a button click
        } catch {
            print("Error starting scanning: \(error)")
        }
    }

    // MARK: UI setup
    private func setupUI() {
        view.addSubview(barkoderView)
        setupConstraints()
    }

    private func setupConstraints() {
        barkoderView.translatesAutoresizingMaskIntoConstraints = false
        NSLayoutConstraint.activate([
            barkoderView.topAnchor.constraint(equalTo: view.topAnchor),
            barkoderView.bottomAnchor.constraint(equalTo: view.bottomAnchor),
            barkoderView.leadingAnchor.constraint(equalTo: view.leadingAnchor),
            barkoderView.trailingAnchor.constraint(equalTo: view.trailingAnchor)
        ])
    } // fullscreen

    // MARK: Config
    private func createBarkoderConfig() {
        barkoderView.config = BarkoderConfig(licenseKey: "YOUR_LICENSE_KEY") { licenseResult in
            print("Licensing SDK: \(licenseResult)")
        }
    }

    private func setActiveBarcodeTypes() {
        guard let decoderConfig = barkoderView.config?.decoderConfig else { return }
        decoderConfig.idDocument.enabled = true // MRZ / ID Document mode
    }

    private func setBarkoderSettings() {
        guard let config = barkoderView.config else { return }
        
        // Your MRZ -> barkoder "vibration" mapping:
        config.vibrateOnSuccessEnabled = true 
        
        // Optional: image result (thumbnails & full image)
        config.imageResultEnabled = true
        config.barcodeThumbnailOnResult = true
        
        // Behaviour similar to "single scan then stop"
        config.closeSessionOnResultEnabled = true
        
        // Continuous scanning / duplicates control
        config.decoderConfig?.decodingSpeed = DecodingSpeed(2); // slow
        config.barkoderResolution = .FHD; // FullHD

        // Setting region of interest (optional)
        // config.regionOfInterestVisible = true
        // try? config.setRegionOfInterest(CGRect(x: 0, y: 30, width: 100, height: 40)) 

        // Show the built-in close button on the scanner
        barkoderView.configureCloseButton(visible: true) {
            // Dismiss the screen/sheet where the Barkoder scanner was presented.
        }
    }

    // MARK: BarkoderResultDelegate
    func scanningFinished(_ decoderResults: [DecoderResult], thumbnails: [UIImage]?, image: UIImage?) {
        // To get the returned images (e.g. "document", "main", "picture", "signature") by name
        guard let firstResult = decoderResults.first else { return }
        
        let imagesByName: [String: UIImage] = (firstResult.images ?? []).reduce(into: [:]) { dict, item in
            guard let name = item.name, let image = item.image else { return }
            dict[name] = image
        }
        
        let mainImage = imagesByName["main"]
        let documentImage = imagesByName["document"]
        let pictureImage = imagesByName["picture"]
        let signatureImage = imagesByName["signature"]
    }
}
            

Note: You must have a valid license with IDDocument enabled. If you start the scanner with idDocument enabled but your license doesn’t include it, the scanner will return the result: “ID Document decoder type is not licensed!”. 


 In barKoder there are no separate settings for (ID, Visa, Passport). Enabling idDocument automatically:

Enables all ID types (ID, Passport, Visa)

Validates country codes

Supports upside-down (rotated) scanning by default. 


To stop the scanner you can call stopScanning()

3. MRZ result data: what changes and how to adapt #


3.1 What you used to get (MRZResultDataModel) #

From successfulScan(withResult:):

                func successfulScan(withResult result: MRZResultDataModel!) {
    // MRZScanner properties:
    result.document_type
    result.document_number
    result.given_names
    result.surnames
    result.nationality
    result.issuing_country
    result.sex
    result.date_of_birth_readable
    result.date_of_birth_raw
    result.expiration_date_readable
    result.expiration_date_raw
    result.estimated_issuing_date_readable
    result.estimated_issuing_date_raw
    result.optional_value
    result.personal_number_raw
}

            


3.2 What you now get (barKoder) #

In the extra field, the JSON contains three variants of the data:

•    “normal” – unformatted values (for example, the document type for a passport is “P”)

•    “formatted” – human-readable values (for example, the document type is “Passport”)

•    “raw” – the full MRZ string From scanningFinished:

                func scanningFinished(_ decoderResults: [DecoderResult],
                          thumbnails: [UIImage]?,
                          image: UIImage?) {
        guard
            let firstResult = decoderResults.first,
            let extra = firstResult.extra as? [String: Any],
            let jsonString = extra[“JSON”] as? String,
            !jsonString.isEmpty,
            let data = jsonString.data(using: .utf8),
            let json = try? JSONSerialization.jsonObject(with: data) as? [String: Any],
            let normal = json[“normal”] as? [String: Any]
        else { return }

   // MRZ-related fields in `extra`:
   // document_type
   // first_name
   // last_name
   // nationality
   // gender
   // personal_number
   // date_of_birth
   // document_number
   // issuing_country
   // date_of_expiry
   // date_of_issuance_estimated
   // optional_data
}

            


3.3 Side-by-side property differences #

Old New
document_type document_type
document_number document_number
given_names first_name
surnames last_name
nationality nationality
issuing_country issuing_country
sex gender
date_of_birth_raw date_of_birth
expiration_date_raw date_of_expiry
estimated_issuing_date_raw date_of_issuance_estimated
personal_number_raw personal_number
optional_value optional_data

3.4 Example: update your old usage to barKoder #

Perhaps you had something like:

                // OLD – in successfulScan(withResult: result)
nationalityLabel.text = result.nationality
documentTypeLabel.text = result.document_type_raw
            

New, using extra directly:

                func scanningFinished(_ decoderResults: [DecoderResult],
thumbnails: [UIImage]?,
image: UIImage?) {

guard
let firstResult = decoderResults.first,
let extra = firstResult.extra as? [String: Any],
let jsonString = extra[“JSON”] as? String,
!jsonString.isEmpty,
let data = jsonString.data(using: .utf8),
let json = try? JSONSerialization.jsonObject(with: data) as? [String: Any],
let normal = json[“normal”] as? [String: Any]
else { return }


// use `normal` here
let nationality = normal[“nationality”] as! String
nationalityLabel.text = nationality
let documentType = normal[“document_type”] as! String
documentTypeLabel.text = documentType
}
            

4. Behavioral features: continuous scanning, duplicates, vibration #

4.1 Vibration #

                // MRZScanner
MRZScannerController.enableVibration(onSuccess: true)

// barKoder
guard let config = barkoderView.config else { return }
config.vibrateOnSuccessEnabled = true

            

4.2 Beep #

                // barKoder
guard let config = barkoderView.config else { return }
config.beepOnSuccessEnabled = true
            

4.3 "Scan once then stop" vs "continuous scanning" #

With MRZScanner you had:

                mrzScannerController!.setContinuousScanningEnabled(false)
            

With barKoder, the equivalent behaviour is to close the session after the first result:

                // SINGLE-SCAN MODE
config.closeSessionOnResultEnabled = true
            

For continuous scanning, keep the session open and tweak the duplicate threshold:

                config.closeSessionOnResultEnabled = false // keep session open
config.thresholdBetweenDuplicatesScans = 5
            

closeSessionOnResultEnabled controls whether scanning stops after the first result, and thresholdBetweenDuplicatesScans controls how often the same code is scanned.

If you previously used setIgnoreDuplicates(true) in MRZScanner, to handle this in barKoder set:

                config.thresholdBetweenDuplicatesScans = -1   // scan it only once, no duplicates
            

5. UI: Full-Screen vs Partial Scanner #

MRZScanner used initUI(..., partialViewRect...) vs full-screen mode.

With barKoder:

There is no special scanner VC. BarkoderView is just a UIView, and its size/position is entirely done via Auto Layout.

                // FULLSCREEN barKoder
NSLayoutConstraint.activate([
barkoderView.topAnchor.constraint(equalTo: view.topAnchor),
barkoderView.bottomAnchor.constraint(equalTo: view.bottomAnchor),
barkoderView.leadingAnchor.constraint(equalTo: view.leadingAnchor),
barkoderView.trailingAnchor.constraint(equalTo: view.trailingAnchor)
])


//Partial (300-pt high area):
NSLayoutConstraint.activate([
barkoderView.topAnchor.constraint(equalTo: view.safeAreaLayoutGuide.topAnchor),
])
   
barkoderView.leadingAnchor.constraint(equalTo: view.leadingAnchor),   
barkoderView.trailingAnchor.constraint(equalTo: view.trailingAnchor),
barkoderView.heightAnchor.constraint(equalToConstant: 300),
            

Quick “Old → New” Cheat Sheet Delegates & Callbacks #

                // OLD
class VC: UIViewController, MRZScannerDelegate {
func successfulScan(withResult result: MRZResultDataModel!) { ... }
}
// NEW
class VC: UIViewController, BarkoderResultDelegate {
func scanningFinished(_ decoderResults: [DecoderResult],
thumbnails: [UIImage]?,
image: UIImage?) { ... }
}
            

MRZ Mode #

                // OLD
mrzScannerController!.setScannerType(TYPE_MRZ)
MRZScannerController.setIDActive(true)
MRZScannerController.setPassportActive(true)
MRZScannerController.setVisaActive(true)
// NEW
barkoderView.config?.decoderConfig?.idDocument.enabled = true
            

Document Image Mode #

                // OLD
mrzScannerController!.setScannerType(TYPE_DOCUMENT_IMAGE_ID)
// NEW
barkoderView.config?.decoderConfig?.idDocument.enabled =
true
barkoderView.config?.decoderConfig?.setcustomOption("deco
de_document_image_only", value: 1) // (set to 0 to disable document image scan, and continue to use MRZ scanning)
            

And you will receive the document image, in

scanningFinished (thumbnail)

Image scan (barKoder) #

You can add a custom button that presents a photo picker; once the user selects an image, pass it to barkoderView like this, and you will receive the results in scanningFinished:

                guard let bkdConfig = barkoderView.config else { return }
bkdConfig.decoderConfig?.decodingSpeed = DecodingSpeed(3); // rigorous
BarkoderHelper.scanImage(image, bkdConfig: bkdConfig, resultDelegate: self)
            

Page Contents