1

I'm developing my SwiftUI test app and having some problem with @Published and @ObservedObject.

Here's my Code(FYI I deleted some lines of code and added some dummy values for you to understand easily)

import SwiftUI

struct S0: Identifiable {
    var id = UUID()
    var arr: [S1] = []
}
struct S1: Identifiable {
    var i: Int
    var id = UUID()
}

class Model: ObservableObject {
    @Published var items: [S0] = [S0(arr: [S1(i: 100)])]
}

struct ContentView: View {
    @ObservedObject var testModel = Model()
    @State var flag = false
    var body: some View {
        VStack {
            List {
                ForEach (self.testModel.items){ item in
                    Text("\(item.arr.last?.i.description ?? "nil")")
                }
            }

            Button(action: {
                self.flag.toggle()
            }) {
                Text("Add")
            }.sheet(isPresented: $flag) {
                ModalView(flag: self.$flag).environmentObject(self.testModel)
            }
        }
    }
}

struct ModalView: View {
    @Binding var flag: Bool
    @EnvironmentObject var testModel: Model
    var body: some View {
        Button(action: {
            if let lastItem = self.testModel.items.last {
                var newItem = lastItem
                newItem.arr[0].i += 100
                self.testModel.items.append(newItem)
            }
            self.flag.toggle()
        }) {
            Text("add")
        }
    }
}

struct ContentView_Previews: PreviewProvider {
    static var previews: some View {
        ContentView()
    }
}


The problem is that when I plus add button, different numbers(+100 to previous number) should be displayed but it is displaying same numbers(100)

As you can see from my code, @Published used struct array which also contains struct array as a child. If I don't have struct array as child, it works fine.

Can anyone please suggest me good solution for this?

Thanks.

9
  • I've just tested your model and all works fine (Xcode 11.2 / iOS 13.2). Would you show code how you use it and what is not updated? Commented Mar 15, 2020 at 19:12
  • @Asperi I updated my question, Can you please check and help me? Commented Mar 15, 2020 at 20:49
  • try to call self.scoreViewModel.objectWillChange.send() before your changes in your modal view Commented Mar 15, 2020 at 21:37
  • 1
    what kind of problem did you experience? I also checked your model (as Asperi did) and it should work. you don't need to call objectWillChange.send() but it has nothing to do with structs ... let us know what is the trouble! Commented Mar 15, 2020 at 22:12
  • 1
    i tried to do 'almost everything', I am not able to reproduce any trouble. Are you sure, that what you are doing in the "Save" action is the right action to do? I think you could have bug in your business logic ... Commented Mar 15, 2020 at 23:47

1 Answer 1

1

I am almost sure, that your trouble is in your business logic and not related to SwiftUI or some unexpected behavior of Observable / Observed.

Let see the example, which mimics your code (nested array in model), which works.

import SwiftUI

struct S0 {
    var arr: [S1] = []
}
struct S1 {
    var i: Int
}

class Model: ObservableObject {
    @Published var s0arr: [S0] = [S0(arr: [S1(i: 100)])]
}

struct ContentView: View {
    @ObservedObject var m = Model()
    @State var flag = false
    var body: some View {
        VStack {
            Text("\(m.s0arr.last?.arr.count ?? 0)")
            Text("\(m.s0arr.last?.arr.last?.i.description ?? "nil")").sheet(isPresented: $flag) {
                M(flag: self.$flag).environmentObject(self.m)
            }.onTapGesture {
                self.flag.toggle()
            }
        }
    }
}

struct M: View {
    @Binding var flag: Bool
    @EnvironmentObject var m: Model
    var body: some View {
        Button(action: {
            let c = self.m.s0arr.count
            if c > 0 {
                if let i = self.m.s0arr[c - 1].arr.last?.i {
                    self.m.s0arr[c - 1].arr.append(S1(i: i + 100))
                }
            }
            self.flag.toggle()
        }) {
            Text("add")
        }
    }
}

struct ContentView_Previews: PreviewProvider {
    static var previews: some View {
        ContentView()
    }
}

and resulting behavior

enter image description here

Sign up to request clarification or add additional context in comments.

3 Comments

Hello, Thanks for your brief explanation and your code. And also sorry for not posting runnable code. I re-posted my question with runnable code so that you can understand my problem easily.
With your help, I found why my code doesn't work. The problem is here, As I used strucut for this one and strucut is value type it's saving previous referencer and values. ``` var newItem = lastItem newItem.arr[0].i += 100 self.testModel.items.append(newItem) ``` My logic code is a bit difficult. Can you please tell me how to copy strucut value without copying value types?
Do I have to do it manually like var newItem = [S0(arr: [S1(i: 200)])]? Is there anyway I can avoid this way without using class?

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.