Learn how to build an iOS application that can present an mDoc via a proximity workflow
Introduction
In this tutorial we will use the iOS native mDoc Holder SDK to build an iOS wallet application that can present a claimed mDoc to a verifier that supports proximity verification as per ISO/IEC 18013-5.
- The user launches the wallet application and generates a QR code.
- The verifier scans the QR code, connects with the wallet and requests an mDoc for verification.
- The wallet displays matching credentials to the user and asks for consent to share them with the verifier.
- The verifier receives the wallet’s response and verifies the provided credential.
The result will look something like this:
Prerequisites
Before we get started, let’s make sure you have everything you need.
Prior knowledge
-
The verification workflow described in this tutorial is based on the ISO/IEC 18013-5 standard. If you are unfamiliar with this standard, refer to our Docs section for more information:
- What are mDocs?
- What is credential verification?
- Breakdown of the proximity presentation workflow.
-
We assume you have experience developing iOS native apps in Swift.
If you need to get a holding solution up and running quickly with minimal development resources and in-house domain expertise, talk to us about our white-label MATTR GO Hold app which might be a good fit for you.
Assets
- You must complete the Claim a credential tutorial, create the application and claim the mDoc provided in the tutorial.
- As part of your onboarding process you should have been provided with access to the following
assets:
- ZIP file which includes the required framework:
(
MobileCredentialHolderSDK-*version*.xcframework.zip
). - Sample Wallet app: You can use this app for reference as we work through this tutorial.
- ZIP file which includes the required framework:
(
Development environment
- Xcode setup with either:
- Local build settings if you are developing locally.
- iOS developer account if you intend to publish your app.
Testing devices
As this tutorial implements a proximity presentation workflow, you will need two separate physical iOS devices to test the end-to-end result:
- Wallet device:
- Supported iOS device to run the built application on, setup with:
- Biometric authentication.
- Bluetooth access.
- Available internet connection.
- Supported iOS device to run the built application on, setup with:
- Verifier device:
- Android/iOS device with an installed verifier application. We recommend downloading and using the MATTR GO Verify example app.
- Setup with Bluetooth access.
Got everything? Let’s get going!
Presenting a credential for proximity verification
In this tutorial we will build the components that enable a wallet user to present a stored mDoc to a verifier via a proximity presentation workflow.
To achieve this, you will build the following capabilities into your wallet application:
- Create a QR code for the verifier to scan and establish a secure connection.
- Receive and handle a presentation request from the verifier.
- Send a matching mDoc presentation to the verifier.
Create a new application view
To avoid confusion with the issuance workflow, we will create a new view for the application you build in the Claim a credential tutorial. You may choose to implement a different architecture in your production apps.
-
Create a swift file named
PresentCredentialsView.swift
and add the following code into the new file:Swiftimport SwiftUI import MobileCredentialHolderSDK struct PresentCredentialsView: View { // Step 3.1: Add selectedCredential var @ObservedObject var viewModel: PresentCredentialsViewModel init(mobileCredentialHolder: MobileCredentialHolder) { self.viewModel = PresentCredentialsViewModel(mobileCredentialHolder: mobileCredentialHolder) } var body: some View { VStack { // Step 2.3: Add button to generate QR code EmptyView() // Step 2.5: Display QR Code for scanning // Step 3.3: Display matched credentials for user consent Spacer() } } // Step 2.4: Create QR code view and generateQR Code function } class PresentCredentialsViewModel: ObservableObject { // Step 2.1: Create qrCode and presentationSession variables // Step 3.2: Add requestedDocuments var var mobileCredentialHolder: MobileCredentialHolder init(mobileCredentialHolder: MobileCredentialHolder) { self.mobileCredentialHolder = mobileCredentialHolder } // Step 2.2: Create proximity presentation session and generate QR code // Step 4.1: Send Response function }
This will serve as the basic structure for your application. We will copy and paste different code snippets into specific locations in this codebase to achieve the different functionalities. These locations are indicated by comments that reference both the section and the step.
- Add the following code in your
ContentView
file under the// Online Presentation Step 1.2: Add button to navigate to present credential view
comment to add a new Present Credentials button that will navigate the user to the newPresentCredentialsView
:
We recommend copying and pasting the comment text (e.g. // Online Presentation Step 1.2: Add button to navigate to present credential view
) to easily locate it in the code.
NavigationLink(destination: PresentCredentialsView) {
Text("Present Credentials")
}.padding()
-
Add the following code in your
ContentView
file under the// Online Presentation Step 1.3: Create PresentCredentialsView
comment to create the new view the button from the previous step will navigate to:Swiftvar presentCredentialsView: some View { PresentCredentialsView( mobileCredentialHolder: viewModel.mobileCredentialHolder ) }
-
Run the app and select the Present Credentials button. You should see a result similar to the following:
As the user selects the Present Credentials button, they are navigated to the new
PresentCredentialsView
(which is still empty at the moment, but we’ll take care of that in the
next step).
Create a QR code for the verifier to scan
Now that we have created the new view, the first capability we need to build in it is to establish a secure communication channel between the verifier and holder devices. As defined in ISO 18130-5, a proximity presentation workflow is always initiated by the holder (wallet user), who must create a QR code for the verifier to scan in order to initiate the device engagement phase.
To achieve this, your wallet application needs a UI element for the user to interact with and
trigger device engagement by calling the
createProximityPresentationSession
function.
💡 From this step onwards we will be working solely in the PresentCredentialsView.swift
file.
-
Add the following code under the
// Step 2.1: Create qrCode and presentationSession variables
comment to create new variables to hold the QR code and presentation session.Swift@Published var qrCode: String? @Published var presentationSession: ProximityPresentationSession?
-
Add the following code under the
// Step 2.2: Create proximity presentation session and generate QR code
comment to call theProximityPresentationSession
function when the user selects the Create QR Code button:Swiftfunc createQRCode() { Task { @MainActor in do { presentationSession = try await mobileCredentialHolder.createProximityPresentationSession( onRequestReceived: onRequestReceived(_:error:) ) qrCode = presentationSession?.deviceEngagement } catch { print(error) } } } private func onRequestReceived(_ mobileCredentialRequests: [(request: MobileCredentialRequest, matchedMobileCredentials: [MobileCredentialMetadata])]?, error: Error?) { // Step 3.4: display matched credentials }
-
Add the following code under the
// Step 2.3: Add button to generate QR code
comment to add a button that will generate the QR code when the user selects it:SwiftButton { viewModel.createQRCode() } label: { Text("Create QR code") }
Now, when the user selects the Create QR Code button, the application will call the
ProximityPresentationSession
function, which returns a
ProximityPresentationSession
instance that includes a deviceEngagement
string in base64
format:
"mdoc:owBjMS4wAYIB2BhYS6QBAiABIVgghaBYJe7KSqcEolhmnIJaYJ2AIevkKbEy5xP7tkwlqAwiWCAMGCGe6uFI2hKeghb59h_K4hPV-Ldq6vnaxsRiySMH9gKBgwIBowD0AfULUKRoj0ZH60Qco-m0k97qRSQ"
The deviceEngagement
string is always prefixed with mdoc:
and contains the information required
to establish a secure connection between the two devices, including:
- Wireless communication protocols supported by the holder.
- How the verifier can connect with the holder.
- Ephemeral public key which is unique to the current session.
- Additional feature negotiations.
Your app needs to convert this deviceEngagement
string into a QR code and display it in the wallet
UI for the verifier to scan.
-
Add the following code under the
// Step 2.4: Create QR code view and generateQR Code function
comment to create a new view to display the QR code in, as well as call the function that takes the data from thedeviceEngagement
string and converts it into a QR code:Swiftvar qrCodeView: some View { ZStack { if let imageData = generateQRCode(data: viewModel.qrCode?.data(using: .utf8) ?? Data()), let image = UIImage(data: imageData) { Image(uiImage: image) .resizable() .aspectRatio(contentMode: .fit) } } } func generateQRCode(data: Data) -> Data? { guard let filter = CIFilter(name: "CIQRCodeGenerator") else { return nil } filter.setValue(data, forKey: "inputMessage") guard let ciimage = filter.outputImage else { return nil } let transform = CGAffineTransform(scaleX: 10, y: 10) let scaledCIImage = ciimage.transformed(by: transform) let uiimage = UIImage(ciImage: scaledCIImage) return uiimage.pngData() }
-
Add the following code under the
// Step 2.5: Display QR Code for scanning
comment to display the QR code in the new QR code view once it is available:Swiftif viewModel.qrCode != nil { qrCodeView .frame(maxWidth: 300, maxHeight: 300) .padding(30) Spacer() }
-
Run the app, select the Present Credentials button and then select the Create QR code button. You should see a result similar to the following:
As the user selects the Present Credentials button, they are navigated to the new
PresentCredentialsView
where they see the Create QR code button. When they select the Create
QR code button, the wallet generates and displays a QR code that can be scanned by a verifier
device to establish a secure proximity communication channel over Bluetooth.
Once the QR code is displayed, the
ProximityPresentationSession
function enters a listening state, ready to establish a Bluetooth connection with a verifier app
that scans this QR Code.
Once the verifier scans the QR code, the devices will automatically exchange public keys to establish a secure communication channel, enabling the verifier to send a presentation request, which details what information is required and for what purpose.
Handle the presentation request
The
createProximityPresentationSession
function can handle three types of events:
onConnected
: When a secure connection is established.onSessionTerminated
: When a secure connection is terminated for whatever reason.onRequestReceived
: When a presentation request is received from the verifier.
onConnected
and onSessionTerminated
are optional events and will not be implemented in this
tutorial. Check out our SDK
Docs
for a complete description of these events and how you can handle them.
When the wallet SDK receives a presentation request from a verifier, an onRequestReceived
event is
triggered. The wallet then checks its credential storage for any credentials that match the
information defined in this request.
The wallet then needs to present these matching credentials to the user, and provide a UI element to provide consent to sharing this information with the verifier.
-
Add the following code under the
// Step 3.1: Add selectedCredential var
comment to create a new variable in thePresentCredentialsView
view that will hold credentials that match the presentation request:Swift@State var selectedCredential: String?
-
Add the following code under the
// Step 3.2: Add requestedDocuments var
comment create a new variable that will hold the credentials that were requested by the verifier:Swift@Published var requestedDocuments: [(request: MobileCredentialRequest, matchedMobileCredentials: [MobileCredentialMetadata])]? = nil
-
Add the following code under the
// Step 3.3: Display matched credentials for user consent
comment to create the logic to display matching credentials to the user and provide a UI element for them to consent sharing these credentials with the verifier:Swiftif viewModel.requestedDocuments != nil { let matchedCredentials = viewModel.requestedDocuments![0].matchedMobileCredentials List(selection: $selectedCredential) { Section(header: Text("Requested Document")) { Text("\(viewModel.requestedDocuments![0].request.docType)") } Section(header: Text("Please select matching document to present")) { ForEach(matchedCredentials, id: \.id) { credential in Text(credential.docType) } } } if let selectedCredential { Button { // Step 4.2: call sendResponse } label: { Text("Send Response") } } }
-
Add the following code under the
// Step 3.4: Display matched credentials
comment to create the logic that displays matching credentials to the user when a presentation request is received:SwiftDispatchQueue.main.async { self.requestedDocuments = mobileCredentialRequests }
-
Run the app, select the Present Credentials button and then select the Create QR code button. Next, use your testing verifier app to scan the presented QR code and send a presentation request. You should see a result similar to the following:
As the user selects the Present Credentials button, they are navigated to the new
PresentCredentialsView
where they see the Create QR code button. When they select the Create
QR code button, the wallet generates and displays a QR code.
When a compliant verifier app scans the QR code, a secure communication channel is established via Bluetooth. The verifier then sends a presentation request, which is displayed to the user on their digital wallet, alongside any credentials they have stored in their wallet that match the request.
Send a presentation response
The next (and last!) capability we need to build is for the wallet to sent a presentation response upon receiving consent from the holder to share information with the verifier.
Once the user provides this consent by selecting the Send Response button, the wallet
application should call the
sendResponse
function to share the selected credentials with the verifier as a presentation response.
-
Add the following code under the
// Step 4.1: Send response function
comment to define thesendResponse
function:Swiftfunc sendResponse(_ id: String) { Task { @MainActor in do { try await presentationSession?.sendResponse(credentialIds: [id], terminateSession: true) } catch { print(error) } } }
-
Add the following code under the
// Step 4.2: call sendResponse
comment to create the logic to call thesendResponse
function once the user selects the Send Response button:SwiftviewModel.sendResponse(selectedCredential)
The
sendResponse
function signs the presentation response with the user’s device private key (to prove Device authentication) and shares it as an encoded CBOR file. -
Run the app and perfrm the following:
- Select the Present Credentials button.
- Select the Create QR code button.
- Use your testing verifier app to scan the present QR code and send a presentation request.
- Back on the holder device, select the matching credential to share and select the Send Response button.
You should see a result similar to the following:
As the user selects the credential to share, the verifier app will receive the presentation response, verify any incoming credentials and display the verification results.
Congratulations, you have now completed this tutorial, and should have a working wallet application that can claim an mDoc using an OID4VCI workflow, and present it to a verifier for proximity verification via Bluetooth.
Summary
You have just used the iOS mDoc holder SDK to built a wallet application that can present a claimed mDoc to a verifier that supports proximity verification as per ISO/IEC 18013-5.
This was achieved by building the following capabilities into the application:
- Generating a QR code for the verifier to scan and establish a secure communication channel.
- Receive and handle a presentation request from the verifier.
- Display matching credentials to the user and ask for consent to share them with the verifier.
- Send matching credentials to the verifier as a presentation response.
What’s next?
- You can check out the iOS mDoc holder SDK Docs to learn more about available functions and classes.