IOS: WKWebView interacts with JS

foreword

With the rapid development of the mobile Internet, the boundaries between Web development and native application development are gradually blurring. On the one hand, web technology is constantly improving, and many functions that could only be realized in native applications before, such as offline storage, device API access, etc., can now be realized through web technology. On the other hand, native application development has also begun to adopt Web technology to improve development efficiency and achieve cross-platform capabilities. This trend of combining web development with native development has brought new possibilities for mobile application development.

Alternation between UIWebView and WKWebView

Originally, the way to load web pages on the iOS platform was through the use of UIWebView. However, with the development of iOS platform technology, the limitations of UIWebView gradually emerged. First of all, the performance problem of UIWebView is prominent. It needs to consume a lot of memory and CPU resources when loading web pages, resulting in low application efficiency. Secondly, UIWebView does not support concurrent loading of multiple web pages, which seems powerless in multitasking scenarios. Finally, the API design of UIWebView is relatively old and does not support new Web technology standards.

In order to solve these problems, Apple introduced a new component in iOS 8 - WKWebView, which is used to replace UIWebView. WKWebView uses the Nitro JavaScript engine in its design, which is an efficient engine that can greatly improve the execution efficiency of JavaScript. At the same time, WKWebView also supports concurrent loading of multiple web pages, and adopts the latest web technology standards, enabling developers to use the latest web technologies.

The principle of interaction between WKWebView and JavaScript

The interaction between WKWebView and JavaScript can basically be divided into two directions: one is that JavaScript calls Swift code, and the other is that Swift code calls JavaScript code.

For JavaScript calling Swift code, WKWebView provides WKScriptMessageHandlera protocol, and developers can receive messages sent by userContentController(_:didReceive:)JavaScript code through methods by implementing the method of the protocol.postMessage

For Swift code calling JavaScript code, WKWebView provides evaluateJavaScript(_:completionHandler:)a method through which developers can execute JavaScript code and obtain the execution result through completionHandler.

Next, use an example to use WKWebView to interact with JavaScript

1. Create and configure WKWebView

First, we need to create a Swift project and ViewControllerimport the WebKit module in the default build to support the use of WKWebView.

import WebKit

Let ViewController implement the WKNavigationDelegate and WKScriptMessageHandler protocols. The WKNavigationDelegate protocol is used to handle web page navigation events, and the WKScriptMessageHandler protocol is used to receive messages sent by JavaScript. Also, declare a variable webView of type WKWebView.

class ViewController: UIViewController, WKNavigationDelegate, WKScriptMessageHandler { 
     var webView: WKWebView!;
     ...
}

In loadViewthe method, we first create an WKUserContentControllerinstance and add(self, name: "nativeCallback")add a message handler called "nativeCallback" through the method. Then, we create an WKWebViewConfigurationinstance and userContentControllerset its properties to the content controller we just created. Finally, we use this configuration object to create an instance of WKWebView and assign it to the view controller's view property.

override func loadView() {
    let contentController = WKUserContentController();
    contentController.add(self, name: "nativeCallback")
    
    let config = WKWebViewConfiguration();
    config.userContentController = contentController;
    webView = WKWebView(frame: .zero, configuration: config);
    webView.navigationDelegate = self;
    view = webView;
}

Note that since no Storyboard or Xib file is used to define the layout of the view in Interface Builder, the loadViewmethod needs to be rewritten. In this method, an instance of UIView (or its subclass) needs to be created and assigned to the view controller's viewAttributes.

viewDidLoad方法中,我们创建了一个URL请求,并通过webView.load(myRequest)方法加载这个请求,用于加载本地服务器上的index.html文件

override func viewDidLoad() {
    super.viewDidLoad()
    let myURL = URL(string: "http://127.0.0.1:8080")
    let myRequest = URLRequest(url: myURL!)
    webView.load(myRequest)
}

2. 处理JavaScript调用

userContentController(_:didReceive:)方法中,我们处理了JavaScript代码发送的消息。当JavaScript代码通过window.webkit.messageHandlers.nativeCallback.postMessage(message)调用"nativeCallback"的message handler时,这个方法就会被调用。我们在这个方法中检查消息的内容,并显示一个alert对话框。

func userContentController(_ userContentController: WKUserContentController, didReceive message: WKScriptMessage) {
    if message.name == "nativeCallback" {
        if let msg = message.body as? String {
            showAlert(message: msg)
        }
    }
}

func showAlert(message: String) {
    let alertController = UIAlertController(title: "提示", message: message, preferredStyle: .alert)
    let okAction = UIAlertAction(title: "确定", style: .default, handler: nil)
    alertController.addAction(okAction)
    present(alertController, animated: true, completion: nil)
}

3. 调用JavaScript方法

在WKNavigationDelegate的webView(_:didFinish:)方法中,我们调用了JavaScript的displayDate()方法来显示当前的日期。

swiftCopy code
func webView(_ webView: WKWebView, didFinish navigation: WKNavigation!) {
    webView.evaluateJavaScript("displayDate()") { (any, error) in
        if (error != nil) {
            print(error ?? "err")
        }
    }
}

上述代码中在WKWebView加载完index.html页面后,swift会调用index.html的js方法displayDate()方法来显示当前的日期。

4. Web页面

在index.html页面中,我们定义了两个JavaScript函数:displayDate()nativeCallback()displayDate()函数会将当前的日期显示在id为"date"的p元素中。nativeCallback()函数会通过webkit.messageHandlers.nativeCallback.postMessage('js调用ios')发送一个消息到原生应用。

<!DOCTYPE html>
<html>
<head>
    <meta charset="utf-8"/>
    <meta name="viewport" content="width=device-width,initial-scale=1,minimum-scale=1,maximum-scale=1,user-scalable=no"/>
    <title>wkwebview</title>
    <style type="text/css">
    	h1 {
	    	color: red;
	    	font-size: 1em;
		}
		body {
		    font-size: 2em;
		}
		button {
		    font-size: 0.5em;
		}
		p {
			font-size: 0.5em
		}
		#tip {
			color: blue;
		}
    </style>
</head>
<body>
    <h1>WKWebview和JS交互</h1>
    
    <div>
    	<p id="tip">ios调用js显示日期:</p>	
    	<p id="date">这是一个段落</p>	
    </div>
    <button type="button" onclick="nativeCallback()">js调用ios</button>
    <script type="text/javascript">
        function displayDate(){
            document.getElementById("date").innerHTML=Date();
        }

        function nativeCallback() {    
            try {
              //使用此方法,会报错,因此使用try-catch
              webkit.messageHandlers.nativeCallback.postMessage('js调用ios');
            } catch(error) {
              console.log('WKWebView post message error: ', error);
            }
        }
    </script>
</body>
</html>

通过上面4个步骤后,可以得到结果,当WKWebView加载页面成功后,对调用index.htmldisplayDate()·方法来显示时间,点击页面上的按钮会弹出提示弹窗

bsbqc-hjhul.gif

以上就是在IOS中使用WKWebViewJavaScript进行交互的基本步骤。这种交互方式为原生提供更大的灵活性,我们可以利用Web技术的便利性,同时保持原生应用的高性能和流畅性。

需要注意的是,虽然WKWebView提供了一套完整的API来和JavaScript进行交互,但我们仍然需要谨慎处理JavaScript发送的消息,防止潜在的安全问题。在实际开发中,我们应该尽量限制JavaScript能够调用的原生方法,并对传递的数据进行严格的验证和过滤。

同时,我们也需要注意WKWebView的性能问题。虽然WKWebView的性能大大优于UIWebView,但它仍然不如原生视图。因此,应该根据应用的具体要求和性能要求,合理选择使用WKWebView和原生视图。

Guess you like

Origin juejin.im/post/7232299098242105403