Hi, I was taught that:
- if my SwiftUI view changes data that are kept outside of the view, it should use
@Binding
- if my view keeps its state in some variables, the variables should be
@State
- if my view only receives data to present etc. then the fields should be normal fields (
let xyz: String
)
In my case I should use the last option because my view only presents the data, but my question/problem is how to pass the current, up to date data to the view's StateObject? Setting the values for the StateObject in onAppear
is not the proper way to solve the problem. For example:
// --- Main view ---
class MainViewModel: ObservableObject {
@Published var items = [Int]()
// ... logic
}
struct MainView: View {
@StateObject var mainViewModel = MainViewModel()
var body: some View {
// 1
MyView(items: mainViewModel.items)
}
}
// --- My view ---
struct MyView: View {
let items: [Int]
var body: some View {
// 2
MyViewSubview(items: items)
}
}
// --- My view's subview
class MyViewSubviewModel: ObservableObject {
@Published var itemsPresentation = ""
func prepareItemsForPresentation(items: [Int]) {
// ... logic
}
}
struct MyViewSubview: View {
let items: [Int]
@StateObject private var myViewSubviewModel = MyViewSubviewModel()
init(items: [Int]) {
self.items = items
}
var body: some View {
// 4
Text(myViewSubviewModel.itemsPresentation)
.onAppear {
// 3
myViewSubviewModel.prepareItemsForPresentation(items: items)
}
}
}
When mainViewModel.items
changes (1
) it will update and redraw MyView
(👍).
In MyView
, updated items
will go to MyViewSubview
(2
) and cause update and redraw (👍).
The problem is happening here in MyViewSubview
, we need to update the delivered data, the proper way is to do it in onAppear
(3
) BUT it will be called only once - when the view is presented the first time. The subsequent updates of items
and redraws of the MyViewSubview
will not call prepareItemsForPresentation
and so it will not call the login in MyViewSubviewModel
and it will not present the proper data (4
).
How to refresh/update @StateObject
whenever new data is passed through an initializer of a view??? onChange(of:perform:)
doesn't work, because items
is not a @State
variable (and can't be, because I shouldn't set @State
in an initializer).