UPGRADE YOUR SKILLS: Learn advanced Swift and SwiftUI on Hacking with Swift+! >>

Having trouble updating array in another view with ObservableObject

Forums > SwiftUI

Hi everyone, I've been trying to work with some dynamic lists and arrays in my plant-care app, and it's been difficult trying to work with ObservableObject to refresh/update views when I'm not in the primary view to update an array.

Specifically, what I'm trying to do in View 1 is update the list by going to the New Sheet popup that triggers on the green Add Reminder button. I've also added a blue "add a new reminder" button up top to test that it works if the button is on the same view. However, when you attempt to add a reminder within the New Sheet view, the list does not refresh/update.

I've tried experimenting with objectWillChange.send() and other functions that I can embed to get it to change, but alas I'm stuck. Any help would be greatly appreciated!

Here's all the code I'm working with, simplified for ease of troubleshooting (I went off the framework found in this thread):

import SwiftUI

class RemindersArray: ObservableObject {
    @Published var items = [RemindersClassModel]()

    func appendNewReminder(remindertitle: String, reminderbody: String) {
        objectWillChange.send()
        items.append(RemindersClassModel(remindertitle: remindertitle, reminderbody: reminderbody))
    }
}

class RemindersClassModel: ObservableObject, Identifiable {
    var id = UUID()
    @Published var remindertitle = ""
    @Published var reminderbody = ""
    @Published var items = [RemindersClassModel]()

    init(remindertitle: String, reminderbody: String) {
        self.remindertitle = remindertitle
        self.reminderbody = reminderbody
    }
}

struct ContentView: View {
    @ObservedObject var reminders = RemindersArray()
    @State var remindertitle = "Water"
    @State var reminderbody = "Help your plant grow - water it!"
    @State var isPresented = false

    var body: some View {
        NavigationView {
            VStack {
                Button("Add a new reminder") {
                    self.reminders.appendNewReminder(remindertitle: self.remindertitle, reminderbody: self.reminderbody)
                }
                List {
                    ForEach(reminders.items) { item in
                        HStack{
                            Text(item.remindertitle)
                            Text(item.reminderbody)
                        }
                    } // End of ForEach
                } // End of List

                Button(action: {
                    print("Adding reminder")
                    self.isPresented.toggle()

                }) {
                    Text("Add Reminder").foregroundColor(Color.white).offset(y: 0)}
                    .background(Capsule()
                        .foregroundColor(Color.green).frame(width: 120, height: 35))
                    .offset(y: -50)
            }
            .navigationBarTitle(Text("View 1"))
        }.sheet(isPresented: $isPresented) {
            NewSheetTest(isPresented: self.$isPresented, array: self.reminders, classBObject: self.reminders.items[0])
        }
    }
}

struct NewSheetTest: View {
    @Binding var isPresented: Bool
    @ObservedObject var reminders = RemindersArray()
    @State var remindertitle = "Water"
    @State var reminderbody = "Help your plant grow - water it!"

    var array: RemindersArray

    @ObservedObject var classBObject: RemindersClassModel

    var body: some View {
        Button("Add a new reminder") {
            //self.reminders.appendNewReminder(remindertitle: self.remindertitle, reminderbody: self.reminderbody)
            self.isPresented = false
            self.reminders.appendNewReminder(remindertitle: self.remindertitle, reminderbody: self.reminderbody)

        }
    }
}

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

3      

As an update, I have also tried changing the RemindersClassModel to a struct instead of a class (to try a value-type change instead), but unfortunately the addition of reminders from the pop-up view still isn't working.

3      

Did you try with EnvironmentObject ?

In SceneDelegate

let reminders = RemindersArray() 
 contentView
               .environmentObject(reminders)

In ContentView

@EnvironmentObject var reminders: RemindersArray
NewSheetTest(isPresented: self.$isPresented, classBObject: self.reminders.items[0])
                .environmentObject(reminders)

In NewSheetTest

@EnvironmentObject var reminders: RemindersArray

4      

hi,

the problem is that your NewSheetTest creates its own RemindersArray with

@ObservedObject var reminders = RemindersArray()

so your self.reminders.appendNewReminder(...) code is adding a new reminder to the local reminders ObservedObject, not the one that you did not pass in from ContentView.

instead, your code for the .sheet should be

NewSheetTest(isPresented: self.$isPresented,
                                     //array: self.reminders,
                                     reminders: self.reminders)
                                     // classBObject: self.reminders.items[0])

and you code in NewSheetTest should be simplified (the array and ClassBObject have no real function there, anyway).

    @ObservedObject var reminders: RemindersArray //  = RemindersArray()
    // var array: RemindersArray
    //  @ObservedObject var classBObject: RemindersClassModel

one other thing: your RemindersArray class does not need to call objectWillChange.send() directly; appending an item to the items array will publish this for you.

    func appendNewReminder(remindertitle: String, reminderbody: String) {
        // objectWillChange.send()
        items.append(RemindersClassModel(remindertitle: remindertitle, reminderbody: reminderbody))
    }

hope that helps,

DMG

4      

@delawaremathguy I actually saw all the other replies and support you've given on other threads where I've found solutions to similar problems - I was praying that you would respond to this one haha. Thanks so much Jerry, you're a lifesaver!!

@achuaswani Thanks for the suggestion! I have limited experience with EnvironmentObject so I will definitely be using this method for other views in my app.

4      

Hey @delawaremathguy, sorry for the bother but was wondering if I could borrow you for one more question - so I've got the above setup working perfectly with your code fix, and I can easily add and delete reminders. Now, I'm trying to add one more layer to it - I have a plant-tracking app, and I can't seem to figure out how to assign reminders to their own specific plant object.

Right now, the ContentView from above holds a RemindersArray, with each object in that array being their own unique reminder.

Now, I'm trying to introduce a PlantView/Array that holds unique plants, each with its own unique RemindersArray.

I initiate both the PlantData and RemindersArray in SceneDelegate as EnvironmentObjects as well.

For the NewSheetTest, I'm guessing I should be passing in PlantData somehow? Currently it stands as:

NewSheetTest(isPresented: self.$isPresented,
                                     reminders: self.reminders) 

..and NewSheetTest does not contain any reference to the PlantArray object. Any help would be great, I'm so close to cracking it!

3      

Hacking with Swift is sponsored by RevenueCat

SPONSORED Take the pain out of configuring and testing your paywalls. RevenueCat's Paywalls allow you to remotely configure your entire paywall view without any code changes or app updates.

Learn more here

Sponsor Hacking with Swift and reach the world's largest Swift community!

Archived topic

This topic has been closed due to inactivity, so you can't reply. Please create a new topic if you need to.

All interactions here are governed by our code of conduct.

 
Unknown user

You are not logged in

Log in or create account
 

Link copied to your pasteboard.