Raisen
Raisen

Reputation: 4495

Why the state variable doesn't change in certain parts of this Swift code?

I'm having a hard time to understand the code below. If you look at my comments, I'm trying to change a binding variable in different parts of the code, but it's only working in the WKWebView events functions in the Coordinator.

Why doesn't the variable change when I try to do so in other parts of the code?

import Cocoa
import SwiftUI
import WebKit
import PlaygroundSupport

struct InternalBrowser: NSViewRepresentable {
    var webView: WKWebView = WKWebView()
    @Binding var status: String
    func makeNSView(context: Context) -> WKWebView {
        self.webView.load(URLRequest(url: URL(string: "https://www.google.com")!))
        self.webView.navigationDelegate = context.coordinator

        // THIS WON'T CHANGE THE BINDING VARIABLE
        self.status = "makeNSView"
        return self.webView
    }
    func updateNSView(_ nsView: WKWebView, context: Context) {
    }
    func makeCoordinator() -> Coordinator {
        return Coordinator(parent: self)
    }
    
    class Coordinator: NSObject, WKNavigationDelegate {
        var parent: InternalBrowser
        init(parent: InternalBrowser) {
            self.parent = parent
            
            // THIS WON'T CHANGE THE BINDING VARIABLE
            self.parent.status = "Coordinator:: init"
        }
        func webView(_ webView: WKWebView, didStartProvisionalNavigation navigation: WKNavigation!) {
            // THIS WORKS
            self.parent.status = "LOADING"
        }
        func webView(_ webView: WKWebView, didFinish navigation: WKNavigation!) {
            // THIS WORKS
            self.parent.status = "LOADED"
        }
    }
}

struct Browser: View {
    @State var status = "STATUS"
    var body: some View {
        VStack {
            InternalBrowser(status: self.$status).frame(width: 400, height: 400)
            Text(self.status)
        }
    }
}

PlaygroundPage.current.setLiveView(Browser().frame(width: 500, height: 500))

Upvotes: 1

Views: 189

Answers (1)

Asperi
Asperi

Reputation: 257719

Why doesn't the variable change when I try to do so in other parts of the code?

@Binding affects corresponding @State which itself results in corresponding View rebuild, but(!) NSViewRepresentable.makeNSView and Coordinator.init are called during(!) View rebuild... so, changing state during view rebuild cause new view rebuild, which comes to new change of state... cycle. Such situations are handled by SwiftUI rendering engine and ... such state changes are ignored. This is what you see.

Upvotes: 1

Related Questions