iOS Integration
If you haven’t already, download Web Discover SDK. Unzip the file, then drag and drop the web discover sdk folder into your Xcode project. Make sure Copy items if needed
, and Create folder references
are checked, along with the appropriate targets next to Add to targets
.
To allow for customization of the web discover app, we inject javascript and modify index.html
.
Injecting vouchrConfig
First we will inject the vouchrConfig
dictionary shown in /config/config.js
:
var vouchrConfig = {
apiKey: 'YOUR_API_KEY',
networkKey: 'YOUR_NETWORK_KEY',
baseApiUrl: 'https://api.vouchrsdk.com/api',
webRevealUrl: 'https://partnerapp.com/reveal'
};
To accomplish this, we write a method that takes property values as parameters, and returns the vouchrConfig
dictionary as a string.
- (NSString *)vouchrConfigStringWithApiKey:(NSString *)apiKey networkKey:(NSString *)networkKey baseApiUrl:(NSString *)baseApiUrl webRevealUrl:(NSString *)webRevealUrl {
return [NSString stringWithFormat:@"{ apiKey: '%@', networkKey: '%@', baseApiUrl: '%@', webRevealUrl: '%@'}", apiKey, networkKey, baseApiUrl, webRevealUrl];
}
func vouchrConfigString(withApiKey apiKey: String, networkKey: String, baseApiUrl: String, webRevealUrl: String) -> String {
return "{ apiKey: '\(apiKey)', networkKey: '\(networkKey)', baseApiUrl: '\(baseApiUrl)', webRevealUrl: '\(webRevealUrl)'}"
}
We will use this method when launching the WKWebView
. Next we will edit index.html
, and write a method to inject the creation of a vouchrApp
.
Setup Callbacks in index.html
Open index.html
, and replace the return
statements in the VouchrApp
constructor with window.webkit.messageHandlers.<#name#>.postMessage('#event_tag#')
statements. This sets up callbacks, which will be further described in the Receiving Callbacks section below. Note that the delaration of vouchrApp
, and calling the `render()’ function have been modified from the original.
var vouchrApp;
vouchrApp = new window.VouchrApp({
vouchrConfig,
idToken,
active: function () {
window.webkit.messageHandlers.vouchr.postMessage('active')
},
summary: function () {
window.webkit.messageHandlers.vouchr.postMessage('summary')
},
complete: function (voucherId) {
window.webkit.messageHandlers.vouchr.postMessage('complete('.concat(voucherId, ')'))
},
error: function () {
window.webkit.messageHandlers.vouchr.postMessage('error')
},
cancel: function () {
window.webkit.messageHandlers.vouchr.postMessage('cancel')
});
vouchrApp.render(document.getElementById('vouchrAppRoot'));
Injecting VouchrApp Creation and Rendering
In order to inject the code above, which constructs a VouchrApp
and renders it on screen, we write a method that take in the config
and userToken
as parameters and returns a string representation of the JavaScript. Copy the code above, and format it into a string by removing whitespaces, etc. Add it to a method like the following. Note the property keys for vouchrConfig
, and idToken
.
- (NSString *)vouchrAppSetupStringWithConfig:(NSString *)config userToken:(NSString *)token {
return [NSString stringWithFormat:@"var vouchrApp;"
"vouchrApp = new window.VouchrApp({vouchrConfig: %@, idToken: '%@',"
"active: function () {"
"window.webkit.messageHandlers.vouchr.postMessage('active') },"
"summary: function () {"
"window.webkit.messageHandlers.vouchr.postMessage('summary') },"
"complete: function (voucherId) {"
"window.webkit.messageHandlers.vouchr.postMessage('complete('.concat(voucherId, ')')) },"
"error: function () {"
"window.webkit.messageHandlers.vouchr.postMessage('error') },"
"cancel: function () {"
"window.webkit.messageHandlers.vouchr.postMessage('cancel') }});"
"vouchrApp.render(document.getElementById('vouchrAppRoot'));", config, token];
}
func vouchrAppSetupString(withConfig config: String, userToken token: String) -> String {
return """
var vouchrApp;
vouchrApp = new window.VouchrApp({vouchrConfig: \(config), idToken: '\(token)',
active: function () {
window.webkit.messageHandlers.vouchr.postMessage('active')
},
summary: function () {
window.webkit.messageHandlers.vouchr.postMessage('summary')
},
complete: function (voucherId) {
window.webkit.messageHandlers.vouchr.postMessage('complete('.concat(voucherId, ')'))
},
error: function () {
window.webkit.messageHandlers.vouchr.postMessage('error')
},
cancel: function () {
window.webkit.messageHandlers.vouchr.postMessage('cancel')
});
vouchrApp.render(document.getElementById('vouchrAppRoot'));
"""
}
Now that we have the methods to inject JavaScript, we no longer need to use config.js
, and we can modify index.html
by removing the code that creates a VouchrApp
and renders it on screen. Open index.html
, locate the tags in the snippet below, and delete the lines in-between.
...<div id="vouchrAppRoot"></div>
<!-- DELETE CODE IN-BETWEEN -->
<script src="./static/js/runtime~main.d653cc00.js"></script>...
Launching WKWebView
We are now ready to create and launch Web Discover in a WKWebView
. We will use the methods created in Injecting vouchrConfig and Injecting VouchrApp Creation and Rendering, which create JavaScript strings for vouchrConfig
, and vouchrApp
. The following sample code initializes and loads a WKWebView
with your custom Web Discover app.
- (void)setupWebView {
WKWebViewConfiguration *configuration = [[WKWebViewConfiguration alloc] init];
WKUserContentController *userContentController = [[WKUserContentController alloc] init];
/* To receive callbacks, register the <#name#> from window.webkit.messageHandlers.<#name#>.postMessage('#event_tag#'), here "vouchr" is used as an example */
[userContentController addScriptMessageHandler:self name:@"vouchr"];
configuration.userContentController = userContentController;
NSString *vouchrConfig = [self vouchrConfigString]; //use the method created in "Injecting vouchrConfig" above
NSString *vouchrAppSetup = [self vouchrAppSetupStringWithConfig:vouchrConfig userToken:self.userToken];
WKUserScript *vouchrAppSetupScript = [[WKUserScript alloc] initWithSource:vouchrAppSetup injectionTime:WKUserScriptInjectionTimeAtDocumentEnd forMainFrameOnly:NO];
[configuration.userContentController addUserScript:vouchrAppSetupScript]; //inject JS here
self.webView = [[WKWebView alloc] initWithFrame:self.view.frame configuration:configuration];
self.webView.navigationDelegate = self;
[self.view addSubview:self.webView];
/* subdirectory is the folder containing index.html, an example is given; yours could be different */
NSURL *url = [[NSBundle mainBundle] URLForResource:@"index" withExtension:@"html" subdirectory:@"web-discover-sdk"];
[self.webView loadFileURL:url allowingReadAccessToURL:url]; //loads the webview
}
func setupWebView() {
let configuration = WKWebViewConfiguration()
let userContentController = WKUserContentController()
// To receive callbacks, register the <#name#> from window.webkit.messageHandlers.<#name#>.postMessage('#event_tag#'), here "vouchr" is used as an example
userContentController.add(self, name: "vouchr")
configuration.userContentController = userContentController
let vouchrConfig = vouchrConfigString() //use the method created in "Injecting vouchrConfig" above
let vouchrAppSetup = vouchrAppSetupString(withConfig: vouchrConfig, userToken: userToken)
let vouchrAppSetupScript = WKUserScript(source: vouchrAppSetup, injectionTime: .atDocumentEnd, forMainFrameOnly: false)
configuration.userContentController.addUserScript(vouchrAppSetupScript) //inject JS here
self.webView = WKWebView(frame: view.frame, configuration: configuration)
self.webView.navigationDelegate = self
self.view.addSubview(webView)
// subdirectory is the parent folder of index.html, an example is given; yours could be different
let url = Bundle.main.url(forResource: "index", withExtension: "html", subdirectory: "web-discover-sdk")
self.webView.loadFileURL(url, allowingReadAccessTo: url) //loads the webview
}
Receiving Callbacks
To receive the callbacks we setup in Setup Callbacks in index.html, ensure your view controller conforms to the WKScriptMessageHandler
protocol, which has one required method, userContentController:didReceiveScriptMessage
, shown in the sample code below. Recall that we added window.webkit.messageHandlers.<#name#>.postMessage('#event_tag#')
to each function. The <#name#>
corresponds to message.name
, and the #event_tag#
corresponds to message.body
.
- (void)userContentController:(WKUserContentController *)userContentController didReceiveScriptMessage:(WKScriptMessage *)message {
NSString *messageHandlerName = @"vouchr"; //the example name given above
if ([message.name isEqualToString:messageHandlerName]) {
if ([message.body isEqualToString:@"active"]) {
//screen is active
} else if ([message.body isEqualToString:@"summary"]) {
//setup and launch summary screen
} else if ([message.body containsString:@"complete"]) {
//e.g., get voucherId
} else if ([message.body isEqualToString:@"error"]) {
//handle error
} else if ([message.body isEqualToString:@"cancel"]) {
[self dismissViewControllerAnimated:YES completion:nil];
}
}
}
func userContentController(_ userContentController: WKUserContentController, didReceive message: WKScriptMessage) {
let messageHandlerName = "vouchr" //the example name given above
if (message.name == messageHandlerName) {
if (message.body == "active") {
//screen is active
} else if (message.body == "summary") {
//setup and launch summary screen
} else if message.body.contains("complete") {
//e.g., get voucherId
} else if (message.body == "error") {
//handle error
} else if (message.body == "cancel") {
dismiss(animated: true)
}
}
}