iCloud & Data

RSS for tag

Learn how to integrate your app with iCloud and data frameworks for effective data storage

CloudKit Documentation

Posts under iCloud & Data subtopic

Post

Replies

Boosts

Views

Created

CKSyncEngine initial sync on a new device
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.
2
0
150
4d
How to figure out sync errors in Production?
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?
1
0
84
5d
Naming Collision Between Model Attribute and Enum Case in SwiftData and CloudKit
There is a conflict in SwiftData (specifically when synced with CloudKit) when a @Model attribute shares the same name as a case within its assigned enum type. When this occurs, accessing the attribute on a model instance consistently returns the value corresponding to the enum case name, rather than the actual value persisted in the database. Steps to Reproduce Define an enumeration (e.g., Status) with a case that matches a planned property name (e.g., case status). Create a SwiftData @Model that uses this enum. Name the property in the model the same as the enum case. Attempt to save and then retrieve the value. Example Code enum TaskStatus: String, Codable { case status // The conflict source case pending case completed } @Model class TodoItem { // Conflict: Property name matches enum case name var status: TaskStatus init(status: TaskStatus) { self.status = status } } Expected Behavior The property item.status should return the value stored in the database (e.g., .pending or .completed). Actual Behavior The property item.status consistently resolves to the enum case .status, ignoring the actual persisted data.
1
0
51
6d
Unable to sync SwiftData model fully using CloudKit
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.
1
0
195
1w
Getting a list of deleted CloudKit records with an expired change token
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?
4
0
231
1w
NSStagedMigrationManager Merging Steps
Hello, I have 3 model versions and I'm trying to step through migration. Version 2 makes significant changes to v1. As a result, I've renamed the entities in question by appending _v2 to their name, as the data isn't important to retain. v3, remove's the appended version number from v2. Setting the .xcdatamodeld to v3 and the migrations steps array as follows causes the app to error [ NSLightweightMigrationStage([v1]), NSLightweightMigrationStage([v2]), NSLightweightMigrationStage([v3]), ] CoreData: error: <NSPersistentStoreCoordinator: 0x10740d680>: Attempting recovery from error encountered during addPersistentStore: 0x10770f8a0 Error Domain=NSCocoaErrorDomain Code=134110 "An error occurred during persistent store migration." An error occurred during persistent store migration. Cannot merge multiple root entity source tables into one destination entity root table. I find this odd because if I run the migration independently across app launches, the migration appears to drop the no longer used tables in v2, then re-add them back in v3. So it seems to me that something is not finishing completely with the fully stepped through migration. -- I'm also unable to understand how to use NSCustomMigrationStage I've tried setting it to migrate from v1, to v2, but I'm getting a crash with error Duplicate version checksums across stages detected
3
0
87
1w
@ComputedProperty vs copying values SwiftData AppEntity
I'm setting up App Entities for my SwiftData models and I'm not sure about the best way to reference SwiftData model properties in the AppEntity. I have a SwiftData model with many properties: @Model final class Contact { @Attribute(.unique) var id: UUID = UUID() var name: String var phoneNumber: String var email: String var website: URL? var birthday: Date? var notes: String // ... many more properties } I want to expose these properties on my AppEntity so they're available for system features, such as giving Apple Intelligence more context about on-screen content. struct ContactEntity: AppEntity { var id: UUID @Property(title: "Name") var name: String @Property(title: "Phone") var phoneNumber: String @Property(title: "Email") var email: String // ... all the other properties } I couldn't find guidance in the documentation for this specific situation. I've considered two approaches: Add @Property variables to the AppEntity for each SwiftData model property and copy all values from the SwiftData model to the AppEntity in the AppEntity initializer — but I recall this being discouraged in previous WWDC sessions since it duplicates data and can become stale Use @ComputedProperty to fetch the model and access the single properties — this seems like an alternative, but fetching the entire model just to access individual properties doesn't feel right What is the recommended approach when SwiftData is the data source? Thank you!
1
0
88
1w
Deleting Production Database SwiftData
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.
2
0
163
1w
Read, Write, and Consuming Files / URLs
Hello, I have an asset pack that I'm use to periodically distribute a sqlite database thats being used to an NSPersistentStore. Because the database is over a few GBs, and the files in an AssetPack are not mutable, I have to stream the database into a temporary file, then replace my NSPersistentStore. This requires that the user has 3x the storage available of the database, and permanently uses twice to storage needed. I'd like: To be able to mark a URL/File to be accessible for read/write access To be able to mark a file / URL as consumed when it's no needed. So that it can be cleared from the user storage while still maintaining an active subscription to the asset pack for updates. Thank you
3
0
167
1w
Cannot Accept CloudKit Share After First App Install
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.
2
1
118
1w
CloudKit container name
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.
3
0
132
2w
Bug? SwiftData + inheritance + optional many-to-one relationship
I've spent a few months writing an app that uses SwiftData with inheritance. Everything worked well until I tried adding CloudKit support. To do so, I had to make all relationships optional, which exposed what appears to be a bug. Note that this isn't a CloudKit issue -- it happens even when CloudKit is disabled -- but it's due to the requirement for optional relationships. In the code below, I get the following error on the second call to modelContext.save() when the button is clicked: Could not cast value of type 'SwiftData.PersistentIdentifier' (0x1ef510b68) to 'SimplePersistenceIdentifierTest.Computer' (0x1025884e0). I was surprised to find zero hit when Googling "Could not cast value of type 'SwiftData.PersistentIdentifier'". Some things to note: Calling teacher.computers?.append(computer) instead of computer.teacher = teacher results in the same error. It only happens when Teacher inherits Person. It only happens if modelContext.save() is called both times. It works if the first modelContext.save() is commented out. If the second modelContext.save()is commented out, the error occurs the second time the model context is saved (whether explicitly or implicitly). Keep in mind this is a super simple repro written to generate on demand the error I'm seeing in a normal app. In my app, modelContext.save() must be called in some places to update the UI immediately, sometimes resulting in the error seconds later when the model context is saved automatically. Not calling modelContext.save() doesn't appear to be an option. To be sure, I'm new to this ecosystem so I'd be thrilled if I've missed something obvious! Any thoughts are appreciated. import Foundation import SwiftData import SwiftUI struct ContentView: View { @Environment(\.modelContext) var modelContext var body: some View { VStack { Button("Do it") { let teacher = Teacher() let computer = Computer() modelContext.insert(teacher) modelContext.insert(computer) try! modelContext.save() computer.teacher = teacher try! modelContext.save() } } } } @Model class Computer { @Relationship(deleteRule: .nullify) var teacher: Teacher? init() {} } @Model class Person { init() {} } @available(iOS 26.0, macOS 26.0, *) @Model class Teacher: Person { @Relationship(deleteRule: .nullify, inverse: \Computer.teacher) public var computers: [Computer]? = [] override init() { super.init() } }
6
2
189
3w
SwiftData crash on adding sort argument to Query
Experiencing a crash that is only reproducible on TestFlight or AppStore version of the app, note this does not happen when running from Xcode. I've isolated the problem to sort argument being added to @Query that fetches a model that sorts based on inherited property. To reproduce: @Model class SuperModel { var createdAt: Date = .now } @available(macOS 26.0, *) @Model class SubModel: SuperModel { } @Query(sort: \SubModel.createdAt, animation: .default) private var models: [SubModel]
1
0
78
3w
Core Data: lightweight migration
Hi everyone, I’m working on an offline-first iOS app using Core Data. I have a question about safe future updates: in my project, I want to be able to add new optional fields to existing Entities or even completely new Entities in future versions — but nothing else (no renaming, deleting, or type changes). Here’s how my current PersistenceController looks: import CoreData struct PersistenceController { static let shared = PersistenceController() let container: NSPersistentContainer init(inMemory: Bool = false) { container = NSPersistentContainer(name: "MyApp") if inMemory { container.persistentStoreDescriptions.first!.url = URL(fileURLWithPath: "/dev/null") } container.loadPersistentStores(completionHandler: { (storeDescription, error) in if let error = error as NSError? { print("Core Data failed to load store: \(error), \(error.userInfo)") } }) container.viewContext.automaticallyMergesChangesFromParent = true } } Do I need to explicitly set these properties to ensure lightweight migration works? shouldMigrateStoreAutomatically = true shouldInferMappingModelAutomatically = true Or, according to the documentation, are they already true by default, so I can safely add optional fields and new Entities in future versions without breaking users’ existing data? Thanks in advance for your guidance!
2
0
179
3w
Performance in Large Datasets (SwiftUI+SwiftData app)
Hi everyone, In the simple app below, I have a QueryView that has LazyVStack containing 100k TextField's that edit the item's content. The items are fetched with a @Query. On launch, the app will generate 100k items. Once created, when I press any of the TextField's , a severe hang happens, and every time I type a single character, it will cause another hang over and over again. I looked at it in Instruments and it shows that the main thread is busy during the duration of the hang (2.31 seconds) updating QueryView. From the cause and effect graph, the update is caused by @Observable QueryController <Item>.(Bool). Why does it take too long to recalculate the view, given that it's in a LazyVStack? (In other words, why is the hang duration directly proportional to the number of items?) How to fix the performance of this app? I thought adding LazyVStack was all I need to handle the large dataset, but maybe I need to add a custom pagination with .fetchLimit on top of that? (I understand that ModelActor would be an alternative to @Query because it will make the database operations happen outside of the main thread which will fix this problem, but with that I will lose the automatic fetching of @Query.) Thank you for the help! import SwiftData import SwiftUI @main struct QueryPerformanceApp: App { var body: some Scene { WindowGroup { ContentView() .modelContainer(for: [Item.self], inMemory: true) } } } @Model final class Item { var name: String init(name: String) { self.name = name } } struct ItemDetail: View { @Bindable var item: Item var body: some View { TextField("Name", text: $item.name) } } struct QueryView: View { @Query private var items: [Item] var body: some View { ScrollView { LazyVStack { ForEach(items) { item in VStack { ItemDetail(item: item) } } } } } } struct ContentView: View { let itemCount = 100_000 @Environment(\.modelContext) private var context @State private var isLoading = true var body: some View { Group { if isLoading { VStack(spacing: 16) { ProgressView() Text("Generating \(itemCount) items...") } } else { QueryView() } } .task { for i in 1...itemCount { context.insert(Item(name: "Item \(i)")) } try? context.save() isLoading = false } } }
1
0
136
3w
Extreme increase in app storage size after enabling CloudKit
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
2
0
154
3w
Developing App User Privacy
Hey everyone, I have a question. When creating an app, how should I design a message table that involves personal privacy? The content is stored locally on the user's device, and then encrypted in the server database? How should I design it?
0
0
54
Jan ’26
SwiftData CloudKit sync broken on iOS 26
Hi everyone, I’m running into a breaking issue with SwiftData automatic CloudKit syncing on iOS 26, and I'm trying to determine if this is a known regression or a new configuration requirement I missed. The Setup: My setup is extremely standard; I am using the default configuration exactly as described in Apple's documentation here: https://developer.apple.com/documentation/swiftdata/syncing-model-data-across-a-persons-devices The schema is very simple: A single @Model class. No relationships. The Issue: Prior to iOS 26, this exact app was successfully syncing data between devices and to iCloud without issues. Immediately after the iOS 26 update, syncing stopped completely. I haven't changed any code, but when I check the CloudKit Console, I am seeing some BAD_REQUEST errors during sync attempts. Since I am using the default SwiftData sync (and not manual CKRecord handling), I’m not sure how my client code could be triggering a bad request unless the schema requirements have changed under the hood. Questions: Has anyone else seen increased BAD_REQUEST errors with SwiftData on iOS 26? Are there new entitlements or strict schema requirements introduced in iOS 26 that might cause a previously valid model to be rejected by CloudKit? Any pointers or confirmations would be appreciated. Thanks!
1
1
167
Jan ’26
NSPersistentCloudKitContainer in duplicate processes
I have a single multiplatform application that I use NSPersistentCloudKitContainer on. This works great, except I noticed when I open two instances of the same process (not windows) on the same computer, which share the same store, data duplication and "Metadata Inconsistency" errors start appearing. This answer (https://stackoverflow.com/a/67243833) says this is not supported with NSPersistentCloudKitContainer. Is this indeed true? If it isn't allowed, is the only solution to disable multiple instances of the process via a lock file? I was thinking one could somehow coordinate a single "leader" process that syncs to the cloud, with the others using NSPersistentContainer, but this would be complicated when the "leader" process terminates. Currently, it seems iPad split views are new windows, not processes -- but overall I'm still curious :0 Thank you!
1
1
238
Dec ’25