Thanks for the reply.
I've been trying to get CKSyncEngine working with SwiftData. Since you don't any sample code, here's my code and some questions about it.
CKSyncEngineDelegate has a function called "handleEvent". In that function I handle the "fetchedRecordZoneChanges" event amongst other events. Here's my function to handle that event (it's basically copy paste from the sample app):
func handleFetchedRecordZoneChanges(_ event: CKSyncEngine.Event.FetchedRecordZoneChanges) {
guard let localContainer else { return }
let newContext = ModelContext(localContainer)
let localHabits = SwiftDataPersistence.fetch(FetchDescriptor<Habit>(), predicate: nil, sortBy: [], fetchLimit: nil, context: newContext)
for modification in event.modifications {
// The sync engine fetched a record, and we want to merge it into our local persistence.
let serverHabit = modification.record
let serverHabitID = serverHabit.recordID.recordName
// If we already have this object locally, let's merge the data from the server.
if let localHabit = localHabits.first(where: { habit in
habit.identifier == serverHabitID
}) {
localHabit.mergeFromServerData(serverHabit)
localHabit.setLastKnownRecordIfNewer(serverHabit)
}
// Otherwise, let's create a new local object.
else {
let newHabit = Habit(record: serverHabit)
newContext.insert(newHabit)
}
}
for deletion in event.deletions {
// TO DO
}
// If we had any changes, let's save to disk.
if !event.modifications.isEmpty || !event.deletions.isEmpty {
SwiftDataPersistence.save(newContext)
NotificationCenter.default.post(name: newContextDidSaveNotification, object: nil)
}
}
Last line of code: I'm sending a notification to my SwiftUI view (that shows habits) to fetch edited/new habits.
Here's my view code:
struct HabitsListView: View {
@State private var habits = [Habit]()
@Environment(\.modelContext) private var context
var body: some View {
LazyVStack(alignment: .leading, spacing: 8) {
ForEach(habits) { habit in
HabitRow(habit)
}
}
.onNewContextDidSave {
fetchHabits()
}
}
func fetchHabits() {
self.habits = SwiftDataPersistence.fetch(FetchDescriptor<Habit>(), predicate: nil, sortBy: [SortDescriptor(\Habit.name)], fetchLimit: nil, context: context)
}
}
This approach seems to work, but it's not ideal and I'm not sure it's a correct way to do it. It works with only 1 custom notification, but I ran into problems when I added more notifications - then the notifications stopped firing or the view didn't catch them.
But my main question at this moment is:
How to get @Query to update my habits list, so I don't have to manually fetch habits at every change.
I would like to use a view like this:
struct HabitsListView: View {
@Query private var habits: [Habit]
var body: some View {
LazyVStack(alignment: .leading, spacing: 8) {
ForEach(habits) { habit in
HabitRow(habit)
}
}
}
}
Thanks!
Martin
Post
Replies
Boosts
Views
Activity
Thanks for making this post. I've been trying to get SwiftData custom migration work with CloudKit for more than a week now and it seems to me it's not compatible with CloudKit (works fine with just SwiftData, but with CloudKit the willMigrate and didMigrate are never called). Better to use custom code I guess to change user data.