I have a CKRecord that references an CKAsset. If I understand it correctly, CKSyncEngine would download the asset every time the record has changed on the server. (Of course it would try to use the local asset cache, but worst-case it might be already flushed)
The documentation for CKAsset says that asset downloads can be prevented by limiting the requested record keys using the desiredKeys property on the fetch operation. But I don't see any possibility to set this property when using CKSyncEngine.
Did I miss something? Are there any alternatives?
iCloud & Data
RSS for tagLearn how to integrate your app with iCloud and data frameworks for effective data storage
Selecting any option will automatically load the page
Post
Replies
Boosts
Views
Activity
I get the following fatal error when the user clicks Save in AddProductionView.
Fatal error: Duplicate keys of type 'AnyHashable' were found in a Dictionary. This usually means either that the type violates Hashable's requirements, or that members of such a dictionary were mutated after insertion.
As far as I’m aware, SwiftData automatically makes its models conform to Hashable, so this shouldn’t be a problem.
I think it has something to do with the picker, but for the life of me I can’t see what.
This error occurs about 75% of the time when Save is clicked.
I'm using Xcode 16.2 and iPhone SE 2nd Gen. Any help would be greatly appreciated…
Here is my code:
import SwiftUI
import SwiftData
@main
struct MyApp: App {
var body: some Scene {
WindowGroup {
ContentView()
.modelContainer(for: Character.self, isAutosaveEnabled: false)
}
}
}
@Model
final class Character {
var name: String
var production: Production
var myCharacter: Bool
init(name: String, production: Production, myCharacter: Bool = false) {
self.name = name
self.production = production
self.myCharacter = myCharacter
}
}
@Model
final class Production {
var name: String
init(name: String) {
self.name = name
}
}
struct ContentView: View {
@State private var showingSheet = false
var body: some View {
Button("Add", systemImage: "plus") {
showingSheet.toggle()
}
.sheet(isPresented: $showingSheet) {
AddProductionView()
}
}
}
struct AddProductionView: View {
@Environment(\.dismiss) private var dismiss
@Environment(\.modelContext) var modelContext
@State var production = Production(name: "")
@Query var characters: [Character]
@State private var characterName: String = ""
@State private var selectedCharacter: Character?
var filteredCharacters: [Character] {
characters.filter { $0.production == production }
}
var body: some View {
NavigationStack {
Form {
Section("Details") {
TextField("Title", text: $production.name)
}
Section("Characters") {
List(filteredCharacters) { character in
Text(character.name)
}
HStack {
TextField("Character", text: $characterName)
Button("Add") {
let newCharacter = Character(name: characterName, production: production)
modelContext.insert(newCharacter)
characterName = ""
}
.disabled(characterName.isEmpty)
}
if !filteredCharacters.isEmpty {
Picker("Select your role", selection: $selectedCharacter) {
Text("Select")
.tag(nil as Character?)
ForEach(filteredCharacters) { character in
Text(character.name)
.tag(character as Character?)
}
}
.pickerStyle(.menu)
}
}
}
.toolbar {
Button("Save") { //Fatal error: Duplicate keys of type 'AnyHashable' were found in a Dictionary. This usually means either that the type violates Hashable's requirements, or that members of such a dictionary were mutated after insertion.
if let selectedCharacter = selectedCharacter {
selectedCharacter.myCharacter = true
}
modelContext.insert(production)
do {
try modelContext.save()
} catch {
print("Failed to save context: \(error)")
}
dismiss()
}
.disabled(production.name.isEmpty || selectedCharacter == nil)
}
}
}
}
I'm using NSPersistentCloudKitContainer to save, edit, and delete items, but it only works half of the time. When I delete an item and terminate the app and repoen, sometimes the item is still there and sometimes it isn't. The operations are simple enough:
moc.delete(thing)
try? moc.save()
Here is my DataController. I'm happy to provide more info as needed
class DataController: ObservableObject {
let container: NSPersistentCloudKitContainer
@Published var moc: NSManagedObjectContext
init() {
container = NSPersistentCloudKitContainer(name: "AppName")
container.loadPersistentStores { description, error in
if let error = error {
print("Core Data failed to load: \(error.localizedDescription)")
}
}
#if DEBUG
do {
try container.initializeCloudKitSchema(options: [])
} catch {
print("Error initializing CloudKit schema: \(error.localizedDescription)")
}
#endif
moc = container.viewContext
}
}
My Code:
let op = CKModifyRecordsOperation(recordIDsToDelete:recordIDsToDelete)
op.modifyRecordsCompletionBlock = { _, deleteRecordIDs, error in
if error == nil {
print("successful delete deleteRecordIDS = \(deleteRecordIDs)")
} else {
print("delete error = \(error?.localizedDescription)")
}
}
op.database = CKContainer.default().privateCloudDatabase
op.qualityOfService = .userInitiated
CKContainer.default().privateCloudDatabase.add(op)
My problem is that CKRecord are not deleted once I reinstall the app: when I reinstall the app and try to delete a CloudKit record, the method is executed successfully (error is nil) but the records are still in CloudKit Dashboards.
Topic:
App & System Services
SubTopic:
iCloud & Data
Tags:
CloudKit
CloudKit Dashboard
CloudKit Console
In a CloudKit private database, the Owner creates a custom zone and performs the following actions:
Creates CKRecord1 with CKShare1 and invites Participant1 to it.
Creates CKRecord2 with CKShare2 and invites Participant2 to it.
Creates CKRecordShared, which should be accessible to both Participant1 and Participant2.
How can I achieve step 3?
I observed that:
Setting a regular reference from CKRecord1 (or CKRecord2) to CKRecordShared does not automatically make CKRecordShared accessible to Participant1 (or Participant2).
CKRecordShared can only have one parent, so it cannot be directly linked via parent reference to both Participant1 and Participant2 at the same time.
One potential solution I see is to have the Owner create a separate CKShare for CKRecordShared and share it explicitly with each participant. However, this approach could lead to user errors, as it requires careful management of multiple shares for each participant.
Is there a better way to handle this scenario, ensuring that CKRecordShared is accessible to multiple participants without introducing unnecessary complexity or potential errors?
I am attempting to migrate a cloudkit module that calls on manual cloudkit methods for fetching record zone changes, modifying records, etc to one that utilizes CKSyncEngine. I've got a basic implementation working with just a create method for one of my data models, however it seems like the sync engine keeps calling sync events on the same pending changes.
Here is my current flow:
The user will hit some button that lets them fill out a form to create a data model.
The user saves the form. This triggers a method that takes the resulting data model and queues it to the sync engine's state (engine.state.add(pendingRecordZoneChanges: pendingChanges)
I have my delegate method nextRecordZoneChangeBatch(_ context:...) implemented where it fetches the corresponding data model using the record ID and returns a batch containing the corresponding populated record from the data model.
I have the handleEvent(_ event:...) delegate method implemented where I handle both .fetchRecordZoneChanges and .sentRecordZoneChanges. I have set up .sentRecordZoneChanges to merge the server record into my local record (and persisted locally) so that the record change tags are the same.
After this last portion, it seems that the sync engine continues to keep pushing syncs/updates and I end up with numerous handleEvent(_ event:) calls that keep returning savedRecords (and occasionally failedRecordSaves).
Am I missing some step to remove the record from the changes after the sync engine recognizes that I have properly saved the record to the server?
My macOS app is developed using SwfitUI, SwiftData, and CloudKit. In the development environment, CloudKit works well. Locally added models can be quickly viewed in the CloudKit Console. macOS app and iOS app with the same BundleID can also synchronize data normally when developing locally. However, in the production environment, the macOS app cannot synchronize data with iCloud. But iOS app can. The models added in the production environment are only saved locally and cannot be viewed in CloudKit Console Production.
I am sure I have configured correctly, container schema changes to deploy to the Production environment. I think there may be a problem with CloudKit in macOS.
Please help troubleshoot the problem. I can provide you with any information you need.
var body: some Scene {
WindowGroup {
MainView()
.frame(minWidth: 640, minHeight: 480)
.environment(mainViewModel)
}
.modelContainer(for: [NoteRecord.self])
}
I didn't do anything special. I didn’t do anything special. I just used SwiftData hosted by CloudKit.
I'm trying to add Cloud Kit integration to SwiftData app (that is already in the App Store, btw).
When the app is installed on devices that are directly connected to Xcode, it works (a bit slow, but pretty well).
But when the app is distributed to Testflight internal testers, the synchronization doesn't happen at all.
So, is this situation normal and how can I test apps with iCloud integration properly?
Users have been reporting that the TestFlight version of my app (compiled with Xcode 26 Beta 6 17A5305f) is sometimes crashing on startup. Upon investigating their ips files, it looks like Core Data is locking up internally during its initialization, resulting in iOS killing my app. I have not recently changed my Core Data initialization logic, and it's unclear how I should proceed.
Is this a known issue? Any recommended workaround? I have attached the crash stack below.
Thanks!
crash_log.txt
It takes a few seconds, sometimes a few minutes for records to be downloaded back from CloudKit when the user reinstalls the app, which leads users to thinking their data was lost. I would like to know if there’s any way to provide a visual feedback about the current CloudKit sync status so I can let users know their data is being in fact downloaded back to their devices.
[Submitted as FB14860454, but posting here since I rarely get responses in Feedback Assistant]
In a simple SwiftData app that adds items to a list, memory usage drastically increases as items are added. After a few hundred items, the UI lags and becomes unusable.
In comparison, a similar app built with CoreData shows only a slight memory increase in the same scenario and does NOT lag, even past 1,000 items.
In the SwiftData version, as each batch is added, memory spikes the same amount…or even increases! In the CoreData version, the increase with each batch gets smaller and smaller, so the memory curve levels off.
My Question
Are there any ways to improve the performance of adding items in SwiftData, or is it just not ready for prime time?
Example Projects
Here are the test projects on GitHub if you want to check it out yourself:
PerfSwiftData
PerfCoreData
SwiftData ModelContainer instances don't seem to have a value for setting the Data Protection class.
Is the best way to set that by setting the Data Protection in the app capabilities? Is that the only way?
I have a need for log data that would be "Complete unless open" and user data that would be "Complete", but how do I change one of the containers data protection class?
Is it possible to reset SwiftData to a state identical to that of a newly installed app?
I have experienced some migration issues where, when I add a new model, I need to reinstall the entire application for the ModelContainer creation to work.
Deleting all existing models does not seem to make any difference.
A potential solution I currently have, which appears to work but feels quite hacky, is as follows:
let _ = try! ModelContainer()
modelContainer = try! ModelContainer(for: Student.self, ...)
This seems to force out this error CoreData: error: Error: Persistent History (66) has to be truncated due to the following entities being removed: (...) which seems to reset SwiftData.
Any other suggestions?
I work on an app that saves data to the Documents folder in the users iCloud Drive. This uses the iCloud -> iCloud Documents capability with a standard container.
We've noticed an issue where a user will delete the apps data by doing to Settings > {Name} > iCloud > Storage > App Name > select "delete data from iCloud", and then our app can no longer write to or create the Documents folder.
Once that happens, we get this error:
Error Domain=NSCocoaErrorDomain Code=513 "You don't have permission to save the file "Documents" in the folder "iCloud~your~bundle~identifier"." UserInfo={NSFilePath=/private/var/mobile/Library/Mobile Documents/iCloud~your~bundle~identifier/Documents, NSURL=file:///private/var/mobile/Library/Mobile%20Documents/iCloud~your~bundle~identifier/Documents, NSUnderlyingError=0x1102c7ea0 {Error Domain=NSPOSIXErrorDomain Code=13 "Permission denied"}}
This is reproducible using the sample project here https://developer.apple.com/documentation/uikit/synchronizing-documents-in-the-icloud-environment.
Steps to reproduce in that project:
Tap the plus sign in the top right corner to create a new document
Add a document name and tap "Save to Documents"
Go to Settings > {Name} > iCloud > Storage > SimpleiCloudDocument App Name > select "delete data from iCloud"
Reopen the app and repeat steps 1-2
Observe error on MainViewController+Document.swift:59
Deleting and reinstalling the app doesn't seem to help.
Topic:
App & System Services
SubTopic:
iCloud & Data
I've noticed several crashes that look like they're caused by an index out of bound in internal methods of NSFetchedResultsController. This happens while changes are merged from the persistent store container into the view context. Here's an example of the last exception backtrace.
Exactly which internal methods that are called in - [NSFetchedResultsController(PrivateMethods) _core_managedObjectContextDidChange:] vary between crash reports but they all end up crashing from _NSArrayRaiseBoundException.
The Core Data stack consists of one persistent store, one persistent store coordinator that the view context is set up to automatically merge changes from, and data is saved to disk from background context.
persistentContainer.loadPersistentStores(...)
viewContext = persistentContainer.viewContext
viewContext.automaticallyMergesChangesFromParent = true
backgroundContext = persistentContainer.newBackgroundContext()
backgroundContext.mergePolicy = NSMergeByPropertyObjectTrumpMergePolicy
backgroundClientContext = persistentContainer.newBackgroundContext()
backgroundClientContext.mergePolicy = NSMergeByPropertyObjectTrumpMergePolicy
Does anyone have any ideas what could be causing this? Thankful for any ideas or advice on how to investigate further.
I'm working on a macOS app with a Safari web extension. I'm trying to share a SwiftData model between devices using CloudKit synchronization. I am able to get synchronization in the main app on the same device, CloudKit sync works correctly — changes appear in the CloudKit Dashboard under com.apple.coredata.cloudkit.zone.
However, in the Safari App Extension, data is saved locally and persists across launches, but never syncs to CloudKit.
I have followed the recommended practices for configuring the App Group and entitlements, but the issue persists.
Questions:
Is there an official limitation preventing Safari App Extensions from connecting to the CloudKit daemon (cloudd)?
If not, what entitlements or configuration changes are required for a Safari App Extension to successfully sync with CloudKit?
Is the xpc_error=159 from bootstrap_look_up() a known sandbox restriction for this extension type?
Any guidance from Apple engineers or others who have successfully used CloudKit from a Safari App Extension would be appreciated.
What I’ve confirmed:
The extension’s .entitlements includes:
com.apple.security.app-sandbox
com.apple.developer.icloud-services
CloudKit
com.apple.developer.icloud-container-identifiers
iCloud.dev.example.myapp
Same iCloud container ID for both app and extension
CloudKit container exists and is initialized in CloudKit Console
Running in :Sandbox environment during development
Database name in SwiftData matches container identifier (without the iCloud. prefix)
The extension’s codesign output shows correct entitlements
App Group is configured (although in this case, extension and app use separate stores intentionally)
Observed behavior in Console.app logs:
CloudKit sync engine initializes in the extension
XPC activities are registered for import/export:
_xpc_activity_register: com.apple.coredata.cloudkit.activity.export.
xpc_activity_set_criteria: ... import.
Then a bootstrap lookup fails:
failed to do a bootstrap look-up: xpc_error=[159: Unknown error: 159]
CloudKit daemon connection error:
CKErrorDomain Code=6 "Error connecting to CloudKit daemon"
NSCocoaErrorDomain Code=4099
There is no “Will attempt to upload transactions” or “Upload succeeded” logs are ever seen.
Symptoms
When the extension is run, I see logs like the following in Console.app:
[0x13e215820] failed to do a bootstrap look-up: xpc_error=[159: Unknown error: 159]
CoreData+CloudKit: -[PFCloudKitSetupAssistant _checkAccountStatus:]_block_invoke(342): Fetched account info for store : (null)
Error Domain=CKErrorDomain Code=6 "Error connecting to CloudKit daemon. This could happen for many reasons..."
I have a ModelActor that creates a hierarchy of models and returns a PersistentIdentifier for the root. I'd like to do that in a transaction, but I don't know of a good method of getting that identifier if the models are created in a transaction.
For instance, an overly simple example:
func createItem(timestamp: Date) throws -> PersistentIdentifier {
try modelContext.transaction {
let item = Item(timestamp: timestamp)
modelContext.insert(item)
}
// how to return item.persistentModelID?
}
I can't return the item.persistentModelID from the transaction closure and even if I could, it will be a temporary ID until after the transaction is executed.
I can't create the Item outside the transaction and just have the transaction do an insert because swift will raise a data race error if you then try to return item.persistentModelID.
Is there any way to do this besides a modelContext.fetch* with separate unique identifiers?
LSUB always returns all the subscribed folders. For example
lsub "" "test/*"
returns a list of all the folders and not just subscribed folders that are subfolders of test. I.e, it returns the same folder list as
lsub "" "*".
For more details please see https://bugzilla.mozilla.org/show_bug.cgi?id=1817707#c15
When a user first downloads my application they are prompted to sign into their apple account via a pop up.
I have not had this pop up previously, I believe the change occurred after iOS18.
I have functions that do a few things:
Retrieves userRecordID
Retrieves a userprofile(via userrecordid) from cloudkit.
I have a simple model
@Model
final class Movie: Identifiable {
#Index\<Movie\>(\[.name\])
var id = UUID()
var name: String
var genre: String?
init(name: String, genre: String?) {
self.name = name
self.genre = genre
}
}
I turned on SQL debugging by including '-com.apple.CoreData.SQLDebug 3' argument on launch.
When I fetch the data using the following code, it selects 3 records initially, but then also selects each record individually even though I am not referencing any other attributes.
var fetchDescriptor = FetchDescriptor\<Movie\>()
fetchDescriptor.propertiesToFetch = \[.id, .name\]
fetchDescriptor.fetchLimit = 3
do {
print("SELECT START")
movies = try modelContext.fetch(fetchDescriptor)
print("SELECT END")
} catch {
print("Failed to load Movie model.")
}
I see it selecting the 3 rows initially, but then it selects each one separately. Why would it do this on the initial fetch? I was hoping to select the data that I want to display and let the system select the entire record only when I access a variable that I did not initially fetch.
CoreData: annotation: fetch using NSSQLiteStatement <0x600002158af0> on entity 'Movie' with sql text 'SELECT 1, t0.Z_PK, t0.ZID, t0.ZNAME FROM ZMOVIE t0 LIMIT 3' returned 3 rows with values: (
"<NSManagedObject: 0x600002158d70> (entity: Movie; id: 0xa583c7ed484691c1 <x-coredata://71E60F4C-1A40-4DB7-8CD1-CD76B4C11949/Movie/p1>; data: <fault>)",
"<NSManagedObject: 0x600002158d20> (entity: Movie; id: 0xa583c7ed482691c1 <x-coredata://71E60F4C-1A40-4DB7-8CD1-CD76B4C11949/Movie/p2>; data: <fault>)",
"<NSManagedObject: 0x600002158f00> (entity: Movie; id: 0xa583c7ed480691c1 <x-coredata://71E60F4C-1A40-4DB7-8CD1-CD76B4C11949/Movie/p3>; data: <fault>)"
)
CoreData: annotation: fetch using NSSQLiteStatement <0x600002154d70> on entity 'Movie' with sql text 'SELECT 0, t0.Z_PK, t0.Z_OPT, t0.ZGENRE, t0.ZID, t0.ZNAME FROM ZMOVIE t0 WHERE t0.Z_PK = ? ' returned 1 rows
CoreData: annotation: with values: (
"<NSSQLRow: 0x600000c89500>{Movie 1-1-1 genre=\"Horror\" id=4C5CB4EB-95D7-4DC8-B839-D4F2D2E96ED0 name=\"A000036\" and to-manys=0x0}"
)
This all happens between the SELECT START and SELECT END print statements. Why is it fulfilling the faults immediately?