swiftPunk
swiftPunk

Reputation: 1

How we can notify ObservableObject about changes of its initializers?

I have a ObservableObject-Class which inside this class, I got a published var with name of persones! I do initialize it with some data called: allData.

Then I try to update my allData with action of a Button, and this action apply the wanted update to my allData, but my published var has no idea, that this data got updated!

How we can make published see the new updated allData?

struct PersonData: Identifiable {
    let id = UUID()
    var name: String
}

var allData = [PersonData(name: "Bob"), PersonData(name: "Nik"), PersonData(name: "Tak"), PersonData(name: "Sed"), PersonData(name: "Ted")]

class PersonDataModel: ObservableObject {
    @Published var persones: [PersonData] = allData
}

struct ContentView: View {
    @StateObject var personDataModel = PersonDataModel()

    var body: some View {
        VStack
        {
            Button("update allData") { allData = [PersonData(name: "Bob")] }

            HStack
            {
                ForEach(personDataModel.persones) { person in Text(person.name) }
            }
        }
        .font(Font.title)
    }
}

PS: I don´t want use .onChange or other things for this, I would like this happens internally in my class.

Also I know I can use down code for this work, but that is not the answer

personDataModel.persones = [PersonData(name: "Bob")] 

Upvotes: 1

Views: 271

Answers (3)

pawello2222
pawello2222

Reputation: 54516

Having a top-level property (outside of any class or struct) is probably not a good idea. I don't see the whole picture, but it looks like your app needs a global state (e.g., a @StateObject initialised on the App level). Consider this answer:


If you really need to observe your array, you need to make it observable.

One option is to use CurrentValueSubject from the Combine framework:

var persons = ["Bob", "Nik", "Tak", "Sed", "Ted"].map(PersonData.init)
var allData = CurrentValueSubject<[PersonData], Never>(persons)

class PersonDataModel: ObservableObject {
    @Published var persones: [PersonData] = allData.value

    private var cancellables = Set<AnyCancellable>()

    init() {
        allData
            .sink { [weak self] in
                self?.persones = $0
            }
            .store(in: &cancellables)
    }
}

struct ContentView: View {
    @StateObject var personDataModel = PersonDataModel()

    var body: some View {
        VStack {
            Button("update allData") { 
                allData.send([PersonData(name: "Bob")]) 
            }
            HStack {
                ForEach(personDataModel.persones) { person in 
                    Text(person.name)
                }
            }
        }
        .font(Font.title)
    }
}

Upvotes: 1

Simone Pistecchia
Simone Pistecchia

Reputation: 2842

I think you're doing something wrong.

if you want to update all your views, you have to pass the same object with @EnviromentObject.

I don't know your storage method (JSON, CORE DATA, iCloud) but the correct approach is to update directly the model

class PersonDataModel: ObservableObject
{
    @Published var persones: [PersonData] = loadFromJSON //one func that is loading your object stored as JSON file

    func updateAllData() {
        storeToJSON(persones) //one func that is storing your object as JSON file
    }
}


struct ContentView: View {
    
    @StateObject var personDataModel = PersonDataModel()
    
    var body: some View {

        VStack
        {
            Button("update allData") { 
                self.personDataModel.persones = [PersonData(name: "Bob")] 
             }
            
            
            HStack
            {
                ForEach(personDataModel.persones) { person in Text(person.name) }
            }


 
        }
        .font(Font.title)
        .onChange($personDataModel.persones) {
             persones.updateAllData()
        }
    }
}

Upvotes: 0

Asperi
Asperi

Reputation: 257749

The allData is copied into persones at initialization time, so changing it afterwards does nothing to personDataModel. After StateObject created you have to work with it, like

Button("update allData") { 
   self.personDataModel.persones = [PersonData(name: "Bob")] 
}

Upvotes: 0

Related Questions