Hybrid Apps

Overview

Some apps are designed to use a combination of native rendering and rendering HTML pages in a webview. To make this work with Granify, it is important that the iOS SDK be used on all native pages, and that the JS SDK be used for each HTML page loaded into a webview. Furthermore, it is necessary to pass state from the app to the webview in order for both SDKs to work in the same session.

For example, an app may be designed to use native rendering for all collection and product pages, but use the company’s website (in a webview) for the complex checkout and payment process. In this case, the app itself would have knowledge of products and collections, but have no knowledge at all about the cart, the checkout process, or the state of an order. Conversely, the website could know all about the cart, checkout and order details, but have no idea about the shoppers’ browsing behaviour.

Granify requires that both these sources of information exist in the same session to be effective. Therefore, it is important that Granify be running in both contexts, and that Granify is able to share data from one to the other, so that we can ensure both Granify SDKs operate on the same session.

Implementing State Transfer

You must pass the current Granify app state to the webview when it is loaded to allow it to continue the session. You do this by calling the Granify.getCurrentState() method. This will return a string containing a JavaScript script. You must then apply this script to the webview unmodified, as soon as it has loaded the initial page.

For example (in a UIWebView implementing the UIWebViewDelegate protocol):

func webViewDidFinishLoad(_ webView: UIWebView) {    
    if ensurePageLoadsGranifySDK(webView.request?.url), let stateScript = try? Granify.getCurrentState() {
        webView.stringByEvaluatingJavaScript(from: stateScript)
    }
}

Another example (in a WKWebView implementing the WKNavigationDelegate protocol):

func webView(_ webView: WKWebView, didFinish navigation: WKNavigation!) {    
    if ensurePageLoadsGranifySDK(webView.request?.url), let stateScript = try? Granify.getCurrentState() {
        let script = WKUserScript(source: stateScript, injectionTime: .atDocumentEnd, forMainFrameOnly: true)
        webView.configuration.userContentController.addUserScript(script)
    }
}

This will re-fetch the state, and re-apply it to the webview every time a new page is loaded in the webview. This is exactly what we want.

Note that the state only needs to be injected into pages that load the Granify JavaScript SDK. If your app loads content from third party vendors into your webview, you should add a check to prevent injecting the Granify state into those pages. The script itself should be safe on those pages, but it will unnecessarily consume resources, and there have been issues with some vendors in the UIWebView in the past.

The ensurePageLoadsGranifySDK() method in the examples above represents a method you should write to ensure that you are only injecting the Granify State into a page that loads the Granify SDK.

Warning

We have discovered that PayPal does a large number of redirects while loading and logging in, and this can sometimes cause issues with the UIWebView if you attempt to inject the Granify state while the webView is performing a redirect. Apple recommends using the WKWebView, but that may not always be possible. We recommend not injecting the Granify state into PayPal pages.

A Note On Loading Order In The Webview

There are a great many ways that a web page might be loaded. Modern websites have many dependencies, and the order in which they are loaded can be significant. This script has been designed so that it doesn’t matter where in the load sequence the JavaScript is injected, as long as the page has started loading. If you inject our JavaScript before the page starts loading, then the new page will overwrite our JavaScript, and render it useless. But it is perfectly safe to inject the JavaScript before the Granify JS SDK has been loaded, and it will be equally effective to load it after the JS SDK has loaded. In that case, the JS SDK will wait until this JavaScript is injected.

Note

The fact that the JS SDK will wait for the state to be injected means that if you fail to inject the state at all, the JS SDK will never initialize, and Granify will not work. It is therefore extremely important that the state be injected for every page that is loaded in the webview, and not just the first time.

Passing State Back To The App

We do not currently support passing state back from the webview to the app. We have plans to implement this feature in a future release, but for now we only pass state from the app to the webview.

This means there are certain arrangements of native / webview that we cannot support. Granify approaches the shopper’s journey as a multi-step process:

browsing and choosing products -> adding to cart -> checking out and payment

Granify’s internal state passes information only one way down this channel. So, at the cart stage, we need to know about the shopper browsing behaviour, but at the browsing stage, we do not need to know about cart or checkout behaviour. Therefore, if your app divides itself along these lines - all browsing is done natively, and the cart and checkout are handled in a webview, or both browsing and cart are handled natively, and only checkout is handled in the webview - our current implementation will work just fine.

However, if your app divides in any other way - maybe you use webviews to render some special product types, while other products, along with the cart and checkout, are rendered natively - our current implementation will not be sufficient. If this is you, please contact your Granify representative with details of your current native / webview arrangement, and we’ll see what we can do to create a solution that will cover your needs.

Requirements for the JS SDK

The only thing that needs to change on the JS SDK side is that the SDK must be downloaded using the iOS App Site ID, and not your normal web Site ID. We need both sides to be working in the same session, and that session will have started with the iOS Site ID, so that needs to be maintained across the whole session. No other changes are required to make the JS SDK work correctly in this context.