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()
}
}
CodePudding user response:
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)
}
}
}
}
CodePudding user response:
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()
}
}
}