こんにちは。ハコベルカーゴの開発を担当している貞元です。
ハコベルカーゴでは、ドライバー向けのiOS・Androidアプリがあり、どちらも主にWebViewを使用しています。 iOSアプリではUIWebViewを使用していたのですが、こちらは非推奨となり更新できなくなるためため、WKWebViewへ移行した内容を紹介します。 2020年11月時点では、UIWebViewを使用したアップデート期限は2020年末以降に延長されているため、正式な期限はAppleのニュースをご確認ください。 なお、iOSアプリ開発の経験は豊富ではないため、定番と異なる点などあるかもしれません。ご了承ください。
環境
- Xcode 12.0.1
- Swift 4
対応内容
1. WebKit.frameworkを追加
WKWebViewはWebKit.frameworkに含まれているため、WebKit.frameworkを追加します。
対象のTARGETSを選択し、「General」→「Frameworks, Libraries, and Embedded Content」の「+」をクリックし、「WebKit.framework」を追加

2. StoryBoardのUIWebViewをWKWebKitへ置き換え
既存のUIWebViewはStoryBoardに配置し使用していました。 そのため、WKWebViewも同じくStoryBoardへ配置し使用します。 なお、WKWebViewをiOS8から使用できますが、StoryBoardを使用する場合はiOS11以上でないとbuildできないため、iOS Development TargetをiOS 11.0以上へ変更する必要があります。
UIWebViewをWKWebViewへ置き換え

NOTE: dataDetectorTypesについて
WKWebViewにはUIWebViewと同様にdataDetectorTypesが存在します。
ただ、StoryBoardを使用した場合はコード上で変更しても適用されないため、こちらで設定が必要です。

3. Delegateを書き換え
WKWebViewには、WKUIDelegate, WKUIDelegateの2種類のDelegateが存在します。 UIWebViewのUIWebViewDelegateはWKNavigationDelegateへ、JavaScriptでalert, confirm, promptを使用している場合はWKUIDelegateが必要です。
WebKitをインポート
+ import WebKit
Delegateを書き換え
- class WebViewController: UIViewController, UITextViewDelegate, UIWebViewDelegate {
+ class WebViewController: UIViewController, UITextViewDelegate, WKNavigationDelegate, WKUIDelegate {
- @IBOutlet weak var webview: UIWebView!
+ @IBOutlet weak var webview: WKWebView!
override func viewDidLoad() {
super.viewDidLoad()
- webview.delegate = self
+ webview.navigationDelegate = self
+ webview.uiDelegate = self
UIWebViewDelegate→WKUIDelegate
- func webView(_ webView: UIWebView, shouldStartLoadWith request: URLRequest, navigationType: UIWebViewNavigationType) -> Bool{
+ func webView(_ webView: WKWebView, decidePolicyFor navigationAction: WKNavigationAction, decisionHandler: @escaping (WKNavigationActionPolicy) -> Void) {
- func webViewDidStartLoad(_ webView: UIWebView){
+ func webView(_ webView: WKWebView, didStartProvisionalNavigation navigation: WKNavigation!) {
- func webView(_ webView: UIWebView, didFailLoadWithError error: Error){
+ func webView(_ webView: WKWebView, didFailProvisionalNavigation navigation: WKNavigation!, withError error: Error) {
- func webViewDidFinishLoad(_ webView: UIWebView){
+ func webView(_ webView: WKWebView, didFinish navigation: WKNavigation!) {
NOTE: カスタムURLスキームについて
telやmail、その他カスタムURLスキームはそのままでは動作しません。 そのため、decidePolicyForにて処理する必要があります。
func webView(_ webView: WKWebView, decidePolicyFor navigationAction: WKNavigationAction, decisionHandler: @escaping (WKNavigationActionPolicy) -> Void) {
let request = navigationAction.request
let url = navigationAction.request.url
if url!.absoluteString.hasPrefix("http://") || url!.absoluteString.hasPrefix("https://") {
switch navigationAction.navigationType {
case .linkActivated:
if navigationAction.targetFrame == nil || !navigationAction.targetFrame!.isMainFrame {
UIApplication.shared.open(url!, options: [:], completionHandler: nil)
decisionHandler(.cancel)
return
}
case .backForward:
break
case .formResubmitted:
break
case .formSubmitted:
break
case .other:
break
case .reload:
break
}
} else {
if url!.absoluteString.range(of: "//itunes.apple.com/") != nil {
if UIApplication.shared.responds(to: #selector(UIApplication.open(_:options:completionHandler:))) {
UIApplication.shared.open(url!, options: [UIApplicationOpenURLOptionUniversalLinksOnly:false], completionHandler: { (finished: Bool) in
})
} else {
UIApplication.shared.open(url!, options: [:], completionHandler: nil)
}
} else {
if UIApplication.shared.canOpenURL(url!) {
UIApplication.shared.open(url!, options: [:], completionHandler: nil)
decisionHandler(.cancel)
return
}
}
decisionHandler(.cancel)
return
}
decisionHandler(.allow)
}
WKUIDelegate
/**
JavaScriptのalertを表示
*/
func webView(_ webView: WKWebView, runJavaScriptAlertPanelWithMessage message: String, initiatedByFrame frame: WKFrameInfo, completionHandler: @escaping () -> Void) {
let alertController = UIAlertController(title: "", message: message, preferredStyle: .alert)
let otherAction = UIAlertAction(title: "OK", style: .default) {
action in completionHandler()
}
alertController.addAction(otherAction)
present(alertController, animated: true, completion: nil)
}
/**
JavaScriptのconfirmを表示
*/
func webView(_ webView: WKWebView, runJavaScriptConfirmPanelWithMessage message: String, initiatedByFrame frame: WKFrameInfo, completionHandler: @escaping (Bool) -> Void) {
let alertController = UIAlertController(title: "", message: message, preferredStyle: .alert)
let cancelAction = UIAlertAction(title: "Cancel", style: .cancel) {
action in completionHandler(false)
}
let okAction = UIAlertAction(title: "OK", style: .default) {
action in completionHandler(true)
}
alertController.addAction(cancelAction)
alertController.addAction(okAction)
present(alertController, animated: true, completion: nil)
}
/**
JavaScriptのpromptを表示
*/
func webView(_ webView: WKWebView, runJavaScriptTextInputPanelWithPrompt prompt: String, defaultText: String?, initiatedByFrame frame: WKFrameInfo, completionHandler: @escaping (String?) -> Void) {
let alertController = UIAlertController(title: "", message: prompt, preferredStyle: .alert)
let okHandler: () -> Void = {
if let textField = alertController.textFields?.first {
completionHandler(textField.text)
} else {
completionHandler("")
}
}
let cancelAction = UIAlertAction(title: "Cancel", style: .cancel) {
action in completionHandler(nil)
}
let okAction = UIAlertAction(title: "OK", style: .default) {
action in okHandler()
}
alertController.addTextField { $0.text = defaultText }
alertController.addAction(cancelAction)
alertController.addAction(okAction)
present(alertController, animated: true, completion: nil)
}
4. JavaScript呼び出しの変更
UIWebViewにてJavaScriptを呼び出しにstringByEvaluatingJavaScriptを使用していました。 WKWebViewではevaluateJavaScriptを使用します。 こちらは非同期の実行となるため、挙動が変わらないように同期実行用のメソッドを用意し、そちらへ置き換えを行いました。
func evaluateJavaScriptSync(webview:WKWebView, script:String) -> Any? {
var syncResult:Any? = ""
var jsCompleted = false
webview.evaluateJavaScript(script) { (result, error) in
syncResult = result
jsCompleted = true
}
while !jsCompleted { RunLoop.current.run(mode: .defaultRunLoopMode, before: Date() + 0.1) }
return syncResult
}
5. Cookie参照の変更
UIWebViewではCookieの参照にHTTPCookieStorage.sharedを使用していました。 しかし、WKWevViewではHTTPCookieStorage.sharedへ反映されず参照できないため、WKWevViewから参照するように変更しています。
webview.configuration.websiteDataStore.httpCookieStore.getAllCookies() {(cookies) in
for cookie in cookies {
// ここでCookieを参照
}
}
まとめ
WKWebViewへの変更期限が近づいています。 iOSアプリ開発の経験は豊富ではないため、調査・変更・テストを繰り返して対応しました。 この記事がWKWebViewの移行の手助けとなると幸いです。