Hey everyone I just ran into an issue where I couldn't sync the model below fully by using CloudKit,
enum LinkMapV3_1: VersionedSchema {
static let versionIdentifier: Schema.Version = .init(3, 1, 0)
static var models: [any PersistentModel.Type] {
[AnnotationData.self, GroupData.self, Item.self, Deployment.self, History.self]
}
// MARK: - Data
@Model
class AnnotationData {
var name: String = ""
var longitude: Double = 0.0
var latitude: Double = 0.0
var order: Int = -1
var level: Int = 1
var detail: String = ""
@Relationship(deleteRule: .nullify, inverse: \GroupData.annotation)
var groups: [GroupData]?
@Relationship(deleteRule: .nullify, inverse: \AnnotationData.to)
var from: AnnotationData?
var to: AnnotationData?
var history: History?
}
// MARK: - History
@Model
class History {
var id: UUID = UUID()
var timestamp: Date = Date()
@Relationship(deleteRule: .nullify, inverse: \AnnotationData.history)
var annotations: [AnnotationData]?
@Relationship(deleteRule: .nullify, inverse: \GroupData.history)
var groups: [GroupData]?
@Relationship(deleteRule: .nullify, inverse: \Item.history)
var items: [Item]?
@Relationship(deleteRule: .nullify, inverse: \Deployment.history)
var deployment: Deployment?
var formattedDate: String {
let formatter = DateFormatter()
formatter.dateStyle = .medium
formatter.timeStyle = .short
return formatter.string(from: timestamp)
}
var timeAgo: String {
let formatter = RelativeDateTimeFormatter()
formatter.unitsStyle = .abbreviated
return formatter.localizedString(for: timestamp, relativeTo: Date())
}
}
}
So when trying to sync with the code in documentation
let modelContainer: ModelContainer
init() {
let config = ModelConfiguration()
typealias vs = LinkMapV3_1
do {
#if DEBUG
// Use an autorelease pool to make sure Swift deallocates the persistent
// container before setting up the SwiftData stack.
try autoreleasepool {
let desc = NSPersistentStoreDescription(url: config.url)
let opts = NSPersistentCloudKitContainerOptions(containerIdentifier: "iCloud.name.Endsunset.LinkMap.SwiftData.v1")
desc.cloudKitContainerOptions = opts
// Load the store synchronously so it completes before initializing the
// CloudKit schema.
desc.shouldAddStoreAsynchronously = false
if let mom = NSManagedObjectModel.makeManagedObjectModel(for: [vs.AnnotationData.self, vs.GroupData.self, vs.Item.self, vs.Deployment.self, vs.History.self]) {
let container = NSPersistentCloudKitContainer(name: "LinkMap", managedObjectModel: mom)
container.persistentStoreDescriptions = [desc]
container.loadPersistentStores {_, err in
if let err {
fatalError(err.localizedDescription)
}
}
// Initialize the CloudKit schema after the store finishes loading.
try container.initializeCloudKitSchema()
// Remove and unload the store from the persistent container.
if let store = container.persistentStoreCoordinator.persistentStores.first {
try container.persistentStoreCoordinator.remove(store)
}
}
}
#endif
modelContainer = try ModelContainer(for:
vs.AnnotationData.self,
vs.GroupData.self,
vs.Item.self,
vs.Deployment.self,
vs.History.self,
configurations: config)
} catch {
fatalError(error.localizedDescription)
}
}
The output is
Where you can see
Optional arrays with @Relationship are missing, and the entry of record types on cloudkit database container are also missing it.
When I attempt to insert an annotation, I got
SwiftData/PersistentModel.swift:559: Fatal error: This KeyPath does not appear to relate AnnotationData to anything - \AnnotationData.groups
It gets more suspicious when restart the app and try again, the above error end with "AnnotationData.history", and if I tried again the above error end with "AnnotationData.from"... and so on.
No matter how my app stop working.
The following console log says that your app detected an account switching at the beginning of the launch process, which may erase the existing data created before the switching. It also says that you were using a simulator.
CoreData: warning: CoreData+CloudKit: -[PFCloudKitSetupAssistant _checkUserIdentity:]_block_invoke_3(1487): : CKIdentity record has changed from _b09f3f2b1357d21fdd823f58762f6077 to _4e8dc229775b034192825a3081a709a4 error: CoreData+CloudKit: -[NSCloudKitMirroringDelegate _performSetupRequest:]_block_invoke(1242): : Failed to set up CloudKit integration for store: (URL: file:///Users/yuanping_ke/Library/Developer/CoreSimulator/Devices/BA9191E9-6DB4-4A30-9196-0A6C06A11F2E/data/Containers/Data/Application/5977FE0D-50D4-4AE0-8134-5D8B0B662298/Library/Application%20Support/default.store)
Later your app did successfully set up CloudKit integration, as shown below, and then did several successful exports and imports, but it seems that there is no change merged into the store.
CoreData: warning: CoreData+CloudKit: -[NSCloudKitMirroringDelegate _performSetupRequest:]_block_invoke(1148): : Successfully set up CloudKit integration for store (DDC3835A-A6F4-41B2-B9E3-0C3E66A7F94B): (URL: file:///Users/yuanping_ke/Library/Developer/CoreSimulator/Devices/BA9191E9-6DB4-4A30-9196-0A6C06A11F2E/data/Containers/Data/Application/5977FE0D-50D4-4AE0-8134-5D8B0B662298/Library/Application%20Support/default.store)
Based on above, my suggestion will be:
-
Avoid switching account so
NSPersistentCloudKitContainerdoesn't delete the existing data. -
Use physical devices to test the CloudKit integration. CloudKit synchronization relies on push notifications, which iOS simulators didn't support at earlier time. The situation may have improved, but still, I'd rather test with physical devices to be sure. For how to set up a physical device, see Configure iCloud on your devices.
-
Make sure the CloudKit schema is correctly created by using CloudKit Console to ensure each SwiftData model and attribute has a CloudKit counterpart. See Reading CloudKit Records for Core Data for the mapping rules. If you see something is missed, try to run initializeCloudKitSchema(options:) again, or manually fix the issue if running the API again doesn't help. See Create the CloudKit schema for more details. The synchronization won't work if the CloudKit schema doesn't match the SwiftData schema.
-
Your console log doesn't reveal why the imports and exports didn't find data. If it shouldn't be that case, I'd suggest that you go though the process described in TN3163: Understanding the synchronization of NSPersistentCloudKitContainer to see if a system diagnose gives you more information.
Best,
——
Ziqiao Chen
Worldwide Developer Relations.