Skip to content

iOS SDK Integration ​

Integration guide for the CICReader iOS SDK, version 1.0.7. Written in Swift; the entry point is CESNfc.

Build Specifications ​

WhatDescription
Xcode16.0+
Target iOS version13+

Integration ​

Add the SDK framework to your target, then these pods:

ruby
pod 'OpenSSL-Universal', '~> 1.1.180'
pod 'PromisesSwift', '~> 2.4.0'

Enable the Near Field Communication Tag Reading capability, then declare the usage description and the ISO7816 application identifiers:

xml
<key>NFCReaderUsageDescription</key>
<string>We would like to use NFC to scan NID chip</string>

<key>com.apple.developer.nfc.readersession.iso7816.select-identifiers</key>
<array>
    <string>A0000002471001</string>
    <string>A0000002472001</string>
    <string>00000000000000</string>
</array>

WARNING

Missing any of the three ISO7816 identifiers makes the chip read fail silently in production builds while working in debug. See Verifying the IPA Configuration below.

Initialize ​

swift
let nfcConfig = CESNfcConfig(baseUrl: "<BASE_URL>",
                             enableCheckNfcData: true,
                             readTimeout: 60000,
                             writeTimeout: 60000)

let nfcReader = CESNfc(nfcConfig)
nfcReader.delegate = self               // optional
nfcReader.displayMessageDelegate = self // optional

Parameters:

ParamTypeRequiredDefaultDescription
baseUrlstringYes—Base URL for API calls.
enableCheckNfcDataboolNotrueVerify NFC data against Cake RAR.
readTimeoutintNo60000API read timeout in milliseconds.
writeTimeoutintNo60000API write timeout in milliseconds.
cacheScanStrategyenumNo.NO_CACHESee Mobile SDK Overview § Cache on Server Errors.
sessionDelayConfigCESSessionDelayConfigNo—Per-device retry delays. See below.

Custom headers ​

swift
extension DemoViewController: CESNfcDelegate {
    func provideHeaders() -> [String: String?] {
        return [
            "custom_header1": "value1",
            "custom_header2": "value2"
        ]
    }
}

Custom display messages ​

Every method is optional — override only the prompts you want to change.

swift
extension ViewController: CESNfcDisplayMessageDelegate {
    func presentRequestPresentPassport() -> String { }
    func presentAuthenticatingWithPassport(progress: Int) -> String { }
    func presentReadingDataGroupProgress(dataGroup: DataGroupId, progress: Int) -> String { }
    func presentError(error: CESNfcError) -> String { }
    func presentSuccessfulRead() -> String { }
    func presentStartVerifyData() -> String { }
    func presentVerifyDataDone() -> String { }
}

Read the Chip ​

Using MRZ — 9-digit ID, date of birth, expiry date ​

Throws InvalidFullMRZKey when any input is wrong.

swift
nfcReader.readNfc(
        idCard: "xxxxxxxxx",
        dayOfBirth: "901212",
        expireDate: "301212",
        accessToken: "<ACCESS_TOKEN>",
        refId: "ref_00000001")
    .then { result in
        // result
    }.catch { err in
        // handle error
    }
ParamTypeRequiredDefaultDescription
idCardstringYes—The last 9 digits of the NID number.
dayOfBirthstringYes—Date of birth, format yyMMdd.
expireDatestringYes—Card expiry date, format yyMMdd.
accessTokenstringYes—Request access token.
refIdstringYes—Transaction reference ID to verify.

Using CAN — 12-digit ID number ​

Throws InvalidCANKey when the number is wrong.

swift
nfcReader.readNfc(
        docId: "001xxxxxxxxx",
        accessToken: "<ACCESS_TOKEN>",
        refId: "ref_00000001")
    .then { result in
        // result
    }.catch { err in
        // handle error
    }
ParamTypeRequiredDefaultDescription
docIdstringYes—The full 12-digit NID number.
accessTokenstringYes—Request access token.
refIdstringYes—Transaction reference ID to verify.

Result ​

CESNfcResult carries the raw data groups plus, when enableCheckNfcData is true, the parsed citizen fields.

Chip data ​

FieldTypeDescription
comstringUnicode version info and the list of available data groups. Base64.
sodstringThe SOD data from the NFC scan. Base64.
dg1stringIndividual basic details — MRZ information. Base64.
dg2stringCitizen facial image encoding. Base64.
dg13stringCitizen-related information. Base64.
dg14stringUsed for the EAC access mechanism. Base64.
dg15stringPublic key, used for Passive Authentication. Base64.
cloneStatusCESNfcVerdictClone status of the card. See Mobile SDK Overview § Clone Status.
imageDataString?Computed facial image contained in the dg2 data.

Citizen fields — API derived ​

FieldTypeDescription
idCardNostringIdentification number of the citizen.
oldIdCardNumberstringOld identification number.
fullNamestringFull name.
dateOfBirthstringDate of birth.
dateOfExpirystringCard expiry date.
genderstringGender.
nationalitystringNationality.
ethnicstringEthnicity.
religionstringReligion.
placeOfOriginstringPlace of origin.
residenceAddressstringPlace of residence.
personalSpecificIdentificationstringPersonal identification characteristics.
dateOfIssuancestringDate the card was issued.
fatherNamestringFather's name.
motherNamestringMother's name.
spouseNamestringPartner's name.
faceImagestringFacial image. Base64.
isVerifiedboolWhether the card was verified by RAR.
verificationKeystringVerification key — pass this to Verify NFC.
requestIdstringRequest ID of the authentication.

iOS uses idCardNo and residenceAddress where Flutter uses citizenIdentify and placeOfResidence — same values, different names.

Event Listener ​

swift
// nfcReader.eventDelegate = self

public func nfcScanWillStart() -> Void { }
public func nfcScanDidEnd(isScanSuccess: Bool) -> Void { }
public func dataVerificationWillStart(isCacheHit: Bool) -> Void { }
public func dataVerificationDidEnd(isCacheHit: Bool) -> Void { }

When caching is enabled and the cache hits, nfcScanDidEnd never fires — render your own verification UI from dataVerificationWillStart:

swift
public func dataVerificationWillStart(isCacheHit: Bool) -> Void {
  if (isCacheHit) {
    // show your UI for data verification here
  } else {
    // SDK shows its dialog and asks the user to scan as normal
  }
}

public func dataVerificationDidEnd(isCacheHit: Bool) -> Void {
  if (isCacheHit) {
    // close your UI for data verification here
  } else {
    // SDK closes its own dialog
  }
}

Retry Delays ​

Each CESNfcError.SessionOnGoing comes with a recommended wait before the next scan attempt. The SDK handles this automatically; you can tune the initial duration per device model.

swift
CESNfcConfig(baseUrl: "<BASE_URL>",
            sessionDelayConfig: CESSessionDelayConfig(
            defaultDelayMs: 10000,
            mappingDurations: [
                DeviceModel.iPhone15ProMax.rawValue: 15000,
                DeviceModel.iPhone7Plus.rawValue: 12000
            ])
        )

Error Handling ​

Errors are thrown as CESNfcError. The full list is in Error Codes; the ones you will hit most:

FieldTypeDescription
NFCNotSupported600032NFC is not supported on this device.
SessionOnGoing600101Another reading session is in progress.
SystemResourceUnavailable600102The NFC system is busy.
UserCanceled600432The user cancelled.
InvalidFullMRZKey600316The MRZ key is invalid.
InvalidCANKey600317The CAN key is invalid.
InvalidCardType600396The scanned card is not an NID.
NetworkError601400A network error occurred.
ServerError601503A server error occurred.

Built-in Functions ​

swift
public var isNfcAvailable: Bool {
    NFCNDEFReaderSession.readingAvailable
}

iOS has no isNfcEnable or openNfcSetting — those are Android-only.

Verifying the IPA Configuration ​

If NFC works in debug but fails in a production build, check the built artefact.

Info.plist must contain:

  • NFCReaderUsageDescription with a usage description
  • com.apple.developer.nfc.readersession.iso7816.select-identifiers containing A0000002471001, A0000002472001 and 00000000000000

embedded.mobileprovision must contain com.apple.developer.nfc.readersession.formats with the TAG entitlement.

Next Steps ​

Developers