Инициализировать член в структуре представления

Dec 17 2020

Я хочу иметь WKWebView с обработкой JavaScript в SwiftUI. Из Swift Variables Initialization я делаю следующее: (Я используюhttps://github.com/kylehickinson/SwiftUI-WebViewчтобы предоставить оболочку WKWebViewв SwiftUI, которая также добавляет важные ограничения макета.)

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

Но затем я узнал от члена экземпляра, который нельзя использовать с типом, что это не работает, потому что замыкание не имеет ссылки на себя. Мне нужно, чтобы у WKWebView был выделенный объект конфигурации, поэтому я не могу просто использовать другой конструктор. Мне нужна ссылка на это сделать evaluateJavaScript.

Как заставить это работать?

РЕДАКТИРОВАТЬ 1 : Добавьте bodyи укажите структуру, используемую для упаковки WKWebView.

РЕДАКТИРОВАТЬ 2 : добавлен код, поясняющий, что мне нужна двусторонняя связь с WKWebViewсобственным приложением через WKScriptMessageHandler(чтобы получать уведомления, когда HTML-документ будет готов) и из собственного приложения WKWebViewчерез evaluateJavaScript(для передачи данных после того, как HTML-документ готов).

Ответы

1 Asperi Dec 17 2020 at 15:42

Одно из возможных решений - настроить WKWebView в 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

Просто сделайте myWebView lazyпеременной, это гарантирует, что jsHandler инициализируется до 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)
}()