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”
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)
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.