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 #
- Remove MRZScanner SDK frameworks and imports.
- 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.
- Replace MRZScannerController with a BarkoderView property in your view controller.
- Configure BarkoderView with:
• BarkoderConfig(licenseKey:)
• decoderConfig.idDocument.enabled = true for MRZ/ID documents.
• Optional: config.vibrateOnSuccessEnabled, config.imageResultEnabled, etc.
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!”.
Enables all ID types (ID, Passport, Visa)
Validates country codes
Supports upside-down (rotated) scanning by default.
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)