Quick start: iOS
Add the Swift package, start it with your licence, run a verification from AVFoundation frames and send the result to your backend.
The iOS SDK is a Swift package, Digid, over the digid engine. It gives you the engine, an AVFoundation adapter and typed results; you build the screens. If you want ready-made screens in a native iOS app, the Flutter flow can be embedded with Flutter's add-to-app: see Quick start: Flutter.
1. Add the package
The package has two products:
Digid: the engine, the Swift API and the camera adapter, without models.DigidModels: the models as a resource bundle. Add it too, unless you deliver the models another way (for example, on-demand resources).
Customers get the package from the customer portal: see Get the SDK. It needs iOS 15 or later. The SDK has no network code; your app adds an NSCameraUsageDescription string to Info.plist.
2. Start the engine with your licence
Create one engine, off the main thread (loading the models takes a second or more):
import Digid
import DigidModels
let kyc = try NidaKyc(modelDirectory: DigidModels.directory)
let status = try kyc.applyLicence(licenceToken) // checked offline
if kyc.isEvaluation { print("digid evaluation mode:", status.reason ?? "") }
The licence is checked against your app's bundle identifier, read from the running app. A bad key never throws: the status says evaluation, and why. See Licences and renewal.
3. Run a verification
Open a session per attempt and feed it camera frames. NidaCaptureAdapter is a sample buffer delegate for AVCaptureVideoDataOutput:
let session = try kyc.newSession(.cardThenSelfie(requireBack: false))
let output = AVCaptureVideoDataOutput()
output.videoSettings = [kCVPixelBufferPixelFormatTypeKey as String: kCVPixelFormatType_32BGRA]
output.alwaysDiscardsLateVideoFrames = true
let adapter = NidaCaptureAdapter(session: session, rotationDeg: 90) { update in
DispatchQueue.main.async {
self.guidanceLabel.text = NidaKyc.text(for: update.guidance, lang: .en)
if update.step == .selfie { self.switchToFrontCamera() } // set adapter.mirrored = true
}
if let result = update.result { self.finish(result, json: try? session.finishJSON()) }
}
output.setSampleBufferDelegate(adapter, queue: DispatchQueue(label: "digid.frames"))
- The output must deliver BGRA frames.
rotationDeg: 90suits the back camera with a portrait interface. - Use
.cardOnly()to read the card without a selfie. - When the step becomes
.selfie, switch to the front camera and setmirroredtotrue. NidaKyc.text(for:lang:)gives the English or Swahili line for every guidance code.kyc.readCardImage(_:)reads a card from one photo instead of the camera.
More on sessions, steps and guidance: Custom screens on the engine.
4. Read the result
switch result.overall {
case .pass: showDone()
case .review: showPendingReview()
default: showRetry()
}
result.evaluation is true without a valid licence. result.checks lists every check, and result.face holds the face match verdict.
5. Send the result to your backend
session.finishJSON() returns the engine's result as JSON text, the same JSON every digid SDK produces. Send it as it is over your own authenticated connection:
var request = URLRequest(url: URL(string: "https://api.example.co.tz/kyc")!)
request.httpMethod = "POST"
request.setValue("application/json", forHTTPHeaderField: "Content-Type")
request.httpBody = Data(resultJSON.utf8)
let (_, response) = try await URLSession.shared.data(for: request)
The
digid.kyc/1document, with its tamper check, is built by the Flutter and Web SDKs. The native iOS SDK hands you the engine result JSON, which carries the same card fields, checks, verdict and face match. Your backend should still apply the checks in Verify on your backend that do not depend on the document's tamper check.
6. Report usage
kyc.usageSender = { report in
var r = URLRequest(url: URL(string: "https://digid.co.tz/api/v1/usage")!)
r.httpMethod = "POST"
r.setValue("application/json", forHTTPHeaderField: "Content-Type")
r.httpBody = try? JSONEncoder().encode(["report": report])
let response = (try? await URLSession.shared.data(for: r))?.1 as? HTTPURLResponse
return response?.statusCode == 200
}
try kyc.setUsageStore(myStore) // keeps the counts across launches
await kyc.online() // call when the app is online
See Licences and renewal for the store, renewal and the "no report, no renewal" rule.