I have started from here: Apple's guide on the sharing core data objects between iCloud users and I have created a sample project that has Collections and Items. Everything works great while I stay on Swift 5, like with the initial project.
I would like to migrate to Swift 6 (Default Actor Isolaton @MainActor, Approachable Concurrency: Yes) on the project and I am stuck at extension CDCollection: Transferable { ... }. When compiling with Swift 5, there is a warning: Conformance of 'NSManagedObject' to 'Sendable' is unavailable in iOS; this is an error in the Swift 6 language mode.
After resolving almost all compile-time warnings I'm left with:
Conformance of 'CDCollection' to protocol 'Transferable' crosses into main actor-isolated code and can cause data races.
Which I don't think will work, because of the warning shown above. It can be worked around like:
nonisolated extension CDCollection: Transferable, @unchecked Sendable
Then there are errors:
let persistentContainer = PersistenceController.shared.persistentContainer Main actor-isolated static property 'shared' can not be referenced from a nonisolated context.
I've created the following class to have a Sendable object:
struct CDCollectionTransferable: Transferable {
var objectID: NSManagedObjectID
var persistentContainer: NSPersistentCloudKitContainer
public static var transferRepresentation: some TransferRepresentation {
CKShareTransferRepresentation { collectionToExport in
let persistentContainer = collectionToExport.persistentContainer
let ckContainer = CloudKitProvider.container
var collectionShare: CKShare?
if let shareSet = try? persistentContainer.fetchShares(
matching: [collectionToExport.objectID]),
let (_, share) = shareSet.first
{
collectionShare = share
}
/**
Return the existing share if the collection already has a share.
*/
if let share = collectionShare {
return .existing(share, container: ckContainer)
}
/**
Otherwise, create a new share for the collection and return it.
Use uriRepresentation of the object in the Sendable closure.
*/
let collectionURI = collectionToExport.objectID
.uriRepresentation()
return .prepareShare(container: ckContainer) {
let collection = await persistentContainer.viewContext
.perform {
let coordinator = persistentContainer.viewContext
.persistentStoreCoordinator
guard
let objectID = coordinator?.managedObjectID(
forURIRepresentation: collectionURI
)
else {
fatalError(
"Failed to return the managed objectID for: \(collectionURI)."
)
}
return persistentContainer.viewContext.object(
with: objectID
)
}
let (_, share, _) = try await persistentContainer.share(
[collection],
to: nil
)
return share
}
}
}
}
And I'm able to compile and run the app with this change:
let transferable = CDCollectionTransferable(
objectID: collection.objectID,
persistentContainer: PersistenceController.shared
.persistentContainer
)
ToolbarItem {
ShareLink(
item: transferable,
preview: SharePreview("Share \(collection.name)!")
) {
MenuButtonLabel(
title: "New Share",
systemImage: "square.and.arrow.up"
)
}
}
The app crashes when launched with
libdispatch.dylib`_dispatch_assert_queue_fail:
0x1052c6ea4 <+0>: sub sp, sp, #0x50
0x1052c6ea8 <+4>: stp x20, x19, [sp, #0x30]
0x1052c6eac <+8>: stp x29, x30, [sp, #0x40]
0x1052c6eb0 <+12>: add x29, sp, #0x40
0x1052c6eb4 <+16>: adrp x8, 63
0x1052c6eb8 <+20>: add x8, x8, #0xa0c ; "not "
0x1052c6ebc <+24>: adrp x9, 62
0x1052c6ec0 <+28>: add x9, x9, #0x1e5 ; ""
0x1052c6ec4 <+32>: stur xzr, [x29, #-0x18]
0x1052c6ec8 <+36>: cmp w1, #0x0
0x1052c6ecc <+40>: csel x8, x9, x8, ne
0x1052c6ed0 <+44>: ldr x10, [x0, #0x48]
0x1052c6ed4 <+48>: cmp x10, #0x0
0x1052c6ed8 <+52>: csel x9, x9, x10, eq
0x1052c6edc <+56>: stp x9, x0, [sp, #0x10]
0x1052c6ee0 <+60>: adrp x9, 63
0x1052c6ee4 <+64>: add x9, x9, #0x9db ; "BUG IN CLIENT OF LIBDISPATCH: Assertion failed: "
0x1052c6ee8 <+68>: stp x9, x8, [sp]
0x1052c6eec <+72>: adrp x1, 63
0x1052c6ef0 <+76>: add x1, x1, #0x9a6 ; "%sBlock was %sexpected to execute on queue [%s (%p)]"
0x1052c6ef4 <+80>: sub x0, x29, #0x18
0x1052c6ef8 <+84>: bl 0x105301b18 ; symbol stub for: asprintf
0x1052c6efc <+88>: ldur x19, [x29, #-0x18]
0x1052c6f00 <+92>: str x19, [sp]
0x1052c6f04 <+96>: adrp x0, 63
0x1052c6f08 <+100>: add x0, x0, #0xa11 ; "%s"
0x1052c6f0c <+104>: bl 0x1052f9ef8 ; _dispatch_log
0x1052c6f10 <+108>: adrp x8, 95
0x1052c6f14 <+112>: str x19, [x8, #0x1f0]
-> 0x1052c6f18 <+116>: brk #0x1
The app still crashes when I comment this code, and all Core Data related warnings.
I'm quite stuck now as I want to use Swift 6.
Has anyone figured CloudKit, CoreData and Swift 6 for sharing between users?
CloudKit
RSS for tagStore structured app and user data in iCloud containers that can be shared by all users of your app using CloudKit.
Posts under CloudKit tag
157 Posts
Selecting any option will automatically load the page
Post
Replies
Boosts
Views
Activity
While testing record creation in public CloudKit database for authenticated user I am able to do so without any issues. But for devices missing iCloud account or authentication expired I am seeing the below error:
▿ <CKError 0x97a959200: "Permission Failure" (10/2007); server message = "CREATE operation not permitted"; op = 67331DE3AF3DD666; uuid = 1F3ACD4F-A799-4CD4-ADF0-EDE9E12F2DCB; container ID = "***">
_nsError : <CKError 0x97a959200: "Permission Failure" (10/2007); server message = "CREATE operation not permitted"; op = 67331DE3AF3DD666; uuid = 1F3ACD4F-A799-4CD4-ADF0-EDE9E12F2DCB; container ID = "***">
I am unable to add create/write permission to _world security role in dashboard.
Is this something not supported by Cloudkit? Only authenticated iCloud users will be able to create and write data to public database as well?
I have the following code running on macOS and iOS:
CKQuerySubscription *zsub = [[CKQuerySubscription alloc] initWithRecordType:ESS_CLOUDCONTROLLER_RECORDTYPE_PUSHNOTE predicate:[NSPredicate predicateWithFormat:@"TRUEPREDICATE"] subscriptionID:@"pushZSub" options:CKQuerySubscriptionOptionsFiresOnRecordUpdate|CKQuerySubscriptionOptionsFiresOnRecordCreation|CKQuerySubscriptionOptionsFiresOnRecordDeletion];
zsub.zoneID = zid;
CKNotificationInfo *inf = [[CKNotificationInfo alloc] init];
inf.shouldSendContentAvailable = YES;
inf.desiredKeys = @[ESS_PN_RECORDFIELD_KEY_OVERALLDATE];
zsub.notificationInfo = inf;
CKModifySubscriptionsOperation *msop = [[CKModifySubscriptionsOperation alloc] initWithSubscriptionsToSave:@[zsub] subscriptionIDsToDelete:nil];
msop.qualityOfService = NSQualityOfServiceUserInitiated;
msop.modifySubscriptionsCompletionBlock = ^(NSArray<CKSubscription *> * _Nullable savedSubscriptions, NSArray<CKSubscriptionID> * _Nullable deletedSubscriptionIDs, NSError * _Nullable operationError) {
dispatch_async(dispatch_get_main_queue(), ^{
if (savedSubscriptions.count == 1) { //works also when already created.
compH(YES, nil);
} else {
compH(NO, nil);
}
});
};
[self.database addOperation:msop];
(code synopsis: after i create a custom zone (not shown in code), I add a ckquerysubscription to it for a specific record type, configured as a silent notification)
When I change the according record in my Mac app, I get an immediate silent push on iOS.
On macOS, however, after I change the record in my iOS app, I don't get one. Sometimes, one silent push makes it through every now and then a minute+ late or so, and after that, it's going missing again.
What's the deal? Everything's set up correctly (com.apple.developer.aps-environment is set, container-identifiers are the same, icloud services are the same, ubiquity-kvstore-identifier are the same).
I obviously register for remote notifications in both apps. I see all the records and subscriptions and zones in both the Mac and iOS app.
I tried setting alertBody to an empty string, or soundName to an empty string, or both to an empty string: no difference
I tried having different subscriptions for my Mac and iOS app, since they use different bundle ids, but that was merged into one subscription server-side, so I'm thinking that's not it
I tried making it not-silent by setting contentAvailable to NO and adding a full alertBody, title and subtitle. Again, worked on iOS, not on macOS.
This has been going on since macOS 14 Sonoma (when I first got reports of this. Now running on macOS 26.3). Before Sonoma, it worked just fine.
Now I thought perhaps it's because I had a subscription on the default zone, and not a custom one, so I tried subscribing to changes on a record in a custom zone (see code above), but that did not change anything either.
It's all working fine, only the push notifications are not making it through to the Mac app.
If I sudo killall apsd (kill the push service daemon), the last push notification suddenly miraculously makes it through, by the way.
At this point, I'm out of ideas and would very much appreciate pointers as to how to debug this. Polling every 30 seconds for changes is so 1990s.
Speaking of which, this is a rather long-time-running app (started in 2011). Could my CloudKit database be “too old” or “corrupted” or whatever?
Thank you kindly,
– Matthias
Usually, when you call fetchRecordZoneChanges with the previous change token, you get a list of the record ID’s that have been deleted since your last fetch.
But if you get a changeTokenExpired error because it‘s been too long since you last fetched, you have to call fetch again without a token.
For my specific application, I still need to know, though, if any records have been deleted since my last sync. How can I get that information if I no longer have a valid change token?
Hi everyone,
On macOS 26.4 beta (with Xcode 26.4 beta), I’m seeing the following console messages in a brand new SwiftData + CloudKit template project (no custom logic added, fresh CloudKit container):
updateTaskRequest called for a pre-running task com.apple.coredata.cloudkit.activity.export.F9EE783D-7521-4EC2-B42C-9FD1F29BA5C4
updateTaskRequest called for an already running/updated task com.apple.coredata.cloudkit.activity.export.F9EE783D-7521-4EC2-B42C-9FD1F29BA5C4
Error updating background task request: Error Domain=BGSystemTaskSchedulerErrorDomain Code=8 "(null)"
These messages appear:
When CloudKit is enabled
Occasionally on app launch
Often when bringing the app back to the foreground (Cmd-Tab away and back)
Even with zero additional SwiftData logic
They do not appear when CloudKit is disabled.
This behavior is reproducible on a completely new project with a fresh CloudKit container.
Questions:
What exactly do these messages indicate?
Is BGSystemTaskScheduler Code=8 expected in this context?
Are these safe to ignore?
Is this a known change in logging behavior in macOS 26.4 beta?
Additionally, in a larger project I’ve observed SwiftData crashes and initially suspected these logs might be related. However, since the issue reproduces in a fresh template project, I’m unsure whether this is simply verbose beta logging or something more serious.
Any clarification would be appreciated.
Filed as FB21993521.
In didFinishLaunchingWithOptions I have this setup for getting the token to send to my server for notifications. The issue is that the delegate callback didRegisterForRemoteNotificationsWithDeviceToken gets called twice when also initializing a CKSyncEngine object.
This confuses me. Is this expected behavior? Why is the delegate callback only called twice when both are called, but not at all when only using CKSyncEngine.
See code and comments below.
/// Calling just this triggers `didRegisterForRemoteNotificationsWithDeviceToken` once.
UIApplication.shared.registerForRemoteNotifications()
/// When triggering the above function plus initializing a CKSyncEngine, `didRegisterForRemoteNotificationsWithDeviceToken` gets called twice.
/// This somewhat make sense, because CloudKit likely also registers for remote notifications itself, but why is the delegate not triggered when *only* initializing CKSyncEngine and removing the `registerForRemoteNotifications` call above?
let syncManager = SyncManager()
/// Further more, if calling `registerForRemoteNotifications` with a delay instead of directly, the delegate is only called once, as expected. For some reason, the delegate is only triggered when two entities call `registerForRemoteNotifications` at the same time?
DispatchQueue.main.asyncAfter(deadline: .now() + 4) {
UIApplication.shared.registerForRemoteNotifications()
}
func application(_ application: UIApplication, didRegisterForRemoteNotificationsWithDeviceToken deviceToken: Data) {
print("didRegisterForRemoteNotificationsWithDeviceToken")
}
I am a novice developer, so please be kind. 😬
I am developing a simple macOS app backed with SwiftData and trying to set up iCloud sync so data syncs between two Macs running the app. I have added the iCloud capability, checked the CloudKit box, and selected an iCloud Container. Per suggestion of Paul Hudson, my model properties have either default values or are marked as optional, and the only relationship in my model is marked as optional.
@Model
final class Project {
// Stable identifier used for restoring selected project across launches.
var uuid: UUID?
var name: String = ""
var active: Bool = true
var created: Date = Foundation.Date(timeIntervalSince1970: 0)
var modified: Date = Foundation.Date(timeIntervalSince1970: 0)
// CloudKit requires to-many relationships to be optional in this schema.
@Relationship
var timeEntries: [TimeEntry]?
init(name: String, active: Bool = true, uuid: UUID? = UUID()) {
self.uuid = uuid
self.name = name
self.active = active
self.created = .now
self.modified = .now
self.timeEntries = []
}
@Model
final class TimeEntry {
// Core timing fields.
var start: Date = Foundation.Date(timeIntervalSince1970: 0)
var end: Date = Foundation.Date(timeIntervalSince1970: 0)
var codeRawValue: String?
var activitiesRawValue: String = ""
// Inverse relationship back to the owning project.
@Relationship(inverse: \Project.timeEntries)
var project: Project?
init(
start: Date = .now,
end: Date = .now.addingTimeInterval(60 * 60),
code: BillingCode? = nil,
activities: [ActivityType] = []
) {
self.start = start
self.end = end
self.codeRawValue = code?.rawValue
self.activitiesRawValue = Self.serializeActivities(activities)
}
I have set up the following in the AppDelegate for registering for remote notifications as well as some logging to console that the remote notification token was received and to be notified when when I am receiving remote notifications.
private final class TimeTrackerAppDelegate: NSObject, NSApplicationDelegate {
func applicationDidFinishLaunching(_ notification: Notification) {
print("📡 [Push] Registering for remote notifications")
NSApplication.shared.registerForRemoteNotifications()
}
func application(_ application: NSApplication, didRegisterForRemoteNotificationsWithDeviceToken deviceToken: Data) {
let tokenPreview = deviceToken.map { String(format: "%02x", $0) }.joined().prefix(16)
print("✅ [Push] Registered for remote notifications (token prefix: \(tokenPreview)...)")
}
func application(_ application: NSApplication, didFailToRegisterForRemoteNotificationsWithError error: Error) {
let nsError = error as NSError
print("❌ [Push] Failed to register for remote notifications: \(nsError.domain) (\(nsError.code)) \(nsError.localizedDescription)")
}
func application(_ application: NSApplication, didReceiveRemoteNotification userInfo: [String: Any]) {
print("📬 [Push] Received remote notification: \(userInfo)")
}
}
In testing, I run the same commit from Xcode on two different Macs logged into the same iCloud account.
My problem is that sync is not reliably working. Starting up the app on both Macs shows that the app successfully registered for remote notifications.
Sometimes, making an edit on Mac 1 is immediately reflected in Mac 2 UI along with didReceiveRemoteNotification message (all occurring while the Mac 2 app remains in foreground). Sometimes, the Mac 2 app needs to be backgrounded and re-foregrounded before the UI shows the updated data.
Sometimes, an edit on Mac 2 will show on Mac 1 only after re-foregrounded but not show any didReceiveRemoteNotification on the Mac 1 console.
Sometimes, an edit on Mac 2 will not show at all on Mac 1 even after re-foregrounding the app.
Sometimes, no edits sync between either Mac.
I had read about how a few years back, there was a bug in macOS where testing iCloud sync between Macs did not work while running from Xcode but would work in TestFlight. For me, running my app in TestFlight on both Macs has never been able to sync any edits between the Macs.
Any idea where I might be going wrong. It seems this should not be this hard and should not be failing so inconsistently. Wondering what I might be doing wrong here.
I'm testing CloudKit Sharing (CKShare) in my app. My app uses CloudKit Sharing to share private data between users (this is not App Store Family Sharing or purchase sharing, it's app-level sharing via CKShare).
To properly test this, I need three or four Apple Accounts with distinct roles in my app. This means I need three/four separate iCloud accounts signed in on test devices. Simulators are probably ok:
two acting as "parents" (share owner and participant):
parent1.sandbox@example.com
parent2.sandbox@example.com,
one or two as a "child" (participant)
child1.sandbox@example.com
child2.sandbox@example.com
except obviously using my domain name.
I attempted to create Sandbox Apple Accounts in App Store Connect, but these don't appear to work with CloudKit Sharing. I then created several standard Apple Accounts, but I've now hit a limit — I believe my mobile number (used for two-factor authentication on the test accounts) has been flagged or rate-limited for account creation, and I can no longer create or verify new accounts with it.
It's also blocked the email addresses associated with those accounts from being used for new account creation.
Can Apple or anyone advise on the recommended approach for testing CloudKit Sharing with multiple participants?
are Sandbox accounts supposed to work for CKShare, or do I need full Apple Accounts?
How do i create and verify these in the correct way to avoid hitting these limits or breaking terms of service?
In the iOS 13 world, I had code like this:
class SceneDelegate: UIResponder, UIWindowSceneDelegate {
		func windowScene(_ windowScene: UIWindowScene, userDidAcceptCloudKitShareWith cloudKitShareMetadata: CKShare.Metadata) {
				// do stuff with the metadata, eventually call CKAcceptSharesOperation
		}
}
I am migrating my app to the new SwiftUI app lifecycle, and can’t figure out where to put this method. It used to live in AppDelegate pre-iOS13, and I tried going back to that, but the AppDelegate version never gets called.
There doesn’t seem to be a SceneDelegateAdaptor akin to UIApplicationDelegateAdaptor available, which would provide a bridge to the old code.
So, I’m lost. How do I accept CloudKit shares with SwiftUI app lifecycle? 🙈
I’m seeing an issue with CloudKit sharing in iOS 26.2.1:
When I call CKShare(rootRecord:) with a brand-new record in a fresh custom zone, the share is created with no root attached (rootFromShare == nil). After saving both the root and share in a single CKModifyRecordsOperation, fetching the share from the server still shows no root reference (rootRecordID == nil). No errors are thrown, but sharing simply fails.
Key facts:
• Custom zone created and confirmed (sharing enabled, capsRaw=7/15)
• Brand-new record type and fresh IDs each run
• Never reusing records or shares
• Saving both root and share together in one operation
• No default zone usage; always custom private zone
Tested:
• Multiple devices, iCloud accounts, and app versions
• Both simulator and physical device
Debug logs consistently show:
• SHARE_CREATE_SHARE_LOCAL ... rootFromShare=nil
• After save/fetch: rootRecordID=nil on server
Has anyone seen this? Is there a new CloudKit regression in iOS 26.x, or am I missing something subtle?
Minimal sample project and full debug logs available if needed.
Any insights or workarounds would be hugely appreciated!
I have implemented CKSyncEngine synchronization, and it works well. I can update data on one device and see the changes propagate to another device quickly. However, the initial sync when a user downloads the app on a new device is a significant issue for both me and my users.
One problem is that the sync engine fetches deletion events from the server. On a new device, the local database is empty, so these deletions are essentially no-ops. This would not be a big problem if there were only a few records or if it was fast. I measured the initial sync and found that there are 150 modified records and 62,168 deletions. Counting these alone takes over five minutes, even without processing them. The deletions do nothing because the local database has nothing to delete, yet they still add a significant delay.
I understand that the sync engine ensures consistency across all devices, but five minutes of waiting with the app open just to insert a small number of records is excessive. The problem would be worse if there were tens of thousands of new records to insert, since downloading and saving the data would take even longer.
This leads to a poor user experience. Users open the app and see data being populated for several minutes, or they are stuck on a screen that says the data is being synchronized with iCloud.
I am wondering if there is a way to make the sync engine ignore deletion events when the state serialization is nil. Alternatively, is there a recommended method for handling initial synchronization more efficiently?
One idea I considered is storing all the data as a backup in iCloud Documents, along with the state serialization at that point in time. When a user opens the app for the first time, I could download the file, extract the data, and set the state serialization to the saved value. I am not sure if this would work. I do not know if state serialization is tied to the device or if it only represents the point where the sync engine left off. My guess is that it might reference some local device storage.
I am not sure what else to try. I could fetch all data using CloudKit, create the sync engine with an empty state serialization, and let it fetch everything again, but that would still take a long time.
My records are very small, mostly a date when something happened and an ID referencing the parent. Since the app tracks watched episodes, I only store the date the user watched the episode and the ID of that episode.
When using Team “57AWJ345M2” and setting the project’s Bundle ID to “com.marsgame.fg2”, enabling the iCloud capability and checking Key-Value Storage results in the following error:"Provisioning profile "iOS Team Provisioning Profile: com.marsgame.fg2" doesn't match the entitlements file's value for the com.apple.developer.ubiquity-kvstore-identifier entitlement."
This issue does not occur if we use a different Team or a different Bundle ID.
The project was transferred from another Team to this new Team, and everything worked fine before the transfer.
Additionally, we have tried creating a brand new project, but as long as we use this same Team and this same Bundle ID, the error still occurs when enabling Key-Value Storage.
I'm using SwiftData with CloudKit private database. I was able to identify the error on my device by debugging in Xcode with com.apple.CoreData.SQLDebug flag. However, in Production, I couldn't find a way to get the cause of errors.
I tried inspecting the error coming from eventChangedNotification. The NSPersistentCloudKitContainer.Event error does not contain any underlying error (neither CKError.userInfo nor in NSError.underlyingError). It only reports a partial failure with CKErrorDomain code 2.
If a user encounter an error, there seems to be no way to retrieve the error details.
Is there any way to access the error details or logs in Production?
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
Console Output
Where you can see
Output Extract
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.
Hi all,
I have setup my app to use SwiftData with CloudKit sync. I have a production environment and development environment. I can reset the development environment for myself and all users in CloudKit console, but I can't reset the production one as it's tried to users' iCloud accounts, so I've added a button in-app for that feature. In the onboarding of my app, I pre-seed the DB with some default objects, which should be persisted between app install. The issue I'm running into is that I'm unable to force-pull these models from iCloud during the onboarding of a clean re-install, which leads to the models later appearing as duplicates once the user has been on the app for a few minutes and it has pulled from their iCloud account. If anyone has any suggestions on how to handle this issue, I would greatly appreciate it.
I have an iOS app (1Address) which allows users to share their address with family and friends using CloudKit Sharing.
Users share their address record (CKRecord) via a share link/url which when tapped allows the receiving user to accept the share and have a persistent view into the sharing user's address record (CKShare).
However, most users when they recieve a sharing link do not have the app installed yet, and so when a new receiving user taps the share link, it prompts them to download the app from the app store.
After the new user downloads the app from the app store and opens the app, my understanding is that the system (iOS) will/should then vend to my app the previously tapped cloudKitShareMetadata (or share url), however, this metadata is not being vended by the system. This forces the user to re-tap the share link and leads to some users thinking the app doesn't work or not completing the sharing / onboarding flow.
Is there a workaround or solve for this that doesn't require the user to tap the share link a second time?
In my scene delegate I am implementing:
func scene(_ scene: UIScene, willConnectTo session: UISceneSession, options connectionOptions: UIScene.ConnectionOptions) {...}
And also
func scene(_ scene: UIScene, continue userActivity: NSUserActivity) {...}
And also:
func windowScene(_ windowScene: UIWindowScene, userDidAcceptCloudKitShareWith cloudKitShareMetadata: CKShare.Metadata) {...}
And:
func scene(_ scene: UIScene, openURLContexts URLContexts: Set<UIOpenURLContext>) {...}
Unfortunately, none of these are called or passed metadata on the initial app run after install. Only after the user goes back and taps a link again can they accept the share.
This documentation: https://developer.apple.com/documentation/cloudkit/ckshare says that adding the CKSharingSupported key to your app's Info.plist file allows the system to launch your app when a user taps or clicks a share URL, but it does not clarify what should happen if your app is being installed for the first time.
This seems to imply that the system is holding onto the share metadata and/or url, but for some reason it is not being vended to the app on first run.
Open to any ideas here for how to fix and I also filed feedback: FB20934189.
I have SwiftData models containing arrays of Codable structs that worked fine before adding CloudKit capability. I believe they are the reason I started seeing errors after enabling CloudKit.
Example model:
@Model
final class ProtocolMedication {
var times: [SchedulingTime] = [] // SchedulingTime is Codable
// other properties...
}
After enabling CloudKit, I get this error logged to the console:
'NSKeyedUnarchiveFromData' should not be used to for un-archiving and will be removed in a future release
CloudKit Console shows this times data as "plain text" instead of "bplist" format.
Other struct/enum properties display correctly (I think) as "bplist" in CloudKit Console.
The local SwiftData storage handled these arrays fine - this issue only appeared with CloudKit integration.
What's the recommended approach for storing arrays of Codable structs in SwiftData models that sync with CloudKit?
I have a new app I am working on, it uses, a container id like com.me.mycompany.FancyApp.prod, the description in the app is My Fancy App. When I deploy the app via TestFlight on a real device, the sync seems to work, but when I view iCloud->Storage-List, I see my app icon, and the name "prod". Where did the name prod come from? It should be My Fancy App, which is the actual name of the App.
I was wondering what the recommended way is to persist user settings with SwiftData?
It seems the SwiftData API is focused around querying for multiple objects, but what if you just want one UserSettings object that is persisted across devices say for example to store the user's age or sorting preferences.
Do we just create one object and then query for it or is there a better way of doing this?
Right now I am just creating:
import SwiftData
@Model
final class UserSettings {
var age: Int = 0
var sortAtoZ: Bool = true
init(age: Int = 0, sortAtoZ: Bool = true) {
self.age = age
self.sortAtoZ = sortAtoZ
}
}
In my view I am doing as follows:
import SwiftUI
import SwiftData
struct SettingsView: View {
@Environment(\.modelContext) var context
@Query var settings: [UserSettings]
var body: some View {
ForEach(settings) { setting in
let bSetting = Bindable(setting)
Toggle("Sort A-Z", isOn: bSetting.sortAtoZ)
TextField("Age", value: bSetting.age, format: .number)
}
.onAppear {
if settings.isEmpty {
context.insert(UserSettings(age: 0, sortAtoZ: true))
}
}
}
}
Unfortunately, there are two issues with this approach:
I am having to fetch multiple items when I only ever want one.
Sometimes when running on a new device it will create a second UserSettings while it is waiting for the original one to sync from CloudKit.
AppStorage is not an option here as I am looking to persist for the user across devices and use CloudKit syncing.
I have a SwiftData flashcard app which I am syncing with CloudKit using NSPersistentCloudKitContainer. While syncing itself is working perfectly, I have noticed a dramatic increase in the app size after enabling sync.
Specifically, without CloudKit, 15k flashcards results in the default.store file being about 4.5 MB. With CloudKit, default.store is about 67 MB. I have inspected the store and found that most of this increase is due to the ANSCKRECORDMETADATA table.
My question is, does implementing CloudKit normally cause this magnitude of increase in storage? If it doesn’t, is there something in my model, schema, implementation, etc. that could be causing it?
Below are two other posts describing a similar issue, but neither with a solution. I replied to the first one about a month ago. I then submitted this to Developer Technical Support, but was asked to post my question in the forums, so here it is.
Strange behavior with 100k+ records in NSPersistentCloudKitContainer
Huge increase in sqlite file size after adopting CloudKit