1

I can't figure it out why view is not updating, please help. In real project I get data via websocket (and set variable with DispatchQueue.main.async {}). Here's the code as an example. After clicking on button nothing happens with the view. I use ObservableObject, Published attributes. What's the problem?

ps. It requires to add some more text to the post, because it's mostly the code, but I don't know what to add, everything is below :)

import SwiftUI

class DataBase: ObservableObject {
    
    @Published var data: [MyData]
    @Published var users: [User]
    
    init(data: [MyData], users: [User]) {
        self.data = data
        self.users = users
    }
}

class MyData: ObservableObject, Identifiable {
    
    @Published var type: String
    @Published var array: [Double]
    
    init(type: String, array: [Double]) {
        self.type = type
        self.array = array
    }
}

class User: ObservableObject, Identifiable {
    
    @Published var id: UUID = UUID()
    @Published var name: String
    @Published var data: MyData
    
    init(name: String, data: MyData) {
        self.name = name
        self.data = data
    }
}

let data: [MyData] = [
    MyData(type: "type1", array: [1, 2, 3]),
    MyData(type: "type2", array: [4, 5, 6, 7]),
]

let users: [User] = [
    User(name: "Tim", data: data[0]),
    User(name: "Steve", data: data[1]),
]

struct ContentView: View {
    
    let db = DataBase(data: data, users: users)
    
    var body: some View {
        ShowView(db: db)
    }
}

struct ShowView: View {
    
    @ObservedObject var db: DataBase
    
    var body: some View {
        
        HStack {
            
            List(db.users) { user in
                Text("\(user.name) \(user.data.type)")
                Text("\(user.data.array.count)")
                Divider()
            }
            
            List(db.data) { data in
                Text("\(data.type)")
                Text("\(data.array.count)")
                Divider()
            }
        }
        
        HStack {
            Button("add data to data[0]") {
                db.data[0].array.append(db.data[0].array.last! + 10)
                print(db.data[0].array)
            }
            
            Button("add data to data[1]") {
                db.data[1].array.append(db.data[1].array.last! + 20)
                print(db.data[1].array)
            }
        }
        
    }
}

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

2 Answers 2

1

try this using objectWillChange, works for me:

struct ShowView: View {
    
    @ObservedObject var db: DataBase
    
    var body: some View {
        
        HStack {
            
            List(db.users) { user in
                Text("\(user.name) \(user.data.type)")
                Text("\(user.data.array.count)")
                Divider()
            }
            
            List(db.data) { data in
                Text("\(data.type)")
                Text("\(data.array.count)")
                Divider()
            }
        }
        
        HStack {
            Button("add data to data[0]") {
                db.objectWillChange.send()  // <-- here
                db.data[0].array.append(db.data[0].array.last! + 10)
                print(db.data[0].array)
            }
            
            Button("add data to data[1]") {
                db.objectWillChange.send()  // <-- here
                db.data[1].array.append(db.data[1].array.last! + 20)
                print(db.data[1].array)
            }
        }
        
    }
}
Sign up to request clarification or add additional context in comments.

Comments

1

Just make model as value type (i.e. struct instead of class) - no more changes needed:

struct MyData: Identifiable {
    var id = UUID()
    var type: String
    var array: [Double]

    init(type: String, array: [Double]) {
        self.type = type
        self.array = array
    }
}

struct User: Identifiable {

    var id: UUID = UUID()
    var name: String
    var data: MyData

    init(name: String, data: MyData) {
        self.name = name
        self.data = data
    }
}

Tested with Xcode 13.4 / iOS 15.5

Update

Then it is needed to create separated views with ObservedObject for every observable model object, like

List(db.users) {
   UserRowView(user: $0)
}

struct UserRowView: View {
  @ObservedObject var user: User  // a class, so needed to be observed

  var body: some View {
    Text("\(user.name) \(user.data.type)")
    Text("\(user.data.array.count)")
    Divider()
  }
}

the same for MyData, or make a dependency update, like

class User: ObservableObject {
    @Published var data: MyData

    // ...

    private var cancellable: AnyCancellable?
    init(...) {
        // ....
        cancellable = data.objectWillChange.sink { [weak self] _ in
            guard let self = self else { return }
            self.objectWillChange.send()
        }
    }
}

1 Comment

I can't - it's just a sample code, real project is too complicated. Maybe it's possible to use some intermediate structs?

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.