admin管理员组文章数量:1399466
I use the new @Observable
macro for my ViewModel. The class looks like this
@Observable
class TransitiveModel {
private var strings: [String] = []
func getStrings() -> [String] {
return strings
}
func addString(_ item: String) {
strings.append(item)
}
}
This is a simplification of my actual class, but it serves as a minimum example. You could argue that in this example a getStrings() method might not be needed, however in my actual class I pass parameters and do some calculations before, so please pretend it is important.
I have a simple view
private var model = TransitiveModel()
var body: some View {
VStack {
ForEach(model.getStrings(), id: \.self) { item in
Text(item)
}
Button("Add Item") {
model.addString("Hello")
}
}
}
When I Press the Button, I expected nothing to change visually, because I am not directly subscribed to the strings
-array in the View. However I observed that the View will update anyway, most probably because getStrings()
relies on the array, which has been changed. I don't want to rely on this behaviour unless I am sure it is correct and not a glitch or something like that.
Am I right to think that the change in strings
will trigger the function to be called again? And is there some kind of documentation that will help me in understanding it?
I use the new @Observable
macro for my ViewModel. The class looks like this
@Observable
class TransitiveModel {
private var strings: [String] = []
func getStrings() -> [String] {
return strings
}
func addString(_ item: String) {
strings.append(item)
}
}
This is a simplification of my actual class, but it serves as a minimum example. You could argue that in this example a getStrings() method might not be needed, however in my actual class I pass parameters and do some calculations before, so please pretend it is important.
I have a simple view
private var model = TransitiveModel()
var body: some View {
VStack {
ForEach(model.getStrings(), id: \.self) { item in
Text(item)
}
Button("Add Item") {
model.addString("Hello")
}
}
}
When I Press the Button, I expected nothing to change visually, because I am not directly subscribed to the strings
-array in the View. However I observed that the View will update anyway, most probably because getStrings()
relies on the array, which has been changed. I don't want to rely on this behaviour unless I am sure it is correct and not a glitch or something like that.
Am I right to think that the change in strings
will trigger the function to be called again? And is there some kind of documentation that will help me in understanding it?
1 Answer
Reset to default 1SwiftUI can still find dependencies of views even if you do not "directly" access an @Observable
property. As long as the getter/setter of an @Observable
property is called during the execution of View.body
, that property will be found as a dependency of that view.
This is because the @Observable
macro generates extra code in the getter/setter of those properties to register accesses/mutations of those properties into an observation registrar, which SwiftUI then uses to track dependencies.
This generated code doesn't look at the call stack and goes "this is not directly called from View.body
so I will not register this access". After all, Observation is not supposed to be strongly coupled with SwiftUI. And why would it do that extra work when the opposite is typically more desirable?
For documentation, off the top of my head I can think of the 2 WWDC videos Discover Observation in SwiftUI where they explain how Observation works, and Demystifying SwiftUI, where they talk about what a "dependency" of a view is.
As for the question in the comments:
Will this still be the case if there was no button but the change of strings was caused somewhere else?
Yes. Since the getter of strings
is called in body
, the SwiftUI view will update whenever the setter of strings
is called, from anywhere.
If you don't want the view to change visually when models.strings
change for some reason, you can add an extra @State
to separately store the strings. This @State
will be independent of model.strings
.
// model should be a @State regardless
@State private var model = TransitiveModel()
// add an extra @State here so that the view does not depend on model.strings
@State private var strings = [String]()
var body: some View {
VStack {
ForEach(strings, id: \.self) { item in
Text(item)
}
Button("Add Item") {
model.addString("Hello")
}
}
.onAppear {
// set strings to model.strings only initially
strings = model.getStrings()
}
}
本文标签: swiftuiPropagation of published changes in ViewsStack Overflow
版权声明:本文标题:swiftui - Propagation of published changes in Views - Stack Overflow 内容由网友自发贡献,该文观点仅代表作者本人, 转载请联系作者并注明出处:http://www.betaflare.com/web/1744220758a2595853.html, 本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌抄袭侵权/违法违规的内容,一经查实,本站将立刻删除。
ForEach(model.strings)
. So your question leads me to think my previous assumption was wrong. Will this still be the case if there was no button but the change ofstrings
was caused somewhere else? – Meyssam Commented Mar 25 at 1:54