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.
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
Created
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?
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
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!
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.
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
Topic:
App & System Services
SubTopic:
iCloud & Data
Tags:
Files and Storage
On demand resources
Core Data
Background Assets
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 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'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()
}
}
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]
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!
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
}
}
}
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
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?
Topic:
App & System Services
SubTopic:
iCloud & Data
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!
This is probably super simple answer that I missed, but: I have an app that has a database; I'd like to create a second app (actually a CLI tool), and access the same database. Is that possible? And, if so, how? 😄
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!
My iOS app uses CloudKit key-value storage. I have not updated the app in a few years but it works fine. Since it was last updated, I transferred the app from an old organization to my personal developer account. Now that I'm working on the app again I get an error: Provisioning profile "iOS Team Provisioning Profile: com.company.app" doesn't match the entitlements file's value for the com.apple.developer.ubiquity-kvstore-identifier entitlement.
In the entitlement file, it has $(TeamIdentifierPrefix)$(CFBundleIdentifier) as the value for iCloud Key-Value Store. I've verified the variables resolve as expected. When I parse the provisioning profile there is no entitlement value for key-value storage. What am I getting wrong?
Hi,
I was testing the new iOS 18 behavior where NSPersistentCloudKitContainer wipes the local Core Data store if the user logs out of iCloud, for privacy purposes.
I ran the tests both with a Core Data + CloudKit app, and a simple one using SwiftData with CloudKit enabled. Results were identical in either case.
In my testing, most of the time, the feature worked as expected. When I disabled iCloud for my app, the data was wiped (consistent with say the Notes app, except if you disable iCloud it warns you that it'll remove those notes). When I re-enabled iCloud, the data appeared. (all done through the Settings app)
However, in scenarios when NSPersistentCloudKitContainer cannot immediately sync -- say due to rate throttling -- and one disables iCloud in Settings, this wipes the local data store and ultimately results in data loss.
This occurs even if the changes to the managed objects are saved (to the local store) -- it's simply they aren't synced in time.
It can be a little hard to reproduce the issue, especially since when you exit to the home screen from the app, it generally triggers a sync. To avoid this, I swiped up to the screen where you can choose which apps to close, and immediately closed mine. Then, you can disable iCloud, and run the app again (with a debugger is helpful). I once saw a message with something along the lines of export failed (for my record that wasn't synced), and unfortunately it was deleted (and never synced).
Perhaps before NSPersistentCloudKitContainer wipes the local store it ought to force sync with the cloud first?
I'm building a SwiftUI app with SwiftData and want to centralize both query logic and related actions in a manager class. For example, let's say I have a reading app where I need to track the currently reading book across multiple views.
What I want to achieve:
@Observable
class ReadingManager {
let modelContext: ModelContext
// Ideally, I'd love to do this:
@Query(filter: #Predicate<Book> { $0.isCurrentlyReading })
var currentBooks: [Book] // ❌ But @Query doesn't work here
var currentBook: Book? {
currentBooks.first
}
func startReading(_ book: Book) {
// Stop current book if any
if let current = currentBook {
current.isCurrentlyReading = false
}
book.isCurrentlyReading = true
try? modelContext.save()
}
func stopReading() {
currentBook?.isCurrentlyReading = false
try? modelContext.save()
}
}
// Then use it cleanly in any view:
struct BookRow: View {
@Environment(ReadingManager.self) var manager
let book: Book
var body: some View {
Text(book.title)
Button("Start Reading") {
manager.startReading(book)
}
if manager.currentBook == book {
Text("Currently Reading")
}
}
}
The problem is @Query only works in SwiftUI views. Without the manager, I'd need to duplicate the same query in every view just to call these common actions.
Is there a recommended pattern for this? Or should I just accept query duplication across views as the intended SwiftUI/SwiftData approach?