Initialiser le membre dans la structure de vue

Dec 17 2020

Je souhaite avoir un WKWebView avec gestion JavaScript dans SwiftUI. À partir de l' initialisation des variables Swift , je fais ce qui suit: (J'utilisehttps://github.com/kylehickinson/SwiftUI-WebViewpour fournir un wrapper pour WKWebViewSwiftUI qui ajoute également de précieuses contraintes de mise en page.)

struct ContentView: View {

    var body: some View {
        WebView(webView: myWebView)
        .onAppear {
            let url = Bundle.main.url(forResource: "index", withExtension: "html")!
            myWebView.loadFileURL(url, allowingReadAccessTo: url)
            let request = URLRequest(url: url)
            myWebView.load(request)
        }
    }

    class JSHandler : NSObject, WKScriptMessageHandler {
        var contentView: ContentView?

        func userContentController(_ userContentController: WKUserContentController, didReceive message: WKScriptMessage) {
            print("documentReady")
            contentView!.myWebView.evaluateJavaScript("<Long Script to Transfer Data>") { (result, error) in
            }
        }
    }

    let myJSHandler = JSHandler()

    var myWebView: WKWebView = {
        let config = WKWebViewConfiguration()
        let controller = WKUserContentController()
        controller.add(myJSHandler , name: "documentReady")
        config.userContentController = controller
        return WKWebView(frame: .zero, configuration: config)
    }()
}

Mais ensuite, j'ai appris du membre d'instance ne peut pas être utilisé sur le type que cela ne fonctionne pas car la fermeture n'a pas de référence à soi. J'ai besoin que WKWebView ait un objet de configuration dédié, donc je ne peux pas simplement utiliser l'autre constructeur. J'ai besoin d'une référence pour le faire evaluateJavaScript.

Comment faire fonctionner cela?

EDIT 1 : Ajoutez bodyet mentionnez le framework utilisé pour encapsuler WKWebView.

EDIT 2 : Ajout d'un code pour clarifier que j'ai besoin de communications bidirectionnelles depuis l' WKWebViewapplication native via WKScriptMessageHandler(pour être averti lorsque le document HTML est prêt) et depuis l'application native vers WKWebViewvia evaluateJavaScript(pour transférer des données lorsque le document HTML est prêt).

Réponses

1 Asperi Dec 17 2020 at 15:42

Une solution possible consiste à configurer WKWebView dans init

struct ContentView: View {
    private var myWebView: WKWebView

    init() {
        let config = WKWebViewConfiguration()
        let controller = WKUserContentController()
        controller.add(myJSHandler , name: "documentReady")
        config.userContentController = controller
        myWebView = WKWebView(frame: .zero, configuration: config)
    }

    var body: some View {
        WebView(webView: myWebView)
        .onAppear {
            let url = Bundle.main.url(forResource: "index", withExtension: "html")!
            myWebView.loadFileURL(url, allowingReadAccessTo: url)
            let request = URLRequest(url: url)
            myWebView.load(request)
        }
    }

    class JSHandler : NSObject, WKScriptMessageHandler {

        func userContentController(_ userContentController: WKUserContentController, didReceive message: WKScriptMessage) {
            print("documentReady")
            message.webView?.evaluateJavaScript("<Long Script to Transfer Data>") { (result, error) in
            }
        }
    }

    let myJSHandler = JSHandler()

}
Arun Dec 17 2020 at 11:48

Faites simplement de myWebView une lazyvariable, cela garantit que le jsHandler est initialisé avant WebView.

lazy var myWebView: WKWebView = {
    let config = WKWebViewConfiguration()
    let controller = WKUserContentController()
    controller.add(myJSHandler , name: "documentReady")
    config.userContentController = controller
    return WKWebView(frame: .zero, configuration: config)
}()