Skip to main content

Overview

This guide shows you how to integrate the MetaMap SDK into your SwiftUI-based iOS application using the MetaMapDelegateObserver wrapper pattern.

Prerequisites

  • MetaMap SDK installed via CocoaPods
  • iOS 13.0 or later
  • Swift 5.7 or later
  • SwiftUI framework

Implementation

Complete SwiftUI Example

Here’s a complete implementation using the MetaMapDelegateObserver pattern:
import SwiftUI
import MetaMapSDK
import UIKit

struct ContentView: View {
    var body: some View {
        VStack {
            ZStack {
//MARK: MetaMapDelegateObserver
                MetaMapDelegateObserver { identityId, verificationId in
                    print("\(identityId), \(verificationId)")
                } cancelled: {
                    print("cancelled")
                }
                HStack {
                    Button(action: {
                        MetaMap.shared.showMetaMapFlow(clientId: "YOUR_CLIENT_ID", flowId: "YOUR_FLOW_ID", metadata: ["key1": "value1", "key2": 123])
                    }) {
                        Text("press me")
                    }
                }
            }
        }
    }
}

struct MetaMapDelegateObserver: UIViewControllerRepresentable {
    let vc = MatiViewController()
    public func makeUIViewController(context: Context) -> MatiViewController {
        return vc
    }
    
    public func updateUIViewController(_ uiViewController: MatiViewController, context: Context) {}
    
    var success: (_ identityId: String?, _ verificationId: String?) -> Void
    var cancelled: () -> Void
    
    public func makeCoordinator() -> Coordinator {
        Coordinator(success: success, cancelled: cancelled)
    }
    
    public  class Coordinator: NSObject, MetaMapButtonResultDelegate {
        public  func verificationSuccess(identityId: String?, verificationID: String?) {
            success(identityId, verificationID)
        }
        
        public  func verificationCancelled() {
            cancelled()
        }
        
        var success: (_ identityId: String?, _ verificationId: String?) -> Void
        var cancelled: () -> Void
        
        init(success: @escaping (_ identityId: String?, _ verificationId: String?) -> Void, cancelled: @escaping () -> Void) {
            self.success = success
            self.cancelled = cancelled
            super.init()
            MetaMapButtonResult.shared.delegate = self
        }
    }
}

class MetaMapViewController: UIViewController {}

Understanding MetaMapDelegateObserver

The MetaMapDelegateObserver is a custom UIViewControllerRepresentable wrapper that bridges UIKit’s delegate pattern with SwiftUI’s declarative approach.

Key Components

1. UIViewControllerRepresentable Implementation

The observer conforms to UIViewControllerRepresentable to integrate with SwiftUI:
struct MetaMapDelegateObserver: UIViewControllerRepresentable {
    let vc = MatiViewController()
    
    public func makeUIViewController(context: Context) -> MatiViewController {
        return vc
    }
    
    public func updateUIViewController(_ uiViewController: MatiViewController, context: Context) {}
}

2. Closure-Based Callbacks

Instead of implementing delegate methods, use closures for handling results:
var success: (_ identityId: String?, _ verificationId: String?) -> Void
var cancelled: () -> Void

3. Coordinator Pattern

The Coordinator bridges delegate callbacks to SwiftUI closures:
public class Coordinator: NSObject, MetaMapButtonResultDelegate {
    public func verificationSuccess(identityId: String?, verificationID: String?) {
        success(identityId, verificationID)
    }
    
    public func verificationCancelled() {
        cancelled()
    }
    
    var success: (_ identityId: String?, _ verificationId: String?) -> Void
    var cancelled: () -> Void
    
    init(success: @escaping (_ identityId: String?, _ verificationId: String?) -> Void, 
         cancelled: @escaping () -> Void) {
        self.success = success
        self.cancelled = cancelled
        super.init()
        MetaMapButtonResult.shared.delegate = self
    }
}

Using MetaMapDelegateObserver

Basic Usage

Add the MetaMapDelegateObserver to your view hierarchy using a ZStack:
struct ContentView: View {
    var body: some View {
        VStack {
            ZStack {
                MetaMapDelegateObserver { identityId, verificationId in
                    // Handle success
                    print("Success: \(identityId ?? "N/A")")
                } cancelled: {
                    // Handle cancellation
                    print("Cancelled")
                }
                
                Button("Start Verification") {
                    MetaMap.shared.showMetaMapFlow(
                        clientId: "YOUR_CLIENT_ID",
                        flowId: "YOUR_FLOW_ID",
                        metadata: ["key1": "value1"]
                    )
                }
            }
        }
    }
}

Handling Results

The observer provides two closure parameters:

Success Closure

MetaMapDelegateObserver { identityId, verificationId in
    // Called when verification succeeds
    if let identityId = identityId {
        print("Identity ID: \(identityId)")
    }
    if let verificationId = verificationId {
        print("Verification ID: \(verificationId)")
    }
} cancelled: {
    // ...
}

Cancelled Closure

MetaMapDelegateObserver { identityId, verificationId in
    // ...
} cancelled: {
    // Called when user cancels verification
    print("User cancelled the verification")
}

Launching Verification Flow

Call showMetaMapFlow from any button or action in your SwiftUI view:
Button(action: {
    MetaMap.shared.showMetaMapFlow(
        clientId: "YOUR_CLIENT_ID",
        flowId: "YOUR_FLOW_ID",
        metadata: ["key1": "value1", "key2": 123]
    )
}) {
    Text("Start Verification")
        .padding()
        .background(Color.blue)
        .foregroundColor(.white)
        .cornerRadius(8)
}

Parameters

showMetaMapFlow Parameters

ParameterTypeRequiredDescription
clientIdStringYesYour MetaMap client ID
flowIdStringYesThe verification flow ID
metadataDictionaryNoAdditional metadata and customization options

Best Practices

  1. Single Observer Instance: Place the MetaMapDelegateObserver once in your view hierarchy, typically at the top level
  2. ZStack Layout: Use a ZStack to ensure the observer doesn’t affect your UI layout
  3. State Management: Store verification results in @State or @StateObject for reactive UI updates
  4. Error Handling: Always implement both success and cancelled closures

Example with State Management

struct ContentView: View {
    @State private var identityId: String?
    @State private var showingResult = false
    
    var body: some View {
        VStack {
            ZStack {
                MetaMapDelegateObserver { identityId, verificationId in
                    self.identityId = identityId
                    self.showingResult = true
                } cancelled: {
                    print("Verification cancelled")
                }
                
                Button("Start Verification") {
                    MetaMap.shared.showMetaMapFlow(
                        clientId: "YOUR_CLIENT_ID",
                        flowId: "YOUR_FLOW_ID",
                        metadata: [:]
                    )
                }
            }
        }
        .alert("Verification Complete", isPresented: $showingResult) {
            Button("OK") { }
        } message: {
            Text("Identity ID: \(identityId ?? "Unknown")")
        }
    }
}

Next Steps

Build docs developers (and LLMs) love