Quick Setup
After completing the installation steps you are ready to read NFC documents. The overall process is: construct a document key from the MRZ data printed on the document, then use one of two approaches to initiate the scan and receive a result.
Document key
OzoneNFCDocumentKey is the access credential that unlocks the NFC chip. It is derived from the Machine Readable Zone (MRZ) printed on the document.
let key = OzoneNFCDocumentKey(
passportNumber: "AB1234567", // 9-character document number from MRZ
dateOfBirth: "900115", // yyMMdd — e.g. 15 Jan 1990
expiryDate: "260731" // yyMMdd — e.g. 31 Jul 2026
)
Date fields use the yyMMdd format (2-digit year, 2-digit month, 2-digit day), matching the MRZ encoding defined in ICAO Doc 9303.
Approach 1 — Async/Await with OzoneNFCReader
OzoneNFCReader provides a headless async API. The system NFC scanning sheet is presented automatically; your code resumes when reading completes or throws.
import OzoneNFC
let reader = OzoneNFCReader()
do {
let document = try await reader.readDocument(key)
// handle document
} catch let error as OzoneNFCError {
// handle known error
} catch {
// handle unexpected error
}
This is the simplest integration — the system NFC scanning sheet is managed automatically by the OS, so you only need to handle the result.
Custom display messages
Pass a displayMessage closure to override the text shown during reading:
let document = try await reader.readDocument(key) { message in
switch message {
case .requestPresentPassport:
return "Hold your phone against the passport cover."
case .authenticatingWithPassport(let progress):
return "Authenticating… \(progress)%"
case .readingDataGroupProgress(let group, let progress):
return "Reading \(group)… \(progress)%"
case .successfulRead:
return "Done!"
default:
return nil // use default text
}
}
Approach 2 — Built-in UI with OzoneNFCViewController
OzoneNFCViewController is a UIViewController that bundles a full guided scanning UI. Use it when you want the built-in visual flow.
UIKit
import OzoneNFC
let vc = OzoneNFCViewController(documentKey: key) { result in
switch result {
case .success(let document):
// handle document
case .failure(let error):
// handle error
}
}
present(vc, animated: true)
Custom display messages
OzoneNFCViewController accepts the same optional displayMessage closure as OzoneNFCReader. Pass it to the initializer to override the text shown during scanning:
let vc = OzoneNFCViewController(documentKey: key, displayMessage: { message in
switch message {
case .requestPresentPassport:
return "Hold your phone against the passport cover."
case .authenticatingWithPassport(let progress):
return "Authenticating… \(progress)%"
case .readingDataGroupProgress(let group, let progress):
return "Reading \(group)… \(progress)%"
case .successfulRead:
return "Done!"
default:
return nil // use default text
}
}) { result in
// handle result
}
present(vc, animated: true)
SwiftUI
Wrap OzoneNFCViewController in a UIViewControllerRepresentable:
import OzoneNFC
import SwiftUI
struct NFCScanView: UIViewControllerRepresentable {
let documentKey: OzoneNFCDocumentKey
let completion: (Result<OzoneNFCDocument, OzoneNFCError>) -> Void
func makeUIViewController(context: Context) -> OzoneNFCViewController {
OzoneNFCViewController(documentKey: documentKey, completion: completion)
}
func updateUIViewController(_ uiViewController: OzoneNFCViewController, context: Context) {}
}
Present it using a NavigationLink or .fullScreenCover.
Reading the result
OzoneNFCDocument is the object returned on a successful read.
Always populated:
| Property | Type | Description |
|---|---|---|
firstName | String | Given name(s) |
lastName | String | Surname |
documentNumber | String | Document number |
documentType | String | Raw document type code (e.g. "P") |
documentSubType | String | Document sub-type as encoded in the MRZ |
documentCode | String | Full document code string from the MRZ |
translatedDocumentType | TranslatedDocumentType | Enum — see below |
dateOfBirth | String | yyMMdd |
expiryDate | String | yyMMdd |
gender | String | Sex as encoded in the MRZ — typically "M", "F", or "<" (unspecified) |
nationality | String | ISO 3166-1 alpha-3 country code |
issuingAuthority | String | Name or code of the authority that issued the document |
passportDataValid | Bool | true if all data group hashes match the SOD |
Optional:
| Property | Type | Description |
|---|---|---|
isExpired | Bool? | Calculated from expiryDate when the result is created; true if expired, false if not, nil if the date cannot be parsed |
age | Int? | Calculated from dateOfBirth; nil if the date cannot be parsed |
personalNumber | String? | Personal number from the MRZ (field 14), if present |
placeOfBirth | String? | Place of birth if present on the chip |
image | UIImage? | Facial image from DG2 |
signatureImage | UIImage? | Signature image from DG7, if present |
Security status fields
Each authentication step exposes an AuthStatus: .success, .failure, or .skipped (not attempted).
| Property | Description |
|---|---|
BACStatus | Basic Access Control |
PACEStatus | Password Authenticated Connection Establishment |
chipAuthenticationStatus | Chip Authentication (anti-cloning) |
activeAuthenticationStatus | Active Authentication (anti-substitution) |
TranslatedDocumentType
public enum TranslatedDocumentType: String {
case `default`
case nationalPassport
case emergencyPassport
case diplomaticPassport
case officialOrServicePassport
case refugeePassport
case alienPassport
case statelessPassport
case travelDocument
case militaryPassport
}
Chip position helper
OzoneNFCReader exposes a static helper to determine where the NFC chip is located on a document, which can be useful for displaying guidance to the user:
let position = OzoneNFCReader.getChipPositionFor(countryCode: "GBR")
Returns a value describing the expected chip location for the given ISO 3166-1 alpha-3 country code, or nil if the location is unknown.
Error handling
OzoneNFCError covers the expected failure cases:
| Case | Description |
|---|---|
.invalidNfcTag | The tag found is not a valid eMRTD chip |
.invalidNfcKeyFormatting | The document key (MRZ data) is malformed |
.userCancelled | The user dismissed the NFC sheet |
.unexpectedError | An unrecoverable internal error occurred |