iOS Integration
Gift Card Store - iOS WebView integration
The iOS integration uses a WKWebView to render the Hubble WebView, enabling the integration of Hubble’s SDK into your iOS application. This document provides a step-by-step guide for integrating and using the Hubble WebView along with detailed descriptions of the primary functions.
Parameters
| Parameter | Required | Description |
|---|---|---|
clientId | Yes | Client ID provided by the Hubble team |
clientSecret | Yes | Client secret provided by the Hubble team |
token | No | Auth token that uniquely identifies your user. Optional if lazy login is enabled. |
wrap-plt | Yes | Platform identifier. Use ios. |
appVersion | No | App version string. Defaults to "10000". |
deviceId | No | Device identifier. |
Deep Links
To navigate directly to a specific page, append the route path before the query parameters. See the Deeplinks page for the full route mapping.
| Page | URL Format |
|---|---|
| Home (default) | https://vouchers.myhubble.money/sdk/gc/?clientId=... |
| Brand purchase | https://vouchers.myhubble.money/sdk/gc/buy/{brandId}?clientId=... |
| Search | https://vouchers.myhubble.money/sdk/gc/search?clientId=... |
| Transactions | https://vouchers.myhubble.money/sdk/gc/transactions?clientId=... |
| Help | https://vouchers.myhubble.money/sdk/gc/help?clientId=... |
Events
The SDK communicates with the host app via a WKScriptMessageHandler registered with the name bridge. Events are JSON objects with a type field:
Action events — SDK lifecycle and navigation:
{"type": "action", "action": "close"}{"type": "action", "action": "app_ready"}{"type": "action", "action": "error"}Analytics events — user interaction tracking:
{"type": "analytics", "event": "event_name", "properties": {"key": "value"}}| Action | Description |
|---|---|
close | User tapped close. Dismiss the WebView. |
app_ready | SDK loaded successfully. |
error | SDK failed to load. Show an error state. |
See the Events page for the full list of analytics events.
Usage
1. Setup the WebView
Use WKWebView to integrate Hubble’s WebView. Customize the WKWebView configuration for message handling and navigation.
var webview: WKWebView!
override func loadView() { let userContentController = WKUserContentController() userContentController.add(self, name: "bridge") // web to native message handler
let configuration = WKWebViewConfiguration() configuration.userContentController = userContentController // Set content controller
webview = WKWebView(frame: .zero, configuration: configuration) webview.navigationDelegate = self webview.uiDelegate = self view = webview}2. Initialization
Initialize the HubbleWebViewController with required parameters.
init(clientId: String, clientSecret: String, token: String? = nil) { self.clientId = clientId self.clientSecret = clientSecret self.token = token super.init(nibName: nil, bundle: nil)}3. Load the WebView
Load the Hubble WebView with the proper URL.
override func viewDidLoad() { var components = URLComponents(string: "https://vouchers.myhubble.money/sdk/gc/")! // For dev: use "https://vouchers.dev.myhubble.money/sdk/gc/"
var queryItems = [ URLQueryItem(name: "clientId", value: clientId), URLQueryItem(name: "clientSecret", value: clientSecret), URLQueryItem(name: "wrap-plt", value: "ios"), ] if let token = token { queryItems.append(URLQueryItem(name: "token", value: token)) } components.queryItems = queryItems
webview.load(URLRequest(url: components.url!)) webview.allowsBackForwardNavigationGestures = true webview.configuration.preferences.javaScriptCanOpenWindowsAutomatically = true}To open a specific page directly (deep link), change the URL path:
// Brand purchase pagevar components = URLComponents(string: "https://vouchers.myhubble.money/sdk/gc/buy/uber")!
// Search pagevar components = URLComponents(string: "https://vouchers.myhubble.money/sdk/gc/search")!
// Transactions pagevar components = URLComponents(string: "https://vouchers.myhubble.money/sdk/gc/transactions")!4. Handling Navigation
Control navigation within the WebView and redirect external links to the default browser.
func webView(_ webView: WKWebView, decidePolicyFor navigationAction: WKNavigationAction, decisionHandler: @escaping (WKNavigationActionPolicy) -> Void) { guard let url = navigationAction.request.url else { decisionHandler(.allow) return }
let baseUrl = URL(string: "https://vouchers.myhubble.money")! // For dev: use "https://vouchers.dev.myhubble.money" let paymentGatewayUrl = URL(string: "https://api.razorpay.com")!
if urlMatchesBaseUrl(url, baseUrl: baseUrl) { decisionHandler(.allow) } else if urlMatchesBaseUrl(url, baseUrl: paymentGatewayUrl) { decisionHandler(.allow) } else { openURLExternally(url) decisionHandler(.cancel) }}
private func urlMatchesBaseUrl(_ url: URL, baseUrl: URL) -> Bool { return url.absoluteString.hasPrefix(baseUrl.absoluteString)}
private func openURLExternally(_ url: URL) { UIApplication.shared.open(url, options: [:], completionHandler: nil)}5. Handling Events
Add WKScriptMessageHandler to HubbleWebViewController to receive events from the SDK.
func userContentController(_ userContentController: WKUserContentController, didReceive message: WKScriptMessage) { guard let body = message.body as? String, let data = body.data(using: .utf8), let json = try? JSONSerialization.jsonObject(with: data) as? [String: Any], let type = json["type"] as? String else { return }
if type == "action" { let action = json["action"] as? String if action == "close" { self.dismiss(animated: true) } else if action == "app_ready" { print("Hubble SDK is ready") } else if action == "error" { print("Hubble SDK failed to load") } } else if type == "analytics" { let event = json["event"] as? String ?? "" let properties = json["properties"] as? [String: Any] ?? [:] print("Analytics event: \(event), properties: \(properties)") }}This integration ensures seamless embedding of Hubble WebView into your iOS application while maintaining proper control over navigation and event handling. Below is the complete iOS snippet for better understanding.
import UIKitimport WebKit
class HubbleWebViewController: UIViewController, WKNavigationDelegate, WKUIDelegate, WKScriptMessageHandler {
func userContentController(_ userContentController: WKUserContentController, didReceive message: WKScriptMessage) { guard let body = message.body as? String, let data = body.data(using: .utf8), let json = try? JSONSerialization.jsonObject(with: data) as? [String: Any], let type = json["type"] as? String else { return }
if type == "action" { let action = json["action"] as? String if action == "close" { self.dismiss(animated: true) } else if action == "app_ready" { print("Hubble SDK is ready") } else if action == "error" { print("Hubble SDK failed to load") } } else if type == "analytics" { let event = json["event"] as? String ?? "" let properties = json["properties"] as? [String: Any] ?? [:] print("Analytics event: \(event), properties: \(properties)") } }
private var clientId: String private var clientSecret: String private var token: String?
init(clientId: String, clientSecret: String, token: String? = nil) { self.clientId = clientId self.clientSecret = clientSecret self.token = token super.init(nibName: nil, bundle: nil) }
required init?(coder: NSCoder) { fatalError("init(coder:) has not been implemented") }
var webview: WKWebView!
override func loadView() { let userContentController = WKUserContentController() userContentController.add(self, name: "bridge")
let configuration = WKWebViewConfiguration() configuration.userContentController = userContentController
webview = WKWebView(frame: .zero, configuration: configuration) webview.navigationDelegate = self webview.uiDelegate = self view = webview webview.topAnchor.constraint(equalTo: view.topAnchor, constant: 0).isActive = true }
override func viewDidLoad() { var components = URLComponents(string: "https://vouchers.myhubble.money/sdk/gc/")! // For dev: use "https://vouchers.dev.myhubble.money/sdk/gc/"
var queryItems = [ URLQueryItem(name: "clientId", value: clientId), URLQueryItem(name: "clientSecret", value: clientSecret), URLQueryItem(name: "wrap-plt", value: "ios"), ] if let token = token { queryItems.append(URLQueryItem(name: "token", value: token)) } components.queryItems = queryItems
webview.load(URLRequest(url: components.url!)) webview.allowsBackForwardNavigationGestures = true webview.configuration.preferences.javaScriptCanOpenWindowsAutomatically = true }
func webView(_ webView: WKWebView, createWebViewWith configuration: WKWebViewConfiguration, for navigationAction: WKNavigationAction, windowFeatures: WKWindowFeatures) -> WKWebView? { if let frame = navigationAction.targetFrame, frame.isMainFrame { return nil } webView.load(navigationAction.request) return nil }
func webView(_ webView: WKWebView, decidePolicyFor navigationAction: WKNavigationAction, decisionHandler: @escaping (WKNavigationActionPolicy) -> Void) { guard let url = navigationAction.request.url else { decisionHandler(.allow) return }
let baseUrl = URL(string: "https://vouchers.myhubble.money")! // For dev: use "https://vouchers.dev.myhubble.money" let paymentGatewayUrl = URL(string: "https://api.razorpay.com")!
if urlMatchesBaseUrl(url, baseUrl: baseUrl) { decisionHandler(.allow) } else if urlMatchesBaseUrl(url, baseUrl: paymentGatewayUrl) { decisionHandler(.allow) } else { openURLExternally(url) decisionHandler(.cancel) } }
private func urlMatchesBaseUrl(_ url: URL, baseUrl: URL) -> Bool { return url.absoluteString.hasPrefix(baseUrl.absoluteString) }
private func openURLExternally(_ url: URL) { UIApplication.shared.open(url, options: [:], completionHandler: nil) }}