2 min read
Customize your webview
Out of dateTo customize your webview you need to inject your own view into the web checkout builder.
Use this guide to add the following customised elements to your webview:
- Customise the webview navigation
- Customise the User Agent
- Customise the post message string
- Customise script handling
- Customise the web view configuration
In the below example, a custom webview called customWebView
is injected. This webview conforms to the builder pattern CheckoutWebViewBuilder
You can change its behaviour by injecting the relevant part of your customisation.
Use this example to see how to provide a custom webview:
import PoqWebCheckoutimport PoqFoundationimport PoqPlatform
class ClientModule: PoqModule { func didAddToPlatform() { // Override platform's deeplink logic. let navigator = NavigationHelper.sharedInstance navigator.mapRoute(navigator.cartTransferURL, toDestination: { _ in navigator.openController(ClientModule.createWebCheckoutViewController()) }) } static func createWebCheckoutViewController() -> UIViewController { let router = CheckoutRouter(navigator: NavigationHelper.sharedInstance) let supportedLocations = [CheckoutLocationDomainModel(host: "poq-web-carttransferdemo.azurewebsites.net", port: nil), CheckoutLocationDomainModel(host: "poq-web-carttransferdemo-uat.azurewebsites.net", port: nil)] let checkoutBridgeConfiguration = CheckoutBridgeConfiguration(supportedLocations: supportedLocations, checkoutPaymentConfiguration: nil) let checkoutType: CheckoutWebViewType = .bridge(configuration: checkoutBridgeConfiguration) let customWebView = CheckoutWebViewBuilder(type: checkoutType).build() return WebCheckoutBuilder<PoqCheckoutState>(router: router, type: checkoutType) .withWebCheckoutView(customWebView) .build() }}
Customise handling cookies
To change how cookies are handled in the web checkout, you need to inject your own CheckoutCookieStore
in the CheckoutWebViewBuilder
.
This protocol takes in a boolean shouldRefreshAllCookies
on concrete class initialisations, which determines whether the cookies are clear/refreshed every time the web checkout is opened (just before they are collected from the start checkout
API response) and injects them.
It implements the function handleCookies
which handles the cookies that are sent from the backend. It does nothing if cookies are nil and also clears previous cookies beforehand if the flag shouldRefreshAllCookies
is enabled.
import PoqWebCheckoutimport PoqFoundationimport PoqPlatform
class ClientModule: PoqModule { func didAddToPlatform() { // Override platform's deeplink logic. let navigator = NavigationHelper.sharedInstance navigator.mapRoute(navigator.cartTransferURL, toDestination: { _ in navigator.openController(ClientModule.createWebCheckoutViewController()) }) } static func createWebCheckoutViewController() -> UIViewController { let router = CheckoutRouter(navigator: NavigationHelper.sharedInstance) let supportedLocations = [CheckoutLocationDomainModel(host: "poq-web-carttransferdemo.azurewebsites.net", port: nil), CheckoutLocationDomainModel(host: "poq-web-carttransferdemo-uat.azurewebsites.net", port: nil)] let checkoutBridgeConfiguration = CheckoutBridgeConfiguration(supportedLocations: supportedLocations, checkoutPaymentConfiguration: nil) let checkoutType: CheckoutWebViewType = .bridge(configuration: checkoutBridgeConfiguration) let customCookieStore = CustomCheckoutCookieStore(shouldRefreshAllCookies: true) let customWebView = CheckoutWebViewBuilder(type: checkoutType) .withCookieStore(customCookieStore) .build() return WebCheckoutBuilder<PoqCheckoutState>(router: router, type: checkoutType) .withWebCheckoutView(customWebView) .build() }}
class CustomCheckoutCookieStore: CheckoutCookieStore { var shouldRefreshAllCookies: Bool init(shouldRefreshAllCookies: Bool) { self.shouldRefreshAllCookies = shouldRefreshAllCookies } func handleCookies(cookies: [HTTPCookie]?, completion: @escaping (() -> Void)) { // Your custom logic goes here }}
Customise the webview navigation
It is possible to inject your own Navigation Delegate for your webview. This component is in charge of handling the web view navigation by passing back the changes of URL to the controller.
import PoqWebCheckoutimport PoqFoundationimport PoqPlatformimport WebKit
class ClientModule: PoqModule { func didAddToPlatform() { // Override platform's deeplink logic. let navigator = NavigationHelper.sharedInstance navigator.mapRoute(navigator.cartTransferURL, toDestination: { _ in navigator.openController(ClientModule.createWebCheckoutViewController()) }) } static func createWebCheckoutViewController() -> UIViewController { let router = CheckoutRouter(navigator: NavigationHelper.sharedInstance) let supportedLocations = [CheckoutLocationDomainModel(host: "poq-web-carttransferdemo.azurewebsites.net", port: nil), CheckoutLocationDomainModel(host: "poq-web-carttransferdemo-uat.azurewebsites.net", port: nil)] let checkoutBridgeConfiguration = CheckoutBridgeConfiguration(supportedLocations: supportedLocations, checkoutPaymentConfiguration: nil) let checkoutType: CheckoutWebViewType = .bridge(configuration: checkoutBridgeConfiguration) let customWebViewNavigationDelegate = CustomWebViewNavigationDelegate() let customWebView = CheckoutWebViewBuilder(type: checkoutType) .withNavigationDelegate(customWebViewNavigationDelegate) .build() return WebCheckoutBuilder<PoqCheckoutState>(router: router, type: checkoutType) .withWebCheckoutView(customWebView) .build() }}
public class CustomWebViewNavigationDelegate: NSObject, WebViewNavigationDelegate { public var checkoutWebViewPresenter: CheckoutWebViewDelegate? public var webViewNavigationData: WebViewNavigationData? public var currentLoadingURL: String? // MARK: - WKNavigationDelegate. public func webView(_ webView: WKWebView, didFinish navigation: WKNavigation!) { // Your custom implementation goes here. } public func webView(_ webView: WKWebView, decidePolicyFor navigationAction: WKNavigationAction, decisionHandler: @escaping (WKNavigationActionPolicy) -> Void) { // Your custom implementation goes here. decisionHandler(.allow) }}
Customise the User Agent
The current User Agent complies with RFC standard (RFC 7231) in this format:
Full User Agent:
user-agent | Mozilla/5.0 (iPhone; CPU iPhone OS 12_1 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) PoqDemoApp-UAT/13.2.0
Where the default value that the iPhone adds is:
user-agent | Mozilla/5.0 (iPhone; CPU iPhone OS 12_1 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko)and the value that we concatenate isPoqDemoApp-UAT/13.2.0
This value, that we concatenate, has two parts separated by the symbol /: The left part and the right part.
The left part: This is string value is taken from the client PLIST value of the key “PoqUserAgent”, therefore, any client can add whatever value they’d like in it. We recommend that you should provide the client name plus the environment.
The right part: This value gets automatically added by getting the
CFBundleShortVersionString
of the App.
If being able to provide your own left part of the string above is not enough configuration for your solution, you could inject your own User Agent and it will be concatenated to the end of the iOS User Agent.
You can inject your own user agent by providing a string value to the builder as follows:
import PoqWebCheckoutimport PoqFoundationimport PoqPlatform
class ClientModule: PoqModule { func didAddToPlatform() { // Override platform's deeplink logic. let navigator = NavigationHelper.sharedInstance navigator.mapRoute(navigator.cartTransferURL, toDestination: { _ in navigator.openController(ClientModule.createWebCheckoutViewController()) }) } static func createWebCheckoutViewController() -> UIViewController { let router = CheckoutRouter(navigator: NavigationHelper.sharedInstance) let supportedLocations = [CheckoutLocationDomainModel(host: "poq-web-carttransferdemo.azurewebsites.net", port: nil), CheckoutLocationDomainModel(host: "poq-web-carttransferdemo-uat.azurewebsites.net", port: nil)] let checkoutBridgeConfiguration = CheckoutBridgeConfiguration(supportedLocations: supportedLocations, checkoutPaymentConfiguration: nil) let checkoutType: CheckoutWebViewType = .bridge(configuration: checkoutBridgeConfiguration) let customUserAgent = "CustomUserAgent" let customWebView = CheckoutWebViewBuilder(type: checkoutType) .withUserAgent(customUserAgent) .build() return WebCheckoutBuilder<PoqCheckoutState>(router: router, type: checkoutType) .withWebCheckoutView(customWebView) .build() }}
In this example, the User Agent output of the above customisation will be:
user-agent | Mozilla/5.0 (iPhone; CPU iPhone OS 12_1 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) CustomUserAgent
Customise the post message string
If your website needs to communicate to the App through a different name in the Script Handler, you can provide your own message string as follows:
import PoqWebCheckoutimport PoqFoundationimport PoqPlatform
class ClientModule: PoqModule { func didAddToPlatform() { // Override platform's deeplink logic. let navigator = NavigationHelper.sharedInstance navigator.mapRoute(navigator.cartTransferURL, toDestination: { _ in navigator.openController(ClientModule.createWebCheckoutViewController()) }) } static func createWebCheckoutViewController() -> UIViewController { let router = CheckoutRouter(navigator: NavigationHelper.sharedInstance) let supportedLocations = [CheckoutLocationDomainModel(host: "poq-web-carttransferdemo.azurewebsites.net", port: nil), CheckoutLocationDomainModel(host: "poq-web-carttransferdemo-uat.azurewebsites.net", port: nil)] let checkoutBridgeConfiguration = CheckoutBridgeConfiguration(supportedLocations: supportedLocations, checkoutPaymentConfiguration: nil) let checkoutType: CheckoutWebViewType = .bridge(configuration: checkoutBridgeConfiguration) let customPostMessage = "CustomPostMessageString" let customWebView = CheckoutWebViewBuilder(type: checkoutType) .withPostMessageString(customPostMessage) .build() return WebCheckoutBuilder<PoqCheckoutState>(router: router, type: checkoutType) .withWebCheckoutView(customWebView) .build() }}
Customise script handling
You can provide your own script handler by injecting a concrete implementation of the protocol ScriptMessageHandlerDelegate
into the CheckoutWebViewBuilder
builder.
This implementation should handle the message that the App receives from the web. The default implementation of this protocol sends back the message to the controller.
import PoqWebCheckoutimport PoqFoundationimport PoqPlatformimport WebKit
class ClientModule: PoqModule { func didAddToPlatform() { // Override platform's deeplink logic. let navigator = NavigationHelper.sharedInstance navigator.mapRoute(navigator.cartTransferURL, toDestination: { _ in navigator.openController(ClientModule.createWebCheckoutViewController()) }) } static func createWebCheckoutViewController() -> UIViewController { let router = CheckoutRouter(navigator: NavigationHelper.sharedInstance) let supportedLocations = [CheckoutLocationDomainModel(host: "poq-web-carttransferdemo.azurewebsites.net", port: nil), CheckoutLocationDomainModel(host: "poq-web-carttransferdemo-uat.azurewebsites.net", port: nil)] let checkoutBridgeConfiguration = CheckoutBridgeConfiguration(supportedLocations: supportedLocations, checkoutPaymentConfiguration: nil) let checkoutType: CheckoutWebViewType = .bridge(configuration: checkoutBridgeConfiguration) let customScriptHandler = CustomScriptMessageHandlerDelegate() let customWebView = CheckoutWebViewBuilder(type: checkoutType) .withScriptMessageHandler(customScriptHandler) .build() return WebCheckoutBuilder<PoqCheckoutState>(router: router, type: checkoutType) .withWebCheckoutView(customWebView) .build() }}
class CustomScriptMessageHandlerDelegate: NSObject, ScriptMessageHandlerDelegate { var checkoutWebViewPresenter: CheckoutWebViewDelegate? func userContentController(_ userContentController: WKUserContentController, didReceive message: WKScriptMessage) { // Your custom implementation goes here. }}
Customise the web view configuration
If you’d like to completely replace the webview configuration of your Poq App, you can do this through the CheckoutWebViewBuilder
.
If you provide your own configuration, you also need to set:
- A user agent
(applicationNameForUserAgent)
. - A script handler.
- A post message string to your own configuration.
Example:
import PoqWebCheckoutimport PoqFoundationimport PoqPlatformimport WebKit
class ClientModule: PoqModule { func didAddToPlatform() { // Override platform's deeplink logic. let navigator = NavigationHelper.sharedInstance navigator.mapRoute(navigator.cartTransferURL, toDestination: { _ in navigator.openController(ClientModule.createWebCheckoutViewController()) }) } static func createWebCheckoutViewController() -> UIViewController { let router = CheckoutRouter(navigator: NavigationHelper.sharedInstance) let supportedLocations = [CheckoutLocationDomainModel(host: "poq-web-carttransferdemo.azurewebsites.net", port: nil), CheckoutLocationDomainModel(host: "poq-web-carttransferdemo-uat.azurewebsites.net", port: nil)] let checkoutBridgeConfiguration = CheckoutBridgeConfiguration(supportedLocations: supportedLocations, checkoutPaymentConfiguration: nil) let checkoutType: CheckoutWebViewType = .bridge(configuration: checkoutBridgeConfiguration) let customUserAgent = "CustomUserAgent" let postMessageString: String = "poqWebCheckout" let scriptMessageHandler: ScriptMessageHandlerDelegate = PoqScriptMessageHandlerDelegate() let config = WKWebViewConfiguration() config.userContentController.add(scriptMessageHandler, name: postMessageString) config.applicationNameForUserAgent = customUserAgent
let customWebView = CheckoutWebViewBuilder(type: checkoutType) .withConfiguration(config) .build() return WebCheckoutBuilder<PoqCheckoutState>(router: router, type: checkoutType) .withWebCheckoutView(customWebView) .build() }}