swiftui - Propagation of published changes in Views - Stack Overflow

I use the new @Observable macro for my ViewModel. The class looks like this@Observableclass Transitiv

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?

Share Improve this question asked Mar 25 at 1:49 MeyssamMeyssam 7486 silver badges17 bronze badges 2
  • When do you expect the view to change then, if not when you press the button? – Sweeper Commented Mar 25 at 1:51
  • @Sweeper Only if I had used 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 of strings was caused somewhere else? – Meyssam Commented Mar 25 at 1:54
Add a comment  | 

1 Answer 1

Reset to default 1

SwiftUI 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()
    }
}

发布者:admin,转转请注明出处:http://www.yc00.com/questions/1744220758a4563769.html

相关推荐

  • swiftui - Propagation of published changes in Views - Stack Overflow

    I use the new @Observable macro for my ViewModel. The class looks like this@Observableclass Transitiv

    8天前
    20

发表回复

评论列表(0条)

  • 暂无评论

联系我们

400-800-8888

在线咨询: QQ交谈

邮件:admin@example.com

工作时间:周一至周五,9:30-18:30,节假日休息

关注微信