GuidesiOS mDocs Holder SDKProximity presentation

How to build mDocs proximity presentation capabilities into my iOS application

This guide offers step-by-step instructions for using the iOS mDocs holder SDK to build the capability to present an mDoc for verification via a proximity presentation workflow as per ISO/IEC 18013-5.

If you are unfamiliar with this presentation workflow, the corresponding tutorial might be a better starting point as it offers more context and a high level overview of the user experience.

Prerequisites

  • Access to the SDK’s GitHub distribution repository. Refer to the getting started guide for more information.

Access requires an SSH key associated with the GitHub user who has access to the distribution repo. Please contact us if you are having trouble with setting this up.

Overview

This guide comprises the following steps:

  1. Setup your project.
  2. Create a QR code.
  3. Receive and handle a presentation request.
  4. Send matching mDocs to the verifier.

Setup your project

  1. Install the SDK.
  2. Add Bluetooth and Biometric permissions to your application.

Initialize the SDK

  1. Create a new variable:
Creating a new variable
	var mobileCredentialHolder: MobileCredentialHolder
  1. Initialize the SDK by creating a new instance of the MobileCredentialHolder class and calling its initialize function:
Initializing the SDK
    mobileCredentialHolder = MobileCredentialHolder.shared
	try mobileCredentialHolder.initialize()

Create a QR code

  1. Create a new UI element to enable the user to request creating a QR code.
  2. Call the ProximityPresentationSession function when the user requests creating a QR code.
Calling the ProximityPresentationSession function
    func createQRCode() {
        Task { @MainActor in
            do {
                presentationSession = try await mobileCredentialHolder.createProximityPresentationSession(
                    onRequestReceived: onRequestReceived(_:error:)
                )
                qrCode = presentationSession?.deviceEngagement
 
            } catch {
                print(error)
            }
        }
    }

The function returns a ProximityPresentationSession which includes a deviceEngagement string:

"mdoc:owBjMS4wAYIB2BhYS6QBAiABIVgghaBYJe7KSqcEolhmnIJaYJ2AIevkKbEy5xP7tkwlqAwiWCAMGCGe6uFI2hKeghb59h_K4hPV-Ldq6vnaxsRiySMH9gKBgwIBowD0AfULUKRoj0ZH60Qco-m0k97qRSQ"
  1. Convert the deviceEngagement string into a QR code and create a new view to display it:
Converting the deviceEngagement string into a QR code
var 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()
}

Once the SDK receives a presentation request from the verifier, it looks for any matching mDocs stored in the wallet.

Respond to a presentation request

  1. Handle the onRequestReceived event of the createProximityPresentationSession function by presenting matching credentials to the user, and add a UI element for providing consent to sharing them with the verifier:
Handling a presentation request
@State var selectedCredential: String?
 
...
 
@Published var requestedDocuments: [(request: MobileCredentialRequest,
                                     matchedMobileCredentials: [MobileCredentialMetadata])]? = nil
 
...
 
if 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 {
                viewModel.sendResponse(selectedCredential)
        } label: {
            Text("Send Response")
        }
    }
}
 
...
 
func sendResponse(_ id: String) {
    Task { @MainActor in
        do {
          try await presentationSession?.sendResponse(credentialIds: [id], terminateSession: true)
        } catch {
            print(error)
        }
    }
}