Skip to content
Last updated

Enabling Elements for Apple iOS

Like other platforms, iOS requires the same two or three simple steps: Add a web view, handle events/messages, and, in some cases, authenticate the user.

1. Set Up a Web View

Component used: WKWebView

Note: iOS 26 introduced WebKit for SwiftUI which has not yet been tested with Elements. No problems are expected, but implementation details may change.

Required Web View Configuration
  • Enable JavaScript
  • Enable Web Storage (DOM Storage)

Web View Sample

elements-in-ios.swift
                // Step 1: Set Up a Web View
                WebView(
                    url: url,
                    onTokenReceived: { token in
                        self.token = token
                        self.showTokenAlert = true
                    },
                    onFullResponseReceived: { response in
                        handleCardTokenizeResponse(response)
                    }
                )
                .edgesIgnoringSafeArea(.all)
                

Step 2. Set Up Event/Message Handling

Sending Events

Component used: WKUserContentController

Note: iOS 26 introduced WebKit for SwiftUI which has not yet been tested with Elements. No problems are expected, but implementation details may change.

Please Log In to view this content.

Send Message Sample Code

elements-in-ios.swift
        // Step 2: Set Up Communications: Event/Message Handling
        // Step 2a: Send Event to Elements
        

Listening for Events

Component used: WKUserContentController

Please Log In to view this content.

Receive Message Sample Code

elements-in-ios.swift
        // Step 2b: Listen for events from Elements
        func userContentController(_ userContentController: WKUserContentController, didReceive message: WKScriptMessage) {
            print("Received postMessage: \(message.body)")
            var data: Data? = nil
            if let dict = message.body as? [String: Any] {
                data = try? JSONSerialization.data(withJSONObject: dict)
            } else if let str = message.body as? String {
                data = str.data(using: .utf8)
            }
            if let data, let msg = try? JSONDecoder().decode(DailypayCardTokenizeMessage.self, from: data),
               msg.type == "DAILYPAY_CARD_TOKENIZE_RESPONSE" {
                onIssuerReceived?(msg.response.issuer)
                onTokenReceived?(msg.response.token)
                onFullResponseReceived?(msg.response)
            }
        }
        

Step 3. Set Up User Authentication (Optional)

elements-in-ios.swift
    // Step 3: Setup Authentication
    //
    // Code sample coming soon
    //
    

Full Sample Code

elements-in-ios.swift
import SwiftUI
import WebKit

struct WebView: UIViewRepresentable {
    let url: URL
    let handlerName = "iosListener"
    var onTokenReceived: ((String) -> Void)?
    var onFullResponseReceived: ((DailypayCardTokenizeResponse) -> Void)?

    class Coordinator: NSObject, WKScriptMessageHandler {
        var onIssuerReceived: ((String) -> Void)?
        var onTokenReceived: ((String) -> Void)?
        var onFullResponseReceived: ((DailypayCardTokenizeResponse) -> Void)?
        init(onTokenReceived: ((String) -> Void)?, onFullResponseReceived: ((DailypayCardTokenizeResponse) -> Void)?) {
            self.onTokenReceived = onTokenReceived
            self.onFullResponseReceived = onFullResponseReceived
        }
        // Step 2: Set Up Communications: Event/Message Handling
        // Step 2a: Send Event to Elements
        // Step 2b: Listen for events from Elements
        func userContentController(_ userContentController: WKUserContentController, didReceive message: WKScriptMessage) {
            print("Received postMessage: \(message.body)")
            var data: Data? = nil
            if let dict = message.body as? [String: Any] {
                data = try? JSONSerialization.data(withJSONObject: dict)
            } else if let str = message.body as? String {
                data = str.data(using: .utf8)
            }
            if let data, let msg = try? JSONDecoder().decode(DailypayCardTokenizeMessage.self, from: data),
               msg.type == "DAILYPAY_CARD_TOKENIZE_RESPONSE" {
                onIssuerReceived?(msg.response.issuer)
                onTokenReceived?(msg.response.token)
                onFullResponseReceived?(msg.response)
            }
        }
        // Step 2 End
    }

    // Step 3: Setup Authentication
    //
    // Code sample coming soon
    //
    // Step 3 End


    func makeCoordinator() -> Coordinator {
        Coordinator(onTokenReceived: onTokenReceived, onFullResponseReceived: onFullResponseReceived)
    }

    func makeUIView(context: Context) -> WKWebView {
        let contentController = WKUserContentController()
        contentController.add(context.coordinator, name: handlerName)
        let config = WKWebViewConfiguration()
        config.userContentController = contentController

        // Forward window.postMessage to iOS
        let js = """
        window.postMessage = function(data) {
            window.webkit.messageHandlers.\(handlerName).postMessage(data);
        };
        """
        let userScript = WKUserScript(source: js, injectionTime: .atDocumentStart, forMainFrameOnly: false)
        contentController.addUserScript(userScript)

        let webView = WKWebView(frame: .zero, configuration: config)
        let request = URLRequest(url: url)
        webView.load(request)
        return webView
    }

    func updateUIView(_ uiView: WKWebView, context: Context) {
        // No-op
    }
}

struct DailypayCardTokenizeResponse: Decodable {
    let firstName: String
    let lastName: String
    let addressLineOne: String
    let addressLineTwo: String
    let addressCity: String
    let addressState: String
    let addressZipCode: String
    let expirationMonth: String
    let expirationYear: String
    let addressCountry: String
    let issuer: String
    let token: String

    enum CodingKeys: String, CodingKey {
        case firstName = "first_name"
        case lastName = "last_name"
        case addressLineOne = "address_line_one"
        case addressLineTwo = "address_line_two"
        case addressCity = "address_city"
        case addressState = "address_state"
        case addressZipCode = "address_zip_code"
        case expirationMonth = "expiration_month"
        case expirationYear = "expiration_year"
        case addressCountry = "address_country"
        case issuer
        case token
    }
}

struct DailypayCardTokenizeMessage: Decodable {
    let type: String
    let response: DailypayCardTokenizeResponse
    let success: Bool
}

struct ContentView: View {
    // Replace client_id and implementation_id with values supplied by DailyPay
    // that are unique to you and your application
    @State private var urlString: String = "https://elements.uat.dailypay.com/v1/debit-card-tokenization?client_id=REPLACE_WITH_YOUR_CLIENT_ID&implementation_id=REPLACE_WITH_YOUR_IMPLEMENTATION_ID"
    @State private var showAlert = false
    @State private var issuer: String? = nil
    @State private var showTokenAlert = false
    @State private var token: String? = nil

    func handleCardTokenizeResponse(_ response: DailypayCardTokenizeResponse) {
        // Add card to DailyPay using POST /accounts
        // See https://developer.dailypay.com/tag/Accounts#operation/createAccount
        print("Ready to send to the DailyPay REST API: \(response)")
    }

    var body: some View {
        VStack {
            TextField("Enter URL", text: $urlString)
                .textFieldStyle(RoundedBorderTextFieldStyle())
                .padding()
            if let url = URL(string: urlString) {
                // Step 1: Set Up a Web View
                WebView(
                    url: url,
                    onTokenReceived: { token in
                        self.token = token
                        self.showTokenAlert = true
                    },
                    onFullResponseReceived: { response in
                        handleCardTokenizeResponse(response)
                    }
                )
                .edgesIgnoringSafeArea(.all)
                // Step 1 End
            } else {
                Text("Invalid URL")
            }
        }
        .alert(isPresented: $showTokenAlert) {
            Alert(
                title: Text("Token Received"),
                message: Text(token ?? "No token"),
                dismissButton: .default(Text("OK"))
            )
        }
    }
}

#Preview {
    ContentView()
}