I'm currently working on a project to integrate some SwiftUI components into an existing AppKit application. The application makes extensive use of NSViewControllers
. I can easily bridge between AppKit and SwiftUI using a view model that conforms to ObservableObject
and is shared between the NSViewController
and the SwiftUI View
. But it's kind of tedious creating a view model for every view.
Is it "safe" and "acceptable" for the NSViewController
to "hold on" to the SwiftUI View that it creates and then access its @State
or @StateObject
properties?
The lifecycle of DetailsView
, a SwiftUI View, isn't clear to me when viewed through the lens of an NSViewController
. Consider the following:
import AppKit
import SwiftUI
struct DetailsView: View {
@State var details: String = ""
var body: some View {
Text(details)
}
}
final class ViewController: NSViewController {
private let detailsView: DetailsView
init() {
self.detailsView = DetailsView()
super.init(nibName: nil, bundle: nil)
}
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
override func viewDidLoad() {
view.addSubview(NSHostingView(rootView: detailsView))
}
func updateDetails(_ details: String) {
// Is this 'safe' and 'acceptable'?
self.detailsView.details = details
}
}
Is the view controller guaranteed to always be updating the correct @State
property or is there a chance that the view controller's reference to it somehow becomes stale because of a SwiftUI update?
Your best approach is to use a shared source of truth, such as an Observable
or a model object.
SwiftUI views are data driven, your View gets re-rendered whenever the data it depend on changes. You should declare state as private to prevent setting it in a memberwise initializer, which can conflict with the storage management that SwiftUI provides. When the value changes, SwiftUI updates the parts of the view hierarchy that depend on the value.