Core Data and CloudKit sync patterns for iOS. Use when debugging sync issues, race conditions, or data consistency problems.
context.save() // Synchronous TO CONTEXT, but disk write is ASYNC
loadData() // May fetch STALE data if disk write incomplete
NSManagedObjectContext.save() commits to context immediately// NSPersistentStoreRemoteChange fires when disk changes
// Often triggers reload that races with in-flight save
NotificationCenter.observe(.NSPersistentStoreRemoteChange) {
dataManager.loadData() // Can overwrite correct data with stale!
}
var isMutating = false
func moveItems(...) {
isMutating = true
defer {
DispatchQueue.main.asyncAfter(deadline: .now() + 0.75) {
self.isMutating = false
}
}
// ... mutation code
}
func loadData() {
guard !isMutating else { return } // Skip during mutation
// ... load code
}
// Use NSPersistentCloudKitContainer for automatic sync
let container = NSPersistentCloudKitContainer(name: "Model")
// Configure merge policy
context.mergePolicy = NSMergeByPropertyObjectTrumpMergePolicy
// Monitor sync events
NotificationCenter.default.addObserver(
forName: NSPersistentCloudKitContainer.eventChangedNotification,
object: container,
queue: .main
) { notification in
guard let event = notification.userInfo?[
NSPersistentCloudKitContainer.eventNotificationUserInfoKey
] as? NSPersistentCloudKitContainer.Event else { return }
if event.endDate != nil {
if let error = event.error {
handleSyncError(error)
}
}
}
// Use appropriate context for operation type
func performBackgroundSync(_ data: SyncData) async throws {
try await container.performBackgroundTask { context in
context.mergePolicy = NSMergeByPropertyObjectTrumpMergePolicy
for item in data.items {
let entity = ItemEntity.findOrCreate(id: item.id, in: context)
entity.update(from: item)
}
try context.save()
}
}
// Never pass managed objects between threads
let item = mainContext.fetch(...) // Main thread
Task.detached {
item.name = "Updated" // BUG: Wrong thread!
}
mergeByPropertyObjectTrump or custom policy