Skip to main content

Swift (iOS) Installation

Integrate the Asurion Widget into your native iOS application using WKWebView. This approach loads the web widget inside a native WebView component, providing a seamless experience on iOS devices.

Prerequisites

  • iOS 11.0 or higher
  • Xcode 13.0 or higher
  • Swift 5.0 or higher

Basic Usage

Create a view controller that renders the widget inside a WKWebView:

import UIKit
import WebKit

class AsurionWidgetViewController: UIViewController, WKUIDelegate {

private var webView: WKWebView!
private let widgetId = "<WIDGET_ID>"
private let context: [String: Any] = ["userId": "user-123", "platform": "ios"]
private let trackingParameters: [String: String] = ["utm_source": "mobile-app", "utm_medium": "webview"]

override func viewDidLoad() {
super.viewDidLoad()
setupWebView()
loadWidget()
}

private func setupWebView() {
let configuration = WKWebViewConfiguration()
configuration.allowsInlineMediaPlayback = true
configuration.mediaTypesRequiringUserActionForPlayback = []

webView = WKWebView(frame: view.bounds, configuration: configuration)
webView.autoresizingMask = [.flexibleWidth, .flexibleHeight]
webView.scrollView.bounces = false
webView.isOpaque = false
webView.backgroundColor = .clear
webView.uiDelegate = self

view.addSubview(webView)
}

// Handle window.open() calls from JavaScript
func webView(_ webView: WKWebView,
createWebViewWith configuration: WKWebViewConfiguration,
for navigationAction: WKNavigationAction,
windowFeatures: WKWindowFeatures) -> WKWebView? {
if let url = navigationAction.request.url {
UIApplication.shared.open(url)
}
return nil
}

private func loadWidget() {
let html = generateWidgetHTML()
webView.loadHTMLString(html, baseURL: nil)
}

private func generateWidgetHTML() -> String {
return """
<!DOCTYPE html>
<html>
<head>
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no" />
<style>
* { margin: 0; padding: 0; }
html, body { height: 100%; width: 100%; overflow: hidden; }
</style>
<script>
(function (w, d, s, o, f, a, js, fjs) {
w[o] = function () {
const [action, ...params] = arguments;
(w[o].q = w[o].q || []).push([action, ...params]);
};
(js = d.createElement(s)), (fjs = d.getElementsByTagName(s)[0]);
js.id = o;
js.src = f;
js.async = 1;
js.setAttribute('data-widget-id', a.id);
js.setAttribute('data-variant', a.variant);
w[o + '_context'] = a.context;
w[o + '_trackingParameters'] = a.trackingParameters;
fjs.parentNode.insertBefore(js, fjs);

// Forward widget events to native
w[o]('subscribe', 'state', function (state) {
window.webkit.messageHandlers.widgetEvents.postMessage({ type: 'state', payload: state });
});
w[o]('subscribe', 'options', function (options) {
window.webkit.messageHandlers.widgetEvents.postMessage({ type: 'options', payload: options });
});
})(
window,
document,
'script',
'_asurion_widget',
'https://app.widget-loader.service-initiation.asurion.com/widget-loader.js',
{
id: '\(widgetId)',
variant: 'embedded',
context: \(jsonString(from: context) ?? "{}"),
trackingParameters: \(jsonString(from: trackingParameters) ?? "{}"),
}
);
</script>
</head>
<body></body>
</html>
"""
}

private func jsonString(from dictionary: [String: Any]?) -> String? {
guard let dictionary = dictionary,
let data = try? JSONSerialization.data(withJSONObject: dictionary),
let string = String(data: data, encoding: .utf8) else {
return nil
}
return string
}
}

SwiftUI Integration

For SwiftUI applications, create a UIViewRepresentable wrapper:

import SwiftUI
import WebKit

struct AsurionWidgetView: UIViewRepresentable {
let widgetId: String
var context: [String: Any]?
var trackingParameters: [String: String]?

func makeCoordinator() -> Coordinator {
Coordinator()
}

func makeUIView(context: Context) -> WKWebView {
let configuration = WKWebViewConfiguration()
configuration.allowsInlineMediaPlayback = true
configuration.mediaTypesRequiringUserActionForPlayback = []

let webView = WKWebView(frame: .zero, configuration: configuration)
webView.scrollView.bounces = false
webView.isOpaque = false
webView.backgroundColor = .clear
webView.uiDelegate = context.coordinator

return webView
}

func updateUIView(_ webView: WKWebView, context: Context) {
let html = generateWidgetHTML()
webView.loadHTMLString(html, baseURL: nil)
}

class Coordinator: NSObject, WKUIDelegate {
func webView(_ webView: WKWebView,
createWebViewWith configuration: WKWebViewConfiguration,
for navigationAction: WKNavigationAction,
windowFeatures: WKWindowFeatures) -> WKWebView? {
if let url = navigationAction.request.url {
UIApplication.shared.open(url)
}
return nil
}
}

private func generateWidgetHTML() -> String {
let contextJSON = jsonString(from: self.context) ?? "{}"
let trackingJSON = jsonString(from: trackingParameters) ?? "{}"

return """
<!DOCTYPE html>
<html>
<head>
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no" />
<style>
* { margin: 0; padding: 0; }
html, body { height: 100%; width: 100%; overflow: hidden; }
</style>
<script>
(function (w, d, s, o, f, a, js, fjs) {
w[o] = function () {
const [action, ...params] = arguments;
(w[o].q = w[o].q || []).push([action, ...params]);
};
(js = d.createElement(s)), (fjs = d.getElementsByTagName(s)[0]);
js.id = o;
js.src = f;
js.async = 1;
js.setAttribute('data-widget-id', a.id);
js.setAttribute('data-variant', a.variant);
w[o + '_context'] = a.context;
w[o + '_trackingParameters'] = a.trackingParameters;
fjs.parentNode.insertBefore(js, fjs);

// Forward widget events to native
w[o]('subscribe', 'state', function (state) {
window.webkit.messageHandlers.widgetEvents.postMessage({ type: 'state', payload: state });
});
w[o]('subscribe', 'options', function (options) {
window.webkit.messageHandlers.widgetEvents.postMessage({ type: 'options', payload: options });
});
})(
window,
document,
'script',
'_asurion_widget',
'https://app.widget-loader.service-initiation.asurion.com/widget-loader.js',
{
id: '\(widgetId)',
variant: 'embedded',
context: \(contextJSON),
trackingParameters: \(trackingJSON),
}
);
</script>
</head>
<body></body>
</html>
"""
}

private func jsonString(from dictionary: [String: Any]?) -> String? {
guard let dictionary = dictionary,
let data = try? JSONSerialization.data(withJSONObject: dictionary),
let string = String(data: data, encoding: .utf8) else {
return nil
}
return string
}
}

// Usage in SwiftUI
struct ContentView: View {
var body: some View {
AsurionWidgetView(
widgetId: "<WIDGET_ID>",
context: ["userId": "user-123", "platform": "ios"]
)
.edgesIgnoringSafeArea(.all)
}
}

Configuration Options

OptionTypeRequiredDescription
idstringtrueYour unique widget identifier provided by Asurion
variantstringtrueWidget variant type. For iOS WebView, only 'embedded' is supported
contextobjectfalseAdditional context data to pass to the widget. Can contain any key-value pairs
trackingParametersobjectfalseTracking parameters that will be appended to external links within the widget. Must be string key-value pairs

Communicating with the Widget

You can send commands to the widget using the evaluateJavaScript method:

class AsurionWidgetViewController: UIViewController, WKUIDelegate {

private var webView: WKWebView!

// ... viewDidLoad, setupWebView, loadWidget, generateWidgetHTML ...
// ... webView(_:createWebViewWith:for:windowFeatures:) ...

func extendContext(_ data: [String: Any]) {
guard let jsonData = try? JSONSerialization.data(withJSONObject: data),
let jsonString = String(data: jsonData, encoding: .utf8) else {
return
}

let script = "window._asurion_widget('extendContext', \(jsonString));"
webView.evaluateJavaScript(script, completionHandler: nil)
}

func extendTrackingParameters(_ params: [String: String]) {
guard let jsonData = try? JSONSerialization.data(withJSONObject: params),
let jsonString = String(data: jsonData, encoding: .utf8) else {
return
}

let script = "window._asurion_widget('extendTrackingParameters', \(jsonString));"
webView.evaluateJavaScript(script, completionHandler: nil)
}
}

Subscribing to Widget Events

The widget forwards events to native code using window.webkit.messageHandlers (configured in Basic Usage above). Handle these events by adding a script message handler:

class AsurionWidgetViewController: UIViewController, WKUIDelegate, WKScriptMessageHandler {

private var webView: WKWebView!

// ...

private func setupWebView() {
let configuration = WKWebViewConfiguration()
configuration.allowsInlineMediaPlayback = true
configuration.mediaTypesRequiringUserActionForPlayback = []
configuration.userContentController.add(self, name: "widgetEvents")

webView = WKWebView(frame: view.bounds, configuration: configuration)
// ... other setup ...
}

func userContentController(_ userContentController: WKUserContentController,
didReceive message: WKScriptMessage) {
guard message.name == "widgetEvents",
let body = message.body as? [String: Any],
let type = body["type"] as? String else {
return
}

switch type {
case "state":
if let payload = body["payload"] as? String {
print("Widget state: \(payload)")
}
case "options":
if let payload = body["payload"] {
print("Widget options: \(payload)")
}
default:
break
}
}
}