Online and offline presentments (ISO 18013)
This page explains how to use the public MobileIDInPersonSharing API for ISO 18013 presentments. It covers both offline QR-based sessions and online sessions started from encoded engagement data, plus the request, consent, transfer, and error-handling flow shared by both modes.
What This Covers
- creating an ISO 18013 handler with
MobileID.iso() - starting an offline session or an online session
- retaining the session and receiving delegate callbacks
- reading the verifier request and selected data items
- accepting or declining a request
- tracking transfer progress
- handling ISO 18013 errors
Public API Surface
Session Setup
MobileID.iso() -> ISO18013ISO18013.createSession() -> SessionISO18013.createSession(encodedData:shouldProceedOnSSLError:) -> SessionSession.start(delegate:)Session.end()SessionDelegate
Request And Transfer
RequestRequest.itemsRequest.verifierInfoRequest.respond(decision:) -> TransferUserSharingDecision.accept(_:)UserSharingDecision.declineTransferTransfer.start(delegate:) async throwsTransfer.progressTransferDelegateTransferProgress
Data And Errors
DataItemISO18013.ErrorISO18013.Error.userVisibleCode
End-To-End Flow
1. Create The ISO 18013 Handler
Start from the MobileID instance you already created during app bootstrap:
Swift1import MobileIDInPersonSharing23let iso18013 = mobileID.iso()
Use the same handler type for both offline and online sessions.
2. Start An Offline Session
Offline sessions are the QR-based presentment path. Create the session, keep it alive for as long as the presentment is active, and start it with a SessionDelegate: typically begins from a QR code and uses Bluetooth for the reader connection. Start them by creating the session with iso18013.createSession() and keeping the returned session alive for the duration of the interaction.
Swift1import MobileIDInPersonSharing2import UIKit34final class OfflinePresentmentCoordinator: NSObject, SessionDelegate {5 private let navigationController: UINavigationController6 private let iso18013: ISO180137 private var session: Session?89 init(iso18013: ISO18013, navigationController: UINavigationController) {10 self.iso18013 = iso1801311 self.navigationController = navigationController12 }1314 func start() {15 let session = iso18013.createSession()16 self.session = session17 session.start(delegate: self)18 }1920 func session(_ session: Session, generated qrCode: QrCode) {21 // Show the QR code to the verifier.22 }2324 func connectionHasBeenEstablished(for session: Session) {25 // Update the UI when the reader connects.26 }2728 func session(_ session: Session, hasReceived request: Request) {29 presentDecisionUI(for: request)30 }3132 func sessionHasFinishedSuccessfully(_ session: Session) {33 self.session = nil34 }3536 func session(_ session: Session, hasEncountered error: ISO18013.Error) {37 self.session = nil38 print("ISO 18013 error: \(error.userVisibleCode)")39 }4041 func notification(_ request: AuthenticationRequest, promptedForAuthentication authenticationPrompt: PinViewController) {}42 func notification(_ request: AuthenticationRequest, promptedForAuthentication authenticationPrompt: FaceViewController) {}43 func didFinishAuthentication(of request: AuthenticationRequest) {}44 func notificationDidBecomeBusy(_ request: AuthenticationRequest, expectedInterval: NavigatorBusyStateInterval) {}45 func notificationDidBecomeIdle(_ request: AuthenticationRequest) {}4647 private func presentDecisionUI(for request: Request) {48 let items = request.items49 let verifierName = request.verifierInfo?.organizationName50 print("Requested items: \(items), verifier: \(String(describing: verifierName))")51 }52}
The session must be retained for the entire presentment. Call end() when you want to stop the flow explicitly.
3. Start An Online Session
Online sessions use encoded engagement data provided by the verifier or deep-link flow:
Swift1import MobileIDInPersonSharing23let onlineSession = iso18013.createSession(4 encodedData: engagementPayload,5 shouldProceedOnSSLError: false6)7onlineSession.start(delegate: sessionDelegate)
Use the online variant when the engagement is already encoded and you want the same request/transfer flow without the QR-based engagement step.
4. Accept Or Decline A Request
When the delegate receives a Request, inspect the requested items and decide whether to share a credential:
Swift1func handle(request: Request, credential: EnrolledCredential) async {2 let transfer = request.respond(decision: .accept(credential))3 do {4 try await transfer.start(delegate: self)5 } catch {6 print("Transfer failed: \(error)")7 }8}910func decline(request: Request) {11 let transfer = request.respond(decision: .decline)12 Task {13 try? await transfer.start(delegate: self)14 }15}
Request.items tells you which data elements the verifier asked for. Request.verifierInfo is optional and can be used to display the organization name if the system provides it.
Only share a credential the user has explicitly chosen. If the user declines, the transfer still runs so the request can be completed cleanly with an empty response.
5. Track Transfer Progress
TransferDelegate lets you observe write progress while the response is being sent:
Swift1extension OfflinePresentmentCoordinator: TransferDelegate {2 func transfer(_ transfer: Transfer, made progress: TransferProgress) {3 print("Sent \(progress.itemsSent) of \(progress.itemsTotal)")4 }5}
TransferProgress exposes:
itemsSentitemsTotalfractionCompletedisCompleted
Treat the progress callbacks as UI hints. They are useful for loading indicators and accessibility announcements, but they should not drive business logic. Online sessions usually complete in a single response handoff, so progress is often less granular than an offline transfer.
6. Handle Presentment Errors
ISO18013.Error covers QR parsing, connection, MSO validity, authentication, and response failures.
Common examples include:
invalidQRCodePayloadinvalidMdocPayloadexpiredMSOconnectionFailedauthenticationKeyExpiredinvalidAuthenticationcredentialLockedinvalidResponse
Use userVisibleCode when you need a stable support code for logs or customer care.
Implementation Notes
- Keep the
Sessionalive until the request/transfer flow completes. - Use the offline session when the verifier starts from a QR code.
- Use the online session when the verifier provides encoded engagement data.
- Present only the credential the user selected.
- Read
Request.itemsbefore building your consent UI so the user can see what will be shared. - Call
Session.end()when the flow is no longer needed.
Related Pages
Getting StartedCredential Wallet And LifecycleDigital Credential API (ISO/IEC 18013-7 Annex C)PIN And Liveness Verification