I've got an application built on top of SwiftData (+ CloudKit) which is published to App Store.
I've got a problem where on each app update, the data saved in the database is duplicated to the end user.
Obviously this isn't wanted behaviour, and I'm really looking forward to fixing it. However, given the restrictions of SwiftData, I haven't found a single fix for this.
The data duplication happens automatically on the first initial sync after the update. My guess is that it's because it doesn't detect the data already in the device, so it pulls all data from iCloud and appends it to the database where data in reality exists.
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
200 Posts
Sort by:
Post
Replies
Boosts
Views
Activity
Hi, I'm working on a macOS app that utilizes SwiftData to save some user generated content to their private databases.
It is not clear to me at which point the app I made starts using the production database. I assumed that if I produce a Release build that it will be using the prod db, but that doesn't seem to be the case.
I made the mistake of distributing my app to users before "going to prod" with CloudKit. So after I realized what I had done, I inspected my CloudKit dashboard and records and I found the following:
For my personal developer account the data is saved in the Developer database correctly and I can inspect it.
When I use the "Act as iCloud account" feature and use one of my other accounts to inspect the data, I notice that for the other user, the data is neither in the Development environment nor the Production environment. Which leads me to believe it is only stored locally on that user's machine, since the app does in fact work, it's just not syncing with other devices of the same user.
So, my question is: how do I "deploy to production"?
I know that there is a Deploy Schema Changes button in the CloudKit dashboard. At which point should I press that? If I press it now, before distributing a new version of my app, will that somehow "signal" the already running apps on user's machines to start using the Production database?
Is there a setting in Xcode that I need to check for my Release build, so that the app does in fact start using the production db?
Is there a way to detect in the code whether the app is using the Production database or not? It would be useful so I can write appropriate migration logic, since I don't want to loose existing data users already have saved locally.
Something I want to know
and all users of CKSyncEngine care about
I'm going to build a full stack solution using CKSyncEngine, but what's the near future and the support and maintenance priorities inside Apple for CKSyncEngine?
There is only one short video for CKSyncEngine, in 2023, no updates after that, no future plans mentioned. I'm worried that this technology be deprecated or not activity maintained. This is a complex technology, without being activity maintained (or open-sourced) there will be fatal production issues we the developer cannot solve.
The CK developer in the video stated that "many apps" were using the framework, but he did not list any. The only named is NSUbiquitousKeyValueStore, but NSUbiquitousKeyValueStore is too simple a use case. I wonder is apple's Notes.app using it, or going to use it? Is SwiftData using it?
API Problems
The API design seems a little bit tricky, not designed from a user's perspective.
handleEvent doesn't contain any context information about which batch. How do I react the event properly? Let's say our sync code and CKSyncEngine, and callbacks are all on a dedicated thread.
Consider this:
in nextRecordZoneChangeBatch you provided a batch of changes, let's call this BATCH 1, including an item in database with uuid "***" and name "yyy"
before the changes are uploaded, there are new changes from many OTHER BACKGROUND THREADS made to the database. item "***"'s name is now "zzz"
handle SentRecordZoneChanges event: I get records that uploaded or failed, but I don't know which BATCH the records belong to.
How do I decide if i have to mark "***" as finished uploading or not? If I mark *** as finished that's wrong, the new name "zzz" is not uploaded.
I have to compare every field of *** with the savedRecord to decide if I finished uploading or not? That is not acceptable as the performance and memory will be bad.
Other questions
I have to add recordIDs to state, and wait for the engine to react. I don't think this is a good idea, because recordID is a CloudKit concept, and what I want to sync is a local database. I don't see any rational for this, or not documented. If the engine is going to ask for a batch of records, you can get all record ids from the batch?
I have integrated CloudKit into a CoreData application and am ready to deploy the schema to production but keep getting an "internal error" when trying to deploy to production or reset my CloudKit environment. I have attached images of what I am seeing including one of the console error. Is there any way to resolve this?
I am using NSPersistentCloudKitContainer and I decided to add a property to an entity. I accidentally ran try! container.initializeCloudKitSchema(options: []) while using the production container in Xcode (com.apple.developer.icloud-container-environment) which throw a couple of errors and created some FAKE_ records in my production container.
So I changed to my development container and ran the try! container.initializeCloudKitSchema(options: []) and now it succeeded.
After that I cleaned up the FAKE_ records scattered in production container but in Xcode when I'm running I now get these logs in the console (and I can't seem to get rid of them):
error: CoreData+CloudKit: -[NSCloudKitMirroringDelegate _importFinishedWithResult:importer:](1398): <PFCloudKitImporter: 0x300cc72c0>: Import failed with error:
Error Domain=NSCocoaErrorDomain Code=4864 "*** -[NSKeyedUnarchiver _initForReadingFromData:error:throwLegacyExceptions:]: incomprehensible archive (0x53, 0x6f, 0x6d, 0x65, 0x20, 0x73, 0x61, 0x6d)" UserInfo={NSDebugDescription=*** -[NSKeyedUnarchiver _initForReadingFromData:error:throwLegacyExceptions:]: incomprehensible archive (0x53, 0x6f, 0x6d, 0x65, 0x20, 0x73, 0x61, 0x6d)}
error: CoreData+CloudKit: -[NSCloudKitMirroringDelegate recoverFromError:](2310): <NSCloudKitMirroringDelegate: 0x302695770> - Attempting recovery from error: Error Domain=NSCocoaErrorDomain Code=4864 "*** -[NSKeyedUnarchiver _initForReadingFromData:error:throwLegacyExceptions:]: incomprehensible archive (0x53, 0x6f, 0x6d, 0x65, 0x20, 0x73, 0x61, 0x6d)" UserInfo={NSDebugDescription=*** -[NSKeyedUnarchiver _initForReadingFromData:error:throwLegacyExceptions:]: incomprehensible archive (0x53, 0x6f, 0x6d, 0x65, 0x20, 0x73, 0x61, 0x6d)}
error: CoreData+CloudKit: -[NSCloudKitMirroringDelegate _recoverFromError:withZoneIDs:forStore:inMonitor:](2620): <NSCloudKitMirroringDelegate: 0x302695770> - Failed to recover from error: NSCocoaErrorDomain:4864
Recovery encountered the following error: (null):0
error: CoreData+CloudKit: -[NSCloudKitMirroringDelegate resetAfterError:andKeepContainer:](610): <NSCloudKitMirroringDelegate: 0x302695770> - resetting internal state after error: Error Domain=NSCocoaErrorDomain Code=4864 "*** -[NSKeyedUnarchiver _initForReadingFromData:error:throwLegacyExceptions:]: incomprehensible archive (0x53, 0x6f, 0x6d, 0x65, 0x20, 0x73, 0x61, 0x6d)" UserInfo={NSDebugDescription=*** -[NSKeyedUnarchiver _initForReadingFromData:error:throwLegacyExceptions:]: incomprehensible archive (0x53, 0x6f, 0x6d, 0x65, 0x20, 0x73, 0x61, 0x6d)}
error: CoreData+CloudKit: -[NSCloudKitMirroringDelegate _requestAbortedNotInitialized:](2198): <NSCloudKitMirroringDelegate: 0x302695770> - Never successfully initialized and cannot execute request '<NSCloudKitMirroringExportRequest: 0x303a52d00> 548CB420-E378-42E5-9607-D23E7A2A364D' due to error: Error Domain=NSCocoaErrorDomain Code=4864 "*** -[NSKeyedUnarchiver _initForReadingFromData:error:throwLegacyExceptions:]: incomprehensible archive (0x53, 0x6f, 0x6d, 0x65, 0x20, 0x73, 0x61, 0x6d)" UserInfo={NSDebugDescription=*** -[NSKeyedUnarchiver _initForReadingFromData:error:throwLegacyExceptions:]: incomprehensible archive (0x53, 0x6f, 0x6d, 0x65, 0x20, 0x73, 0x61, 0x6d)}
Hello,
We’re experiencing an issue in our app where a subset of users are being logged out automatically without any apparent reason. This is impacting both iOS 16 and iOS 17 users (though it’s hard to confirm if it’s platform-specific). Here’s a breakdown of the situation:
Symptoms
Some users report being logged out of the app randomly, even after remaining active.
The issue doesn’t seem to affect all users—just a specific subset.
Users are prompted to log in again, and once they do, the app behaves as expected until the issue recurs.
Here is my sample code of integration with iCloud
let dbName = "GroceryItem1"
let fieldName = "todoTitle1"
@objc func saveItem(name: String) {
let record = CKRecord(recordType: dbName)
record.encryptedValues[fieldName] = name
database.save(record) { record, error in
if error == nil {
DispatchQueue.main.asyncAfter(deadline: .now() + 2) {
self.fetchItems()
}
} else {
print("Error saving item: \(error?.localizedDescription ?? "Unknown error")")
}
}
}
Now my code works fine but what i don't understand is when i login to icloud.developer.apple.com why am i able to see the data, why is it not encrypted?
My database is private as well
If i go to record types i can see the recordfield is encrypted as shown in below screenshot
When attempting to deploy schema changes in the iCloudKit Database by clicking the Deploy Schema Changes button, a Confirm Deployment dialog appears, showing an error: “Internal error”. The following error details were observed in the JavaScript console:
• description: “The request has failed due to an error.”
• headers: undefined
• message: “Known response error: The request has failed due to an error.”
• result:
• code: 400
• detailedMessage: undefined
• message: “bad-request”
• reason: “Internal error”
• redirectUrl: undefined
• requestUuid: “0c5b4af2-15c9-425f-87ea-************”
• retryAfterSeconds: undefined
I really don't understand what kind of cyber crap CloudKit is!
In macOS, CloudKit basically doesn’t work properly
I am trying to implement record sharing in my project, but when I try to copy the link on the UICloudSharingController, the sheet closes and the link doesn't get copied.
My CloudKitManager function:
public func shareTeam(_ team: Team) -> AnyPublisher<CKShare, Error> {
Future { [weak self] promise in
guard let self = self else {
promise(.failure(CloudKitError.unknown))
return
}
let record = team.toCKRecord()
let share = CKShare(rootRecord: record)
share[CKShare.SystemFieldKey.title] = "Join \(team.name)" as CKRecordValue
share.publicPermission = .readWrite
let operation = CKModifyRecordsOperation(recordsToSave: [record, share], recordIDsToDelete: nil)
operation.savePolicy = .ifServerRecordUnchanged
operation.qualityOfService = .userInitiated
operation.modifyRecordsResultBlock = { result in
switch result {
case .success:
promise(.success(share))
case .failure(let error):
promise(.failure(error))
}
}
self.privateDatabase.add(operation)
}
.eraseToAnyPublisher()
}
ViewModel function:
func shareTeam() {
guard let selectedTeam = selectedTeam else { return }
CloudKitManager.shared.shareTeam(selectedTeam)
.receive(on: DispatchQueue.main)
.sink { [weak self] completion in
switch completion {
case .finished:
break
case .failure(let error):
self?.didError = true
self?.error = error
}
} receiveValue: { share in
let sharePresenter = SharePresenter(
share: share,
container: CloudKitManager.shared.container,
teamName: selectedTeam.name,
rootRecord: selectedTeam.toCKRecord()
)
sharePresenter.presentShareSheet()
}
.store(in: &cancellables)
}
I've been searching all over the web trying to find the proper way to get all records created by a specific user in CloudKit.
I am able to get the correct id using:
guard let userRecordID = try? await container.userRecordID() else { return }
I can see that the id returned is associated with records in my CloudKit dashboard. So I would expect that the following would get those records:
let predicate = NSPredicate(format: "%K == %@", #keyPath(CKRecord.creatorUserRecordID), userRecordID)
let query = CKQuery(recordType: "CKUser", predicate: predicate)
But instead when I use that query it returns nothing. It is successful but with nothing returned...
Any ideas why this would be happening?
P.S. I have also tried constructing the predicate using the reference, but I get the same result - success with no results.
P.S.2 Also worth mentioning that I am trying to get the results from the public database and I have set my CKContainer to the correct container id.
Something has caused my CloudKit queries to fail. On the dashboard I get an error message "Failed to execute query" when I try to "SORT BY" a field. The field is listed under Indexes as "sortable". For a different field, when I enter the field under "FILTER BY", and before I tap "Query", I get "No results". That field is listed under the Indexes as "queryable".
It used to work fine.
I have described this further, with screenshots at FB16114560
First query works fine. If next query comes within 30 seconds of first query it generates a CKErrorRequestRateLimited (error.code==7) error telling me to try after a time such that there would be 30 seconds between queries.
<CKError 0x30299dd70: "Request Rate Limited" (7/2061); "Operation throttled by previous server http 503 reply. Retry after 20.5 seconds. (Other operations may be allowed.)"; Retry after 20.5 seconds>
I get this red warning in Xcode every time my app is syncing to the iCloud. My model has only basic types and enum that conform to Codable so i'm not sure what is the problem.
App is working well, synchronization works. But the warning doesn't look good.
Maybe someone has idea how to debug it.
Hi everyone. I created test-apps years ago. Now I looked into https://icloud.developer.apple.com/dashboard/
There I found these old apps listed.
How do I delete these Apps in the Console and entirely from my account?
I don't have the project files anymore.
All the best! 😊
Hi everyone,
I have an application that allows to share Core Data records through CKShare.
If I compile the app in debug or release mode on my devices with Xcode the Sharing functionality work like a charm, but if I download the application from App Store doesn't work, It seems that can't generate the link for sharing.
Does anyone have any idea why?
Thanks
Looking at my CloudKit Telemetry console I noticed a significant increase in 'Other' errors recently. These errors are impacting user experience and I really don't know how to better understand the issues that may be occurring due to the "other" category. If I query the logs for "other" errors, only 2 results show up for the week. There are 2500+ errors in the telemetry graph (see attached).
Is anyone else experiencing this or does anyone have a suggestion on how I can better understand this issue? Thank you!
Hi,
I'm building a habit tracking app for iOS and macOS. I want to use up to date technologies, so I'm using SwiftUI and SwiftData.
I want to store user data locally on device and also sync data between device and iCloud server so that the user could use the app conveniently on multiple devices (iPhone, iPad, Mac).
I already tried SwiftData + NSPersistentCloudKitContainer, but I need to control when to sync data, which I can't control with NSPersistentCloudKitContainer. For example, I want to upload data to server right after data is saved locally and download data from server on every app open, on pull-to-refresh etc. I also need to monitor sync progress, so I can update the UI and run code based on the progress. For example, when downloading data from server to device is in progress, show "Loading..." UI, and when downloading finishes, I want to run some app business logic code and update UI.
So I'm considering switching from NSPersistentCloudKitContainer to CKSyncEngine, because it seems that with CKSyncEngine I can control when to upload and download data and also monitor the progress.
My database schema (image below) has relationships - "1 to many" and "many to many" - so it's convenient to use SwiftData (and underlying CoreData).
Development environment: Xcode 16.1, macOS 15.1.1
Run-time configuration: iOS 18.1.1, macOS 15.1.1
My questions:
1-Is it possible to use SwiftData for local data storage and CKSyncEngine to sync this local data storage with iCloud?
2-If yes, is there any example code to implement this?
I've been studying the "CloudKit Samples: CKSyncEngine" demo app (https://github.com/apple/sample-cloudkit-sync-engine), but it uses a very primitive approach to local data storage by saving data to a JSON file on disk.
It would be very helpful to have the same demo app with SwiftData implementation!
3-Also, to make sure I don't run into problems later - is it okay to fire data upload (sendChanges) and download (fetchChanges) manually with CKSyncEngine and do it often? Are there any limits how often these functions can be called to not get "blocked" by the server?
4-If it's not possible to use SwiftData for local data storage and CKSyncEngine to sync this local data storage with iCloud, then what to use for local storage instead of SwiftData to sync it with iCloud using CKSyncEngine? Maybe use SwiftData with the new DataStore protocol instead of the underlying CoreData?
All information highly appreciated!
Thanks,
Martin
So the App Store got deleted or something after I got iOS 18 so then I asked Siri to open it for me and it said un supported URL
I have already built an app using SwiftData and now I want to introduce CloudKit features in it, what should I do? I'm totally new to this part. Thank you!