Listener + Core Data + Multiple view models

Hi Code Crew,

I have multiple Models and View Models… Data are coming from Firestore and is save into Core Data so app doesn’t have to fetch data from firestore if data is already available in Core Data. I use a Firestore “collection” to save all the “Updates” dates… e.g. userProfileLastUpdate. This is stored/managed by the app “UserUpdate” model. There is a listener attached to it so when there is an update, the app gets notified. And so if “UserUpdate” model listener indicates userProfileLastUpdate got change, app needs to fetch the new data from Firestore.

Just to simplify the app model, I have two view models below…

ViewModels:

  • UserUpdateModel() → Manger user “updates dates”. Has a listener for “user update dates”
  • UserProfileModel() → User Profile is manager here

Firestore collection:

  • Collection = “userUpdates” → contains userID, ProfileLastUpdate (timestamp), etc

Core Data:

  • On app first run, it will fetch data Firestore database and save data into Core Data. This means app doesn’t need to fetch data from Firestore again on the next startup since data is already available in core data.

My question is when my UserUpdate listener from UserUpdateModel() indicates that ProfileLastUpdate is updated, how does that app trigger UserProfileModel() to fetch the data? I can’t put a listener to the UserProfile collection in Firestore because that would mean the app will always download everything on startup.

Please suggest if my architecture isn’t efficient but this is the only way I can think of so :

  • I don’t have to put a listener to all the collections in Firestore
  • app does not need to download data from firestore on startup (on my testing, when you enable listener, it will download all the data)

Cheers,

Hello @ChaseCooks

If I understand it correctly it looks like you’re trying to create an efficient synchronization process between Firebase Firestore, Core Data, and your app’s views.

One potential approach to handle this issue is to leverage Combine or Swift’s native callback closures to communicate between your models. You could create a closure in your UserUpdateModel that gets called when an update occurs. The UserProfileModel would hold a reference to this closure and would start fetching the new data once the closure is called.

Here’s a simplified example of how you might set this up:

class UserUpdateModel {
    // Update Closure
    var onUpdate: ((Date) -> Void)?

    // Simulated listener
    func listenForUpdates() {
        // When an update occurs (simulated by the below code), we call onUpdate
        DispatchQueue.main.asyncAfter(deadline: .now() + 2.0) {
            let updateDate = Date() // The updated date
            self.onUpdate?(updateDate)
        }
    }
}

class UserProfileModel {
    var userUpdateModel: UserUpdateModel?
    
    init() {
        self.userUpdateModel = UserUpdateModel()
        
        // Assign closure to execute when user updates
        self.userUpdateModel?.onUpdate = { updatedDate in
            // Here we can start fetching the new data
            print("Received update on date \(updatedDate)")
        }
        
        self.userUpdateModel?.listenForUpdates()
    }
}

In the above example, UserProfileModel has a reference to UserUpdateModel, and it assigns a closure to UserUpdateModel.onUpdate. When UserUpdateModel receives an update, it calls this closure, which triggers UserProfileModel to fetch the new data.

This approach should help you keep Firestore and Core Data in sync, while also preventing unnecessary downloads on app startup. However, you still need to carefully manage the life cycle of your objects to avoid retain cycles or premature deallocation.

Your architecture appears efficient given the requirements you’ve outlined, as it reduces Firestore read operations and leverages local storage. But always remember to handle error scenarios and network unavailability.

Also, it’s worth mentioning that if you’re developing for iOS 13 or newer, you might want to consider using SwiftUI and Combine. It gives a more declarative approach and fits very well in this kind of situation.

I hope this give you an idea to how to solve your problem, good luck with your project. :slight_smile:

1 Like

Hi Joash,

Thank you so much!

Update closure is new to me so will look it up. The sample is really helpful!

Keep hearing combine as well but know so little about it… Will dig into this two features…

Thanks again!

Cheers,

1 Like