Search code examples
swiftuiswift3wkwebview

SwiftUI - WebView


fairly new to SwiftUI, so thank you in advance. Inside the init of the Coordinator we pass the WebView to let the Coordinator know who the parent is, but the WebView being a struct, wouldn't Coordinator be having a new copy of the WebView instead of the original?

If so, why is this approach commonly mentioned? Would it be better to create a new class(ObservableObject) and let WebView create an object of the class and pass the class into the Coordinator?

   struct WebView: UIViewRepresentable {
    var url: URL

    func makeCoordinator() -> WebView.Coordinator {
        Coordinator(self)
    }

    func makeUIView(context: Context) -> WKWebView {
        let view = WKWebView()
        view.navigationDelegate = context.coordinator
        view.load(URLRequest(url: url))
        return view
    }

    func updateUIView(_ uiView: WKWebView, context: Context) {}

    class Coordinator: NSObject, WKNavigationDelegate {
        var parent: WebView

        init(_ parent: WebView) {
            self.parent = parent
        }
    }
}

Solution

  • Yes, Coordinator holds a copy of original WebView, you understand it right. But its purpose is not what you think.

    First, you need to understand that WebView doesn't hold WKWebView, its purpose is giving information how to update WKWebView to fit your intent, and Coordinator is helping it by keeping a copy of WebView

    How is it helping ? Usually to track old version (latest version, or first version) of WebView so you can get its infomations if needed

    func updateUIView(_ uiView: WKWebView, context: Context) {
        let lastURL = context.coordinator.parent.url
        print(lastURL)
        context.coordinator.parent = self // (*)
    }
    /// be cause of (*), next time lastURL will be the previous URL
    /// without (*), lastURL always be the first URL you used to init WKWebView
    

    Here's another small example you can see on Preview how UIViewRepresentable works

    struct LabelView: UIViewRepresentable {
        var text: String
        
        func makeUIView(context: Context) -> UILabel {
            let label = UILabel(frame: .init(x: 0, y: 0, width: 100, height: 100))
            label.text = text
            return label
        }
        
        func updateUIView(_ uiView: UILabel, context: Context) {
            let lastLabel = context.coordinator.parent.text
            uiView.text = "\(lastLabel) -> \(text)"
            context.coordinator.parent = self
        }
        
        func makeCoordinator() -> Coordinator {
            Coordinator(self)
        }
        
        class Coordinator: NSObject, WKNavigationDelegate {
            var parent: LabelView
    
            init(_ parent: LabelView) {
                self.parent = parent
            }
        }
    }
    struct TestLabelView: View {
        @State private var num = 0
        var body: some View {
            Button { num += 1 } label: {
                LabelView(text: "\(num)")
            }
        }
    }