top of page
Search
Writer's pictureTony Nelson

Hybrid Application Development Using WKWebView and Salesforce



What is WKWebView

Today’s blog will be about mobile application implementation using Salesforce MobileSDK and how to combine the web page using internal browser WebView and native codes. Originally, Apple Inc. used to provide a component called UIWebView which shows a web browser in a native application. WKWebView is released and supports iOS8 as the updated version of UIWebView.


Some of the highlights of WKWebView:

  • Its performance is higher than UIWebView because it is powered by a JavaScript engine just like the iOS Safari.

  • More methods, such as retrieving the loading status, are available.


It is said that the third party browsers like iOS version of Chrome could never beat the performance of Safari. One thing to be careful with WKWebView is that it supports only iOS8 and up. If you need to provide support for iOS7, you will need to coexist with UIWebView.


Delivering Session to WKWebView

One of the concerns in hybrid application implementation is how to deliver sessions – how to show pages with WebView in a native application using Salesforce MobileSDK. To display Salesforce pages with WebView, you will need to deliver pages with OAuth permission from the native side at the page transition. Now, let’s take a look at the code to implement this.


func createAccountRequest() -> NSURLRequest {

let instanceUrl: String = SFRestAPI.sharedInstance().coordinator.credentials.instanceUrl.description

let accessToken: String = SFRestAPI.sharedInstance().coordinator.credentials.accessToken

let authUrl: String = instanceUrl + “/secur/frontdoor.jsp?sid=” + accessToken + “&retURL=”

let accountUrl: String = instanceUrl + “/apex/AccountMobile?id=” + accountItem!.salesforceId!

let request: NSURL = NSURL(string:authUrl + accountUrl)!

let urlRequest: NSURLRequest = NSURLRequest(URL: request)

return urlRequest

}


view rawDetailViewController.swift hosted with GitHub


Basically, all of the necessary information to authentication is stored in SFRestAPI’s single instance. You will find the instance URL such as https://ap.salesforce.com stored in the instanceUrl.


The AccessToken will be the session information. You will need to define sid in the parameter setting, and the destination URL as retUrl to deliver the authentication information to your Salesforce pages.


Pretty simple isn’t it?


Let’s Build WKWebView

I have a test application already made by Salesforce MobileSDK3.0 with Swift. So I’d like to use this app to embed WKWebView. This application is programmed to display a list of account information after it retrieves ten records. Detailed information will display when a record is tapped on the screen. Let's replace this detail page from the native page to a Visualforce page using WKWebView.


Below is the sample for Visualforce and Extension page:


<apex:page standardController=”Account” showHeader=”false” sidebar=”false” standardStylesheets=”false” extensions=”AccountMobileExtension”>

<head>

<meta name=”viewport” content=”width=device-width, initial-scale=1″ />

<link href=”//cdn.muicss.com/mui-0.0.1/css/mui.min.css” rel=”stylesheet” type=”text/css” />

<script src=”//cdn.muicss.com/mui-0.0.1/js/mui.min.js”></script>

</head>

<apex:form id=”form” styleClass=”form”>

<div class=”mui-container”>

<div class=”mui-panel”>

<apex:outputLabel value=”{!$ObjectType.Account.fields.Name.label}” for=”accountName”/>

<apex:inputField id=”accountName” styleClass=”mui-form-control” value=”{!account.name}” />

<apex:commandButton styleClass=”mui-btn mui-btn-primary mui-btn-raised” value=”Save” action=”{!doSave}” />

</div>

</div>

</apex:form>

</apex:page>


view rawAccountMobile.page hosted with GitHub


public with sharing class AccountMobileExtension {

private final ApexPages.StandardController sCon;

public AccountMobileExtension(ApexPages.StandardController stdController) {

this.sCon = stdController;

}

public PageReference doSave() {

PageReference pr = this.sCon.save();

if (pr != null) {

pr = new PageReference(‘completed://’);

}

return pr;

}

}


view rawAccountMobileExtension.cls hosted with GitHub


It is a simple page where you can display, create and edit the account information. I have used this opportunity to create the page with material design using Polymer, which is a trend right now. This MUI is a new framework with some expectations as a good CSS framework. Let us take a look at how to show this page with WKWebView and compare the difference with the original detail page.


import UIKit

import WebKit

class DetailViewController: BaseViewController, WKNavigationDelegate {

var accountItem: AccountModel?

var accountWebView: WKWebView?

init(nibName: String, accountItem: AccountModel) {

super.init(nibName: nibName, bundle: nil)

self.accountItem = accountItem

}

required override init(coder aDecoder: NSCoder) {

fatalError(“init(coder:) has not been implemented”)

}

override func didReceiveMemoryWarning() {

super.didReceiveMemoryWarning()

}

override func viewDidLoad() {

super.viewDidLoad()

setupDesign()

setupWebView()

}

override func viewWillAppear(animated: Bool) {

accountWebView!.loadRequest(createAccountRequest())

}

override func viewDidDisappear(animated: Bool) {

SVProgressHUD.dismiss()

}

func setupDesign() {

let refreshButton: UIBarButtonItem = UIBarButtonItem(barButtonSystemItem: .Refresh, target: self, action: “tapRefleshButton”)

self.navigationItem.rightBarButtonItem = refreshButton

}

func setupWebView() {

accountWebView = WKWebView()

self.view = accountWebView!

accountWebView!.navigationDelegate = self

}

func createAccountRequest() -> NSURLRequest {

let instanceUrl: String = SFRestAPI.sharedInstance().coordinator.credentials.instanceUrl.description

let accessToken: String = SFRestAPI.sharedInstance().coordinator.credentials.accessToken

let authUrl: String = instanceUrl + “/secur/frontdoor.jsp?sid=” + accessToken + “&retURL=”

let accountUrl: String = instanceUrl + “/apex/AccountMobile?id=” + accountItem!.salesforceId!

let request: NSURL = NSURL(string:authUrl + accountUrl)!

let urlRequest: NSURLRequest = NSURLRequest(URL: request)

return urlRequest

}

func webView(webView: WKWebView, didStartProvisionalNavigation navigation: WKNavigation!) {

SVProgressHUD.showWithStatus(“Loading”)

}

func webView(webView: WKWebView!, didFinishNavigation navigation: WKNavigation!) {

SVProgressHUD.dismiss()

self.title = accountWebView!.title

}

func webView(webView: WKWebView, didFailNavigation navigation: WKNavigation!, withError error: NSError) {

SVProgressHUD.dismiss()

}

func webView(webView: WKWebView, decidePolicyForNavigationAction navigationAction: WKNavigationAction, decisionHandler: (WKNavigationActionPolicy) -> Void) {

if (navigationAction.request.URL.absoluteString!.hasPrefix(“completed://”)) {

self.navigationController!.popViewControllerAnimated(true)

}

decisionHandler(WKNavigationActionPolicy.Allow)

}

func tapRefleshButton() {

accountWebView!.reload()

}

}


view rawDetailViewController.swift hosted with GitHub


I have set the WKWebView property on the 7th line. On the 47th line, I have drawn WKWebView on the view of setupWebView() and set its delegate. Lines from 66 to 88, I have written WKWebView delegate methods.


So, the below commands have each purpose.


didStartProvisionalNavigation: a call method when WebView loading is initiated.


didFinishNavigation: a call method when WebView loading is complete.


didFailNavigation: a call method when WebView loading failed.


decidePolicyForNavigationAction: a call method when an action such as page transition occurs.


This time, I have created a feature to display a listing page (native page) when the detail page (WebView page) is saved after the Save button is clicked. Mechanically speaking, on the Apex side, I have written a page to transition to a page named “completed://” as a “PageReference” in the doSave() method of “AccountMobileExtension.cls”. Then, hook it with “decidePolicyForNavigationAction” of the native side and return to the previous page when an action with “completed://” prefix is triggered. A Task complete message will be sent from the WebView when the page is saved.


Let’s Use WKWebView

This is a demo movie of the application I made using WKWebView.


Even though there is some loading time, it has benefits as below:

  • The application can be used in Web, Android, and iOS

  • You do not have to consider validation on the native sideModel control as native will be easier because you only have to consider how to deliver the ID.

  • You can co-work the Apex, Visualforce development side and native development side

The above benefits are enough facts to conclude that it is efficient to build some sections of the pages with WebView.


Summary

When you speak of mobile, there are so many choices to select from. The OS could be iOS or Android. The devices could be a tablet, phablet, and a smartphone. Implementation would be native, hybrid or HTML5.


People have different thoughts on mobile applications. Some say, “HTML5 can never beat the performance of native! Praise the native!” or “Create your apps with HTML5 and JavaScript so that it can run on any device!” or “Web-based applications are going to rule the world!”..etc. I personally think what matters the most is to choose the best method which fits the gap. For example, to create a page that requires natives performance such as list view or a collection view and critically affect the UI/UX then it would be wise to use native. On the other hand, a page designed for data input, which typically is ok if the performance is degraded a little, can be displayed as WebView on a Web application. So, my advice would be to choose the best method to fulfill the purpose of the page.


There are many factors to consider when creating a web application: which part would be better to create in native, which area to display in WebView, authentic methods, offline requirements, hardware limitations and more.

Image courtesy of Kibsri at FreeDigitalPhotos.net
368 views0 comments

Recent Posts

See All

Comments

Couldn’t Load Comments
It looks like there was a technical problem. Try reconnecting or refreshing the page.
bottom of page