WebViewでJavaScriptとの通信で発生した問題と解決策を紹介【iOS】
iOSのWebViewを使用して、ネイティブアプリとWebコンテンツの統合を実現することは一般的ですが、JavaScriptとの通信に関してはいくつかの問題が発生することがあります。この記事では、iOSのWebViewでJavaScriptと通信する際によく起きる問題とそれらの問題を解決するための解決策について説明します。
WebView内でJavaScriptと通信する
WebViewをセットアップ
まず、WebViewをアプリ内に組み込みます。
//ViewController
import UIKit
import WebKit
class ViewController: UIViewController {
var webView: WKWebView!
override func loadView() {
let webConfiguration = WKWebViewConfiguration()
webView = WKWebView(frame: .zero, configuration: webConfiguration)
view = webView
}
override func viewDidLoad() {
super.viewDidLoad()
//ページを読み込む(例でgoogle)
if let url = URL(string: "https://www.google.com") {
let request = URLRequest(url: url)
webView.load(request)
}
}
}
JavaScriptとの通信を設定
override func loadView() {
let webConfiguration = WKWebViewConfiguration()
//JavaScriptからメッセージを受信するための設定
webConfiguration.userContentController.add(self, name: "callbackHandler")
webView = WKWebView(frame: .zero, configuration: webConfiguration)
view = webView
}
JavaScriptからのメッセージを受信
extension ViewController: WKScriptMessageHandler {
func userContentController(_ userContentController: WKUserContentController, didReceive message: WKScriptMessage) {
if message.name == "callbackHandler" {
//メッセージの処理
if let messageBody = message.body as? String {
print("JavaScriptからのメッセージ: \(messageBody)")
}
}
}
}
JavaScriptからiOSへメッセージを送信
// iOSにメッセージを送信
window.webkit.messageHandlers.callbackHandler.postMessage("iOSへのメッセージ");
ここまででJavaScriptからNativeを呼び出すことができます。ここからつまづいた部分と解決策を紹介いたします。
問題点1: window.webkit.messageHandlersがnullとなる(JS内)
JSからiOSへメッセージを送信するためのwindow.webkit.messageHandlersがnullになってしまいました。よく出てくる記事では、グローバルで
var WKWebkit = window.webkit;
のように定義して、使用したい場所で
WKWebkit.messageHandlers
とすると解決するらしいのですが、私の場合は解決しませんでした。
解決策は'limitsNavigationsToAppBoundDomains'をWKWebViewConfigurationに設定することでした。
https://webkit.org/blog/10882/app-bound-domains/
limitsNavigationsToAppBoundDomainsは、WKWebViewConfigurationのプロパティで、iOS 14以降で導入された新しいセキュリティ機能です。
セキュリティを向上させるため、JavaScriptからのメッセージングやデータの送信をアプリのドメイン内に制約することができます。
フラグがtrueに設定されていない限り、アプリ内のすべての WKWebView の一部の機能が無効になるようです。
override func loadView() {
let webConfiguration = WKWebViewConfiguration()
//JavaScriptからメッセージを受信するための設定
webConfiguration.userContentController.add(self, name: "callbackHandler")
//APIへのアクセスを許可するための設定
webConfiguration.limitsNavigationsToAppBoundDomains = true;
webView = WKWebView(frame: .zero, configuration: webConfiguration)
view = webView
}
問題点2: WebViewのデバッグが出来ない
Webインスペクタを使用してデバッグが出来ます。いままではWebViewを実行しているiOSデバイスでSafariを開き、以下の手順を行えばWebView内のJavaScriptコードを検証ができるはずでした。Safariを開いて、「開発」メニューを選択します。接続したデバイスの名前を選択し、WebViewを選択します。WebViewのコンテンツとJavaScriptコードにアクセスすることで、デバッグが行えます。しかし、デバイスは表示されても自分の見ているWebViewが表示されませんでした。これはiOS16.4以上で発生しているようで、インスペクタを有効にしなくてはいけないようです。
https://developer.apple.com/forums/thread/727049
https://webkit.org/blog/13936/enabling-the-inspection-of-web-content-in-apps/
override func loadView() {
let webConfiguration = WKWebViewConfiguration()
//JavaScriptからメッセージを受信するための設定
webConfiguration.userContentController.add(self, name: "callbackHandler")
//APIへのアクセスを許可するための設定
webConfiguration.limitsNavigationsToAppBoundDomains = true;
//iOS16.4以上でWebインスペクタを有効にするための設定
webConfiguration.isInspectable = true
webView = WKWebView(frame: .zero, configuration: webConfiguration)
view = webView
}
まとめ
今回はiOSでWebViewを表示する際に起きた問題について紹介させていただきました。バージョンごとに仕様が変わっていくので、不具合が起きたときはバージョンによって起きているのかを先に確認すると良いと思います。以上です。ありがとうございました。
プログラマー/K.D