Swift・iOS

Swiftを中心に学んだことを記録に残すブログです。技術に関係ない記事もたまに書いています。

【SwiftUI】WKWebViewを使えるようにする

 

はじめに

UIViewRepresentableによって、SwiftUIのアプリでもWKWebViewを使えるようにしてみました。

 

※2021/03/23 追記

SFSafariViewControllerをSwiftUIで使用する方法について、以下記事にしました。

【SwiftUI】SFSafariViewControllerを使えるようにする - Swift・iOS

 

※2021/04/07 追記

WKWebView用に戻るボタンや進むボタンを実装したい場合は以下の記事を参考にしてください。

【SwiftUI】WKWebViewの戻る/進む/再読み込みの機能を実装する - Swift・iOS

 

開発環境

macOS Catalina 10.15.7

Xcode 12.2

・Swift 5.3.1 

 

実装部分

WebView.swift

import SwiftUI
import WebKit

struct WebView: UIViewRepresentable {
    var urlString: String
    
    class Coordinator: NSObject, WKUIDelegate, WKNavigationDelegate {
        var parent: WebView
        
        init(_ parent: WebView) {
            self.parent = parent
        }
        
        // "target="_blank""が設定されたリンクも開けるようにする
        func webView(_ webView: WKWebView, createWebViewWith configuration: WKWebViewConfiguration, for navigationAction: WKNavigationAction, windowFeatures: WKWindowFeatures) -> WKWebView? {
            if navigationAction.targetFrame == nil {
                webView.load(navigationAction.request)
            }
            return nil
        }
        
        // URLごとに処理を制御する
        func webView(_ webView: WKWebView, decidePolicyFor navigationAction: WKNavigationAction, decisionHandler: (WKNavigationActionPolicy) -> Void) {
            if let url = navigationAction.request.url?.absoluteString {
                if (url.hasPrefix("https://apps.apple.com/")) {
                    guard let appStoreLink = URL(string: url) else {
                        return
                    }
                    UIApplication.shared.open(appStoreLink, options: [:], completionHandler: { (succes) in
                    })
                    decisionHandler(WKNavigationActionPolicy.cancel)
                } else if (url.hasPrefix("http")) {
                    decisionHandler(WKNavigationActionPolicy.allow)
                } else {
                    decisionHandler(WKNavigationActionPolicy.cancel)
                }
            }
        }
    }
    
    func makeCoordinator() -> Coordinator {
        Coordinator(self)
    }
    
    func makeUIView(context: Context) -> WKWebView {
        let webView = WKWebView()
        
        return webView
    }
    
    func updateUIView(_ webView: WKWebView, context: Context) {
        // makeCoordinatorで生成したCoordinatorクラスのインスタンスを指定
        webView.uiDelegate = context.coordinator
        webView.navigationDelegate = context.coordinator
        
        // スワイプで画面遷移できるようにする
        webView.allowsBackForwardNavigationGestures = true
        
        let url = URL(string: urlString)!
        let request = URLRequest(url: url)
        webView.load(request)
    }
}

 

ContentView.swift(WebViewを呼び出す側の実装例)

import SwiftUI

struct ContentView: View {
    var body: some View {
        WebView(urlString: "{表示したいURL}")
    }
}

struct ContentView_Previews: PreviewProvider {
    static var previews: some View {
        ContentView()
    }
}

 

おわりに

本記事の通りに実装してもページが表示されない場合は、ATSの設定などを確認してみてください。

また、インジケータの表示やJavaScriptの一部の表示に関しては、可能ではあるものの複雑な実装になってしまったり、情報が少なかったりしたので今回はスキップして今後の課題としました・・・参考リンクは以下に貼っておきます。

 

参考

https://developer.apple.com/documentation/swiftui/uiviewrepresentable

https://developer.apple.com/tutorials/swiftui/interfacing-with-uikit

https://developer.apple.com/documentation/webkit/wkwebview

SwiftUIでWebViewを使う - Qiita

Using coordinators to manage SwiftUI view controllers - a free Hacking with iOS: SwiftUI Edition tutorial

【Swift】WKWebViewでリンク先が開けない場合の対処法(ATS/target=”_blank”) - Swift・iOS

【Swift】WKWebViewで進む/戻る/更新(リロード)機能を実装する - Swift・iOS

SwiftUI How can I add an activity indicator in WKWebView? - Stack Overflow

swift - Implement Javascript Alert and Confirm on WKUIDelegate SwiftUI? - Stack Overflow