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

SOLVED: ObservableObject: Trouble with having unique updates on 2 layers of Observable model arrays?

Forums > SwiftUI

Hi all,

I have some more fun ObservableObject issues that I'm trying to work around, and I feel like I'm very close to an answer. Basically, I have an app that tracks some plants. I have a class called RealmSyncModel for these plants which first accesses an external MongoDB database to populate the plants array for that user. These plants are retrieved through another Plant class, which has all the info required to initiate and structure the individual objects in the array.

For each plant, a user can also add Reminders. This is handled through a separate ObservableObject class called RemindersArray, which has a Published property that references a RemindersClassModel. I was able to get it to the point where users can add multiple unique reminders, and each reminder could be edited/deleted. I was able to get some help on that part through this previous thread here.

Now my issue is: When a reminder is added, it adds that reminder for every single plant. What should happen, is that reminders should be unique for every individual plant object. For example, if you add a reminder to water your Cilantro, you shouldn't see that same reminder if you go back and choose your Tomato plant.

I feel like I've hit a wall trying to think through how to make this happen and I'm spinning in circles. Part of me thinks it involves a function tied to the RealmSyncModel/Plant classes when a Reminders is called.

Here's some example code, I can provide more context if needed!

Plants

class RealmModelStats : ObservableObject {
@Published var plantarray = [Plant]()

init () {
        self.getRealm()
    }
    func getRealm() {

    //This contains the code which calls the database objects, iterates through them, and adds them to the Plant array

}
class Plant: Object, ObjectKeyIdentifable, Identifiable {
    @objc dynamic var _id: ObjectId = ObjectId.generate()
    @objc dynamic var _partition: String = ""

    convenience init (//this contains all the different init variables)
{
...
}

Reminders Section, which contains a list of reminders and calls plantdata for a specific chosen plant

struct PlantReminders: View {
   var plantdata: Plant
   @State var isManagePresented = false
    @State var showingNewReminders = false
    @EnvironmentObject var reminders: RemindersArray
    @State var BackActionState: Int? = 0

  var body: some View {

  List {
                    ForEach(reminders.items) { reminder in
                          NavigationLink(destination: PlantReminderManageSheet(plantsensor: self.plantsensor, BackActionState: self.$BackActionState)) {
                            PlantReminderRow(isManagePresented: self.$isManagePresented, plantdata: self.plantdata, reminders: reminder)
                        }}.onDelete(perform: delete)
                }.frame(width: 350, height: 250).padding(.bottom, 0)
    }
}

Reminders Models

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

    func appendNewReminder(remindertitle: String, reminderbody: String, chosendate: String, chosentime: String, repeattime: String) {
        items.append(RemindersClassModel(remindertitle: remindertitle, reminderbody: reminderbody, chosendate: chosendate, chosentime: chosentime, repeattime: repeattime))
    }
}

class RemindersClassModel: ObservableObject, Identifiable {
   var id = UUID()
   @Published var remindertitle: String = ""
   @Published var reminderbody: String = ""
    @Published var chosendate: String = ""
    @Published var chosentime: String = ""
    @Published var repeattime: String = ""

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

3      

hi,

first, let me say that i have zero experience and zero knowledge of MongoDB. so whatever follows may or not make any sense.

bottom line: each Plant has to maintain a connection to its Reminders. i don't see such a connection in your code above.

if you were using CoreData, there would be a one-to-many relationship from Plant to Reminder. i think that if you were using CloudKit directly (not a comfortable subject for me right now), each Reminder might contain the id of the Plant it belongs to. even if Plant were a simple class or struct, one of its variables would be a Set or Array of Reminders associated with the Plant.

i'd not get caught up in whether something should be passed as an @Environment variable or not, for the moment. i would think more directly that you "pass a Plant" to a PlantReminders view; that View accesses the Plant's Reminders to form a List. if you then navigate from that List using a NavigationLink to edit a specific Reminder, it would be natural to pass along the Reminder being displayed to be edited.

tracking any edit of a Reminder back to a specific Plant that owns that Reminder might follow the path referenced in your previous post.

if you have some code out on GitHub, some folks might take a look at it; otherwise, we may need to see more code here. (and for naming, i'd suggest renaming RemindersClassModel simply as Reminder, which makes you RemindersArray sort of look like an array of Reminder.)

hope that helps,

DMG

4      

Hey @delawaremathguy - thanks again for taking the time to take a look here, I seriously appreciate it. You're definitely right in that I'm scratching my head in figuring out the best way to pass the Plant ID/connection onto the Reminders Array.

As it stands, the Plant class is tied strictly to MongoDB Realm, while the Reminder class (which I'll definitely rename, good point) is tied to creating a new local reminder. I guess it's worth rethinking some basic questions from an architecture and setup standpoint, which I'm hoping you can help me with:

  • 1) Since creating a Reminder is done locally currently, should I look into creating reminders within MongoDB Realm as well? The downside here is the work involved on the Database end, but it would make sense to set up a one-to-many relationship on the backend side.
  • 2) ...or, should I use CoreData to cache incoming data from MongoDB Realm and convert the Plant Realm data into CoreData so that it's cached (which would be nice not to reload the Realm data over and over again), and can also be tied to Reminders?
  • 3) ...or am I overthinking this entirely, and all that needs to be done is for there to be some sort of var declaration referencing the RemindersArray within the Plant class?

For the third point, would it be something as simple as including the Reminder tie-in with the Plant class?

class Plant: Object, ObjectKeyIdentifable, Identifiable {
   @objc dynamic var _id: ObjectId = ObjectId.generate()
    @objc dynamic var _partition: String = ""
    @objc dynamic var plantreminder: Reminder

I also am not sure how to tie in the Plant object within the Reminder class, is there some initialization involved with it as well?

3      

hi,

Number 3, i think!

it appears MongoDB Realm easily supports a to-many relationship. so your Plant defintion would include a variable that, as far as i can see in the quick introduction to MongoBD, would be something like this:

class Plant: Object, ObjectKeyIdentifable, Identifiable {
   @objc dynamic var _id: ObjectId = ObjectId.generate()
    @objc dynamic var _partition: String = ""
    let plantreminders = List<Reminder>() // a Plant will own multiple Reminder objects
    // other stuff
}

and now, you would define a new class Reminder that refers to one reminder for one plant. you'd have to read more into the documentation (again, i have only read a few pages on MongoDB) on how to link a Reminder to a Plant -- it could be as easy as adding a Reminder to the plantreminders of a Plant (but don't take my word for it). you'll probably also come across the notion that your Reminder class should include a reciprocal LinkingObjects() variable for a Reminder so that it can link back to the Plant it belongs to.

that's as much as i can help on this, i think. MongoDB and Core Data both persist data and give you cloud support for working with multiple devices.

hope that's useful for you,

DMG

4      

@delawaremathguy, thanks so much as always for taking the time to look into my question! I'm super close to figuring this out - your hunches are correct in that it involves a mix of linking to another Realm class object, as well as using LinkingObjects(). I found a simple example here that I'm trying to work off of: https://github.com/realm/realm-cocoa/blob/master/examples/ios/swift/ListSwiftUI/Models/Ingredient.swift

Will keep you posted as soon as I have it working flawlessly.

3      

Hacking with Swift is sponsored by Essential Developer

SPONSORED Join a FREE crash course for mid/senior iOS devs who want to achieve an expert level of technical and practical skills – it’s the fast track to being a complete senior developer! Hurry up because it'll be available only until April 28th.

Click to save your free spot now

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.