現在製作 App,很多時候需要嵌入 WebView,如此能快速開發與更新。既然 App 與 Web 互動頻繁,那麼就需要來了解如何讓 WKWebView 與 Native 互通資料。

多年前我也有寫過 Objective C與Javascript的溝通,然而那時還能使用 UIWebView,現在已經被棄用,所以我們就直接使用 WKWebView 吧!
為了好 Demo 流程運作,需要準備一個檔案 test.html,放在 Xcode 專案底下。
test.html 檔案內容:
<html>
<head>
<title>JS Bridge Demo</title>
<meta charset="utf-8">
<script type="text/javascript">
function shareSuccess(token) {
print(token);
print("Hello, Happy World!");
var h1Elements = document.getElementsByTagName("h1");
for(var i = 0; i < h1Elements.length; i++) {
h1Elements[i].style.color = "#FF3456";
h1Elements[i].innerHTML = token;
}
}
function btnClick() {
try {
window.webkit.messageHandlers.shareAction.postMessage([{ "title": "分享", "content": "快樂測試", "url": "連結" }, "shareSuccess"])
} catch (err) {
alert(err)
}
}
</script>
</head>
<body>
<h1>JS Bridge Happy Demo</h1>
<p style="text-align: center;">
<button type="button" onclick="btnClick()" style="font-size: 77px;">Test JS</button>
</p>
</body>
</html>
如果是用 Chrome 開啟該檔案,會如此呈現:

接著來寫 iOS Native 程式碼:
/**
Theme: Interaction for Native and WebView
IDE: Xcode 13.3.1
Language: Objective C (iOS)
Date: 111/11/29
Author: HappyMan
Blog: https://cg2010studio.wordpress.com/
*/
#import "WebViewController.h"
#import <WebKit/WKWebView.h>
#import <WebKit/WKUserContentController.h>
#import <WebKit/WKScriptMessage.h>
#import <WebKit/WKWebViewConfiguration.h>
#import <WebKit/WKFrameInfo.h>
@interface WebViewController () <WKScriptMessageHandler>
{
WKWebView *wv;
}
@end
@implementation WebViewController
- (void)viewDidLoad {
[super viewDidLoad];
WKUserContentController *ucc = [[WKUserContentController alloc] init];
[ucc addScriptMessageHandler:self name:@"shareAction"];// JS 函式名稱
WKWebViewConfiguration *wvc = [[WKWebViewConfiguration alloc] init];
wvc.userContentController = ucc;
wv = [[WKWebView alloc] initWithFrame:UIScreen.mainScreen.bounds configuration:wvc];
[self.view addSubview:wv];
[self.view insertSubview:wv atIndex:0];
NSURL *url = [[NSBundle mainBundle] URLForResource:@"test" withExtension:@"html"];// HTML 檔
NSURLRequest *urlRequest = [NSURLRequest requestWithURL:url];
[wv loadRequest:urlRequest];
}
#pragma mark - WKScriptMessageHandler
// <name> 函式名稱,<messageBody> 參數
// window.webkit.messageHandlers.<name>.postMessage(<messageBody>)
-(void)userContentController:(WKUserContentController *)userContentController didReceiveScriptMessage:(WKScriptMessage *)message
{
NSLog(@"message.body: %@", message.body);
NSLog(@"message.name: %@", message.name);
NSLog(@"message.frameInfo.request: %@", message.frameInfo.request);
// 收到 JS 訊息
if ([message.name isEqualToString:@"shareAction"]) {// JS 函式名稱
NSDictionary *dict = ((NSArray *)message.body)[0];
NSLog(@"title: %@", dict[@"title"]);
NSLog(@"content: %@", dict[@"content"]);
NSLog(@"url: %@", dict[@"url"]);
// 回傳給 JS 結果
NSString *str = ((NSArray *)message.body)[1];// JS 函式名稱
NSString *scriptStr = [NSString stringWithFormat:@"%@(\"happy studio ^_^\")", str];
[wv evaluateJavaScript:scriptStr completionHandler:^(id _Nullable, NSError * _Nullable error) {
NSLog(@"error: %@", error);
}];
}
}
@end
我們期望的流程是:

點擊 Test JS,會有 Log:
message.body: (
{
content = "快樂測試";
title = "分享";
url = "連結";
},
shareSuccess
)
message.name: shareAction
message.frameInfo.request: <NSMutableURLRequest: 0x6000018cc520> { URL: file:///Users/happyboy/Library/Developer/CoreSimulator/Devices/TestWebView.app/test.html }
title: 分享
content: 快樂測試
url: 連結
error: (null)
需留意的地方:
window.webkit.messageHandlers.<name>.postMessage(<messageBody>)
- <name>:函式名稱
- <messageBody>:參數
Native 必須加入 WebView Message Handler:shareAction
於是大部分需求都能如此互通有無~😀
參考:
隨意留個言吧:)~