Swift

Opening external links

Inside our apps, we may include external links that require the user to navigate to a different page, such as Privacy Policy links. In these cases, we add a query parameter to the URL named openInExternalBrowser=true to indicate to our customers that these links must be opened in the user's default browser, so we do not lose the context of the application.

📘

For most Apps, you should do just fine by opening external links using an In-App browser, but for the Fund product, it is imperative that you open external links in the user's default browser so that they can have a better UX.

Webview handler name

To use our SDK with Swift , you must add the name of message handler as NativeIOS when using the WKWebView. This handler allows our server to communicate with WebViews in Swift. The example below shows how you can implement this in Swift, feel free to use your own implementation knowing that you must open external links with query param openInExternalBrowser=true on external browsers and set the message handler as NativeIOS.

import Foundation
import WebKit
import SwiftUI
struct WebViewContainer: UIViewRepresentable {
    
    let sdkMobileServer = "https://sdk-mobile.cert.zerohash.com/v1/"
    let zeroHashAppsURL = "https://web-sdk.cert.zerohash.com/"
    var token: String = "<JWT_TOKEN_HERE>"
    var url : URL = URL(string: "\(sdkMobileServer)?achDepositsJWT=\(token)&cryptoBuyJWT=\(token)&cryptoSellJWT=\(token)&cryptoWithdrawalsJWT=\(token)&fiatWithdrawalsJWT=\(token)&userOnboardingJWT=\(token)&zeroHashAppsURL=\(zeroHashAppsURL)")!

    class Coordinator: NSObject, WKNavigationDelegate, WKScriptMessageHandler {
        var webView: WKWebView?
        var token: String
        
        init(token: String) {
            self.token = token
        }
        
        func webView(_ webView: WKWebView, didFinish navigation: WKNavigation!) {
            self.webView = webView
        }
				func webView(_ webView: WKWebView, decidePolicyFor navigationAction: WKNavigationAction, decisionHandler: @escaping (WKNavigationActionPolicy) -> Void) {
            // Get the URL that was clicked
            guard let url = navigationAction.request.url else {
                decisionHandler(.allow)
                return
            }
            print("Navigation requested to: \(url.absoluteString)")
            print("Navigation type: \(navigationAction.navigationType.rawValue)")
            // Parse URL components to check for query parameters
            if let components = URLComponents(url: url, resolvingAgainstBaseURL: true),
            let items = components.queryItems,
            items.contains(where: { $0.name == "openInExternalBrowser" && $0.value == "true" }) {
                print("Opening in external browser: \(url.absoluteString)")
                UIApplication.shared.open(url)
                // Cancel the navigation in the WebView
                decisionHandler(.cancel)
                return
            }
            // Allow all other navigation
            decisionHandler(.allow)
        }
        
        // Handle messages from the WebView
        func userContentController(_ userContentController: WKUserContentController, didReceive message: WKScriptMessage) {
            guard let jsonString = message.body as? String else {
                print("Unexpected message type:", type(of: message.body))
                return
            }
            do {
                guard let jsonObject = try JSONSerialization.jsonObject(with: Data(jsonString.utf8), options: []) as? [String: Any] else {
                    print("Failed to convert JSON string to a JSON object")
                    return
                }
                guard let messageType = jsonObject["type"] as? String else {
                    print("Missing 'type' key in JSON object")
                    return
                }
                
                print("Message type:", messageType)
                if messageType == "SDK_MOBILE_READY" {
                    print("SDK Mobile ready:", messageType)
                    self.sendMessageToWebView("OPEN_MODAL")
                }
                // Handle other message types here
                // Your code here...
            } catch {
                print("Error parsing JSON:", error.localizedDescription)
            }
        }

        func sendMessageToWebView(_ event: String) {
            let script = "window.postMessage({type: '\(event)', payload: { appIdentifier: 'crypto-buy'}});"
            print("Sending message to WebView:", event)
            // print("Sending message to WebView:", script)
            webView?.evaluateJavaScript(script, completionHandler: { (result, error) in
                if let error = error {
                    print("Error sending message to WebView:", error.localizedDescription)
                }
            })
        }
      
      	// Implement this method to handle navigation requests
        func webView(_ webView: WKWebView, decidePolicyFor navigationAction: WKNavigationAction, decisionHandler: @escaping (WKNavigationActionPolicy) -> Void) {
            if let url = navigationAction.request.url {
                // Check if the URL is an external link
                if navigationAction.navigationType == .linkActivated {
                    // Optionally open external links in the default browser
                    UIApplication.shared.open(url, options: [:], completionHandler: nil)
                    decisionHandler(.cancel) // Cancel the request so the URL doesn't load in the webView
                } else {
                    decisionHandler(.allow) // Allow the request to load within the webView
                }
            } else {
                decisionHandler(.allow) // Allow the request to load within the webView
            }
        }
    }

    func makeCoordinator() -> Coordinator {
        return Coordinator(onClose: onClose, jwt: jwt, module: module)
    }
    
    func makeUIView(context: Context) -> WKWebView {
        let coordinator = makeCoordinator()
        let userContentController = WKUserContentController()
        // Add a script message handler for the 'NativeIOS' message name
        // IMPORTANT: do not change the message name 'NativeIOS' as it is used by the SDK to communicate with the native app
        userContentController.add(coordinator, name: "NativeIOS")
        
        let configuration = WKWebViewConfiguration()
        configuration.userContentController = userContentController
        // IMPORTANT: This line is required so that the Onboarding document collection works properly.
        configuration.allowsInlineMediaPlayback = true
        
        let _wkwebview = WKWebView(frame: .zero, configuration: configuration)
        _wkwebview.navigationDelegate = coordinator
        _wkwebview.load(URLRequest(url: url))
        
        return _wkwebview
    }
}