Inicializar o membro na estrutura da vista

Dec 17 2020

Eu quero ter um WKWebView com manipulação de JavaScript no SwiftUI. Na inicialização de variáveis ​​Swift , estou fazendo o seguinte: (estou usandohttps://github.com/kylehickinson/SwiftUI-WebViewpara fornecer um wrapper para WKWebViewSwiftUI que também adiciona restrições de layout valiosas.)

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)
    }()
}

Mas então eu aprendi com o membro da instância não pode ser usado no tipo que isso não funciona porque o fechamento não tem referência a si mesmo. Preciso que o WKWebView tenha um objeto de configuração dedicado, portanto, não posso simplesmente usar o outro construtor. Eu preciso de uma referência para fazer evaluateJavaScript.

Como fazer isso funcionar?

EDIT 1 : Adicione o bodye mencione a estrutura usada para envolver WKWebView.

EDIT 2 : código adicionado para esclarecer que preciso de comunicações bidirecionais do WKWebViewaplicativo nativo via WKScriptMessageHandler(para ser notificado quando o documento HTML estiver pronto) e do aplicativo nativo para WKWebViewvia evaluateJavaScript(para transferir dados quando o documento HTML estiver pronto).

Respostas

1 Asperi Dec 17 2020 at 15:42

Uma solução possível é configurar WKWebView em 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

Apenas torne myWebView uma lazyvariável, isso garante que o jsHandler seja inicializado antes do 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)
}()