I'm using SwiftData with CloutKit with a very simple app. Data syncs between iOS, iPadOS, and visionOS, but not macOS. From what I can tell, macOS is never getting CK messages unless I'm running the app from Xcode.
I can listen for the CK messages and show a line in a debug overlay. This works perfectly when I run from Xcode. I can see the notifications and see updates in my app. However, if I just launch the app outside of Xcode I will never see any changes or notifications. It is as if the Mac app never even tries to contact CloudKit.
Schema has been deployed in the CloudKit console. The app is based on the multi-platform Xcode template. Again, only the macOS version has this issue. Is there some extra permission or setting I need to set up in order to use CloudKit on macOS?
@State private var publisher = NotificationCenter.default.publisher(for: NSPersistentCloudKitContainer.eventChangedNotification).receive(on: DispatchQueue.main)
.onReceive(publisher) { notification in
// Listen for changes in CK events
if let userInfo = notification.userInfo,
let event = userInfo[NSPersistentCloudKitContainer.eventNotificationUserInfoKey] as? NSPersistentCloudKitContainer.Event {
let message = "CloudKit Sync: \(event.type.rawValue) - \(event.succeeded ? "Success" : "Failed") - \(event.description)"
// Store for UI display
syncNotifications.append(message)
if syncNotifications.count > 10 {
syncNotifications.removeFirst()
}
}
}
.overlay(alignment: .topTrailing) {
if !syncNotifications.isEmpty {
VStack(alignment: .leading) {
ForEach(syncNotifications, id: \.self) { notification in
Text(notification)
.padding(8)
}
}
.frame(width: 800, height: 500)
.cornerRadius(8)
.background(Color.secondary.opacity(0.2))
.padding()
.transition(.move(edge: .top))
}
}
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
Hi !
Would anyone know (if possible) how to create backup files to export and then import from the data recorded by SwiftData?
For those who wish, here is a more detailed explanation of my case:
I am developing a small management software with customers and events represented by distinct classes. I would like to have an "Export" button to create a file with all the instances of these 2 classes and another "Import" button to replace all the old data with the new ones from a previously exported file.
I looked for several solutions but I'm a little lost...
SwiftData crashes 100% when fetching history of a model that contains an optional codable property that's updated:
SwiftData/Schema.swift:389: Fatal error: Failed to materialize a keypath for someCodableID.someID from CrashModel. It is possible that this path traverses a type that does not work with append(), please file a bug report with a test.
Would really appreciate some help or even a workaround.
Code:
import Foundation
import SwiftData
import Testing
struct VaultsSwiftDataKnownIssuesTests {
@Test
func testCodableCrashInHistoryFetch() async throws {
let container = try ModelContainer(
for: CrashModel.self,
configurations: .init(
isStoredInMemoryOnly: true
)
)
let context = ModelContext(container)
try SimpleHistoryChecker.hasLocalHistoryChanges(context: context)
// 1: insert a new value and save
let model = CrashModel()
model.someCodableID = SomeCodableID(someID: "testid1")
context.insert(model)
try context.save()
// 2: check history it's fine.
try SimpleHistoryChecker.hasLocalHistoryChanges(context: context)
// 3: update the inserted value before then save
model.someCodableID = SomeCodableID(someID: "testid2")
try context.save()
// The next check will always crash on fetchHistory with this error:
/*
SwiftData/Schema.swift:389: Fatal error: Failed to materialize a keypath for someCodableID.someID from CrashModel. It is possible that this path traverses a type that does not work with append(), please file a bug report with a test.
*/
try SimpleHistoryChecker.hasLocalHistoryChanges(context: context)
}
}
@Model final class CrashModel {
// optional codable crashes.
var someCodableID: SomeCodableID?
// these actually work:
//var someCodableID: SomeCodableID
//var someCodableID: [SomeCodableID]
init() {}
}
public struct SomeCodableID: Codable {
public let someID: String
}
final class SimpleHistoryChecker {
static func hasLocalHistoryChanges(context: ModelContext) throws {
let descriptor = HistoryDescriptor<DefaultHistoryTransaction>()
let history = try context.fetchHistory(descriptor)
guard let last = history.last else {
return
}
print(last)
}
}
Testing Environment: iOS 18.4.1 / macOS 15.4.1
I am working on an iOS project that aims to utilize the user's iCloud Drive documents directory to save a specific directory-based file structure. Essentially, the app would create a root directory where the user chooses in iCloud Drive, then it would populate user generated files in various levels of nested directories.
I have been attempting to use NSMetadataQuery with various predicates and search scopes but haven't been able to get it to directly monitor changes to files or directories that are not in the root directory.
Instead, it only monitors files or directories in the root directory, and any changes in a subdirectory are considered an update to the direct children of the root directory.
Example
iCloud Drive Documents (Not app's ubiquity container)
User Created Root Directory (Being monitored)
File A
Directory A
File B
An insertion or deletion within Directory A would only return a notification with userInfo containing data for NSMetadataQueryUpdateChangedItemsKey relating to Directory A, and not the file or directory itself that was inserted or deleted. (Query results array also only contain the direct children.)
I have tried all combinations of these search scopes and predicates with no luck:
query.searchScopes = [
rootDirectoryURL,
NSMetadataQueryUbiquitousDocumentsScope,
NSMetadataQueryAccessibleUbiquitousExternalDocumentsScope,
]
NSPredicate(value: true)
NSPredicate(format: "%K LIKE '*.md'", NSMetadataItemFSNameKey)
NSPredicate(format: "%K BEGINSWITH %@", NSMetadataItemPathKey, url.path(percentEncoded: false))
I do see these warnings in the console upon starting my query:
[CRIT] UNREACHABLE: failed to get container URL for com.apple.CloudDocs
[ERROR] couldn't fetch remote operation IDs: NSError: Cocoa 257 "The file couldn’t be opened because you don’t have permission to view it."
"Error returned from daemon: Error Domain=com.apple.accounts Code=7 "(null)""
But I am not sure what to make of that, since it does act normally for finding updates in the root directory.
Hopefully this isn't a limitation of the API, as the only alternative I could think of would be to have multiple queries running for each nested directory that I needed updates for.
Topic:
App & System Services
SubTopic:
iCloud & Data
Tags:
Files and Storage
iCloud Drive
Foundation
I have developed an podcast app, where subscriped podcast & episodes synched with iCloud.
So its working fine with iOS & iPad with latest os version, but iCloud not synching in iPod with version 15.
Please help me to fix this.
Thanks
Devendra K.
I am using SwiftData to model my data. For that i created a model called OrganizationData that contains various relationships to other entities. My data set is quite large and i am having a big performance issue when fetching all OrganizationData entities. I started debugging and looking at the sql debug log i noticed that when fetching my entities i run into faults for all relationships even when not accessing them.
Fetching my entities:
let fetchDescriptor = FetchDescriptor<OrganizationData>()
let context = MapperContext(dataManager: self)
let organizations = (try modelContainer.mainContext.fetch(fetchDescriptor))
Doing this fetch, also fetches all relationships. Each in a single query, for every OrganizationData entity.
CoreData: annotation: to-many relationship fault "relationship1" for objectID 0x8aa5249772916e00 <x-coredata://B891FCEB-DF16-4E11-98E6-0AFB5D171A81/OrganizationData/p3869> fulfilled from database. Got 9 rows
CoreData: annotation: to-many relationship fault "relationship2" for objectID 0x8aa5249772916e00 <x-coredata://B891FCEB-DF16-4E11-98E6-0AFB5D171A81/OrganizationData/p3869> fulfilled from database. Got 0 rows
CoreData: annotation: to-many relationship fault "relationship3" for objectID 0x8aa5249772916e00 <x-coredata://B891FCEB-DF16-4E11-98E6-0AFB5D171A81/OrganizationData/p3869> fulfilled from database. Got 0 rows
CoreData: annotation: to-many relationship fault "relationship4" for objectID 0x8aa5249772916e00 <x-coredata://B891FCEB-DF16-4E11-98E6-0AFB5D171A81/OrganizationData/p3869> fulfilled from database. Got 0 rows
CoreData: annotation: to-many relationship fault "relationship5" for objectID 0x8aa5249772916e00 <x-coredata://B891FCEB-DF16-4E11-98E6-0AFB5D171A81/OrganizationData/p3869> fulfilled from database. Got 0 rows
CoreData: annotation: to-many relationship fault "relationship6" for objectID 0x8aa5249772916e00 <x-coredata://B891FCEB-DF16-4E11-98E6-0AFB5D171A81/OrganizationData/p3869> fulfilled from database. Got 0 rows
CoreData: annotation: to-many relationship fault "relationship7" for objectID 0x8aa5249772916e00 <x-coredata://B891FCEB-DF16-4E11-98E6-0AFB5D171A81/OrganizationData/p3869> fulfilled from database. Got 1 rows
CoreData: annotation: to-many relationship fault "relationship8" for objectID 0x8aa5249772916e00 <x-coredata://B891FCEB-DF16-4E11-98E6-0AFB5D171A81/OrganizationData/p3869> fulfilled from database. Got 0 rows
CoreData: annotation: to-many relationship fault "relationship9" for objectID 0x8aa5249772916e00 <x-coredata://B891FCEB-DF16-4E11-98E6-0AFB5D171A81/OrganizationData/p3869> fulfilled from database. Got 0 rows
The relationships are all defined the same
@Relationship(deleteRule: .cascade, inverse: \EntityData1.organization)
var relationship1: [EntityData1] = []
Am i missing something? As far as i understood relationships are lazy and should only be faulted when accessing the property. But doing the fetch as described above already causes a query to happen, making the fetch take very long when using a large data set.
As of 2025-05-03, when a macOS user enables iCloud Drive synchronization for Desktop & Documents in US region, does iCloud filter xattrs upon upload or later when downloading back to another macOS host? Or is it the case that iCloud has no filtering of third-party xattrs? Where can I find the technical document outlining exactly what iCloud does with xattrs set on macOS host files and folders synchronized with iCloud Drive?
Does the CloudKit participant limit of 100 include the owner?
My app uses iCloud to let users sync their files via their private iCloud Drive, which does not use CloudKit.
FileManager.default.url(forUbiquityContainerIdentifier: nil)?.appending(component: "Documents")
I plan to transfer my app to another developer account, but I'm afraid it will affect the access of the app to the existing files in that folder. Apple documentation doesn't mention this case.
Has anyone done this before and can confirm if the app will continue to work normally after transferring?
Thanks
The stuff I've found by searching has confused me, so hopefully someone can help simplify it for me?
I have an app (I use it for logging which books I've given away), and I could either add a bunch of things to the app, or I could have another app (possibly a CLI tool) to generate some reports I'd like.
I'm getting the following error message when executing the rollback method in a modelContext, what could be causing this ?
SwiftData/ModelSnapshot.swift:46: Fatal error: A ModelSnapshot must be initialized with a known-keys dictionary
I have a CoreData model with two configuration - but several problems. Notably the viewContext only shows data from the .private configuration. Here is the setup:
The private configuration holds entities, for example, User and Course and the shared one holds entities, for example, Player and League. I setup the NSPersistentStoreDescriptions to use the same container but with a databaseScope of .private/.shared and with the configuration of "Private"/"Shared". loadPersistentStores() does not report an error.
If I try container.initializeCloudKitSchema() only the .private configuration produces CKRecord types. If I create a companion app using one configuration (w/ all entities) the schema initialization creates all CKRecord types AND I can populate some data in the .private and a created CKShare. I see that data in the CloudKit dashboard.
If I axe the companion app and run the real thing w/ two configurations, the viewContext only has the .private data. Why?
If when querying history I use NSPersistentHistoryTransaction.fetchRequest I get a nil return when using two configurations (but non-nil when using one).
Starting 20th March 2025, I see an increase in bandwidth and latency for one of my CloudKit projects.
I'm using NSPersistentCloudKitContainer to synchronise my data.
I haven't changed any CloudKit scheme during that time but shipped an update. Since then, I reverted some changes from that update, which could have led to changes in the sync behaviour.
Is anyone else seeing any issues?
I would love to file a DTS and use one of my credits for that, but unfortunately, I can't because I cannot reproduce it with a demo project because I cannot travel back in time and check if it also has an increase in metrics during that time.
Maybe an Apple engineer can green-light me filing a DTS request, please.
Hi all,
I am using SwiftData and cloudkit and I am having an extremely persistent bug.
I am building an education section on a app that's populated with lessons via a local JSON file. I don't need this lesson data to sync to cloudkit as the lessons are static, just need them imported into swiftdata so I've tried to use the modelcontainer like this:
static func createSharedModelContainer() -> ModelContainer {
// --- Define Model Groups ---
let localOnlyModels: [any PersistentModel.Type] = [
Lesson.self, MiniLesson.self,
Quiz.self, Question.self
]
let cloudKitSyncModels: [any PersistentModel.Type] = [
User.self, DailyTip.self, UserSubscription.self,
UserEducationProgress.self // User progress syncs
]
However, what happens is that I still get Lesson and MiniLesson record types on cloudkit and for some reason as well, whenever I update the data models or delete and reinstall the app on simulator, the lessons duplicate (what seems to happen is that a set of lessons comes from the JSON file as it should), and then 1-2 seconds later, an older set of lessons gets synced from cloudkit.
I can delete the old set of lessons if I just delete the lessons and mini lessons record types, but if I update the data model again, this error reccurrs.
Sorry, I don't know if I managed to explain this well but essentially I just want to stop the lessons and minilessons from being uploaded to cloudkit as I think this will fix the problem. Am I doing something wrong with the code?
In a document based SwiftData app for macOS, how do you go about opening a (modal) child window connected to the ModelContainer of the currently open document?
Using .sheet() does not really result in a good UX, as the appearing view lacks the standard window toolbar.
Using a separate WindowGroup with an argument would achieve the desired UX. However, as WindowGroup arguments need to be Hashable and Codable, there is no way to pass a ModelContainer or a ModelContext there:
WindowGroup(id: "myWindowGroup", for: MyWindowGroupArguments.self) { $args in
ViewThatOpensInAWindow(args: args)
}
Is there any other way?
Here’s the situation:
• You’re downloading a huge list of data from iCloud.
• You’re saving it one by one (sequentially) into SwiftData.
• You don’t want the SwiftUI view to refresh until all the data is imported.
• After all the import is finished, SwiftUI should show the new data.
The Problem
If you insert into the same ModelContext that SwiftUI’s @Environment(.modelContext) is watching, each insert may cause SwiftUI to start reloading immediately.
That will make the UI feel slow, and glitchy, because SwiftUI will keep trying to re-render while you’re still importing.
How to achieve this in Swift Data ?
I have transitioned to CKSyncEngine for syncing data to iCloud, and it is working quite well. I have a question regarding best practices for modifying and saving a CKRecord which already exists in the private or shared database.
In my current app, most CKRecords will never be modified after saving to the database, so I do not persist a received record locally after updating my local data model. In the rare event that the local data for that record is modified, I manually fetch the associated server record from the database, modify it, and then use CKSyncEngine to save the modified record.
As an alternative method, I can create a new CKRecord locally with the corresponding recordID and the modified data, and then use CKSyncEngine to attempt to save that record to the database. Doing so generates an error in the delegate method handleSentRecordZoneChanges, where I receive the local record I tried to save back inevent.failedRecordSaves with a .serverRecordChanged error, along with the corresponding server CKRecord. I can then update that server record with the local data and re-save using CKSyncEngine. I have not yet seen any issues when doing it this way.
The advantage of the latter method is that CKSyncEngine handles the entire database operation, eliminating the manual fetch step. My question is: is this an acceptable practice, or could this result in other unforeseen issues?
I have a document based SwiftData app in which I would like to implement a persistent cache. For obvious reasons, I would not like to store the contents of the cache in the documents themselves, but in my app's data directory.
Is a use case, in which a document based SwiftData app uses not only the ModelContainers from the currently open files, but also a ModelContainer writing a database file in the app's documents directory (for cache, settings, etc.) supported?
If yes, how can you inject two different ModelContexts, one tied to the currently open file and one tied to the local database, into a SwiftUI view?
We're in the process of migrating our app to the Swift 6 language mode. I have hit a road block that I cannot wrap my head around, and it concerns Core Data and how we work with NSManagedObject instances.
Greatly simplied, our Core Data stack looks like this:
class CoreDataStack {
private let persistentContainer: NSPersistentContainer
var viewContext: NSManagedObjectContext { persistentContainer.viewContext }
}
For accessing the database, we provide Controller classes such as e.g.
class PersonController {
private let coreDataStack: CoreDataStack
func fetchPerson(byName name: String) async throws -> Person? {
try await coreDataStack.viewContext.perform {
let fetchRequest = NSFetchRequest<Person>()
fetchRequest.predicate = NSPredicate(format: "name == %@", name)
return try fetchRequest.execute().first
}
}
}
Our view controllers use such controllers to fetch objects and populate their UI with it:
class MyViewController: UIViewController {
private let chatController: PersonController
private let ageLabel: UILabel
func populateAgeLabel(name: String) {
Task {
let person = try? await chatController.fetchPerson(byName: name)
ageLabel.text = "\(person?.age ?? 0)"
}
}
}
This works very well, and there are no concurrency problems since the managed objects are fetched from the view context and accessed only in the main thread.
When turning on Swift 6 language mode, however, the compiler complains about the line calling the controller method:
Non-sendable result type 'Person?' cannot be sent from nonisolated context in call to instance method 'fetchPerson(byName:)'
Ok, fair enough, NSManagedObject is not Sendable. No biggie, just add @MainActor to the controller method, so it can be called from view controllers which are also main actor. However, now the compiler shows the same error at the controller method calling viewContext.perform:
Non-sendable result type 'Person?' cannot be sent from nonisolated context in call to instance method 'perform(schedule:_:)'
And now I'm stumped. Does this mean NSManageObject instances cannot even be returned from calls to NSManagedObjectContext.perform? Ever? Even though in this case, @MainActor matches the context's actor isolation (since it's the view context)?
Of course, in this simple example the controller method could just return the age directly, and more complex scenarios could return Sendable data structures that are instantiated inside the perform closure. But is that really the only legal solution? That would mean a huge refactoring challenge for our app, since we use NSManageObject instances fetched from the view context everywhere. That's what the view context is for, right?
tl;dr: is it possible to return NSManagedObject instances fetched from the view context with Swift 6 strict concurrency enabled, and if so how?
In core-data I have a contact and location entity. I have one-to-many relationship from contact to locations and one-to-one from location to contact. I create contact in a seperate view and save it. Later I create a location, fetch the created contact, and save it while specifying the relationship between location and contact contact and test if it actually did it and it works.
viewContext.perform {
do {
// Set relationship using the generated accessor method
currentContact.addToLocations(location)
try viewContext.save()
print("Saved successfully. Locations count:", currentContact.locations?.count ?? 0)
if let locs = currentContact.locations {
print("📍 Contact has \(locs.count) locations.")
for loc in locs {
print("➡️ Location: \(String(describing: (loc as AnyObject).locationName ?? "Unnamed"))")
}
}
} catch {
print("Failed to save location: \(error.localizedDescription)")
}
}
In my NSManagedObject class properties I have this : for Contact:
@NSManaged public var locations: NSSet?
for Location:
@NSManaged public var contact: Contact?
in my persistenceController I have:
for desc in [publicStore, privateStore] {
desc.setOption(true as NSNumber, forKey:
NSPersistentStoreRemoteChangeNotificationPostOptionKey)
desc.setOption(true as NSNumber, forKey: NSPersistentHistoryTrackingKey)
desc.setOption(true as NSNumber, forKey: NSMigratePersistentStoresAutomaticallyOption)
desc.setOption(true as NSNumber, forKey: NSInferMappingModelAutomaticallyOption)
desc.setOption(true as NSNumber, forKey: "CKSyncCoreDataDebug") // Optional: Debug sync
// Add these critical options for relationship sync
desc.setOption(true as NSNumber, forKey: "NSPersistentStoreCloudKitEnforceRecordExistsKey")
desc.setOption(true as NSNumber, forKey: "NSPersistentStoreCloudKitMaintainReferentialIntegrityKey")
// Add this specific option to force schema update
desc.setOption(true as NSNumber, forKey: "NSPersistentStoreRemoteStoreUseCloudKitSchemaKey")
}
When synchronization happens on CloudKit side, it creates CKRecords: CD_Contact and CD_Location. However for CD_Location it creates the relationship CD_contact as a string and references the CD_Contact. This I thought should have come as REFERENCE On the CD_Contact there is no CD_locations field at all. I do see the relationships being printed on coredata side but it does not come as REFERENCE on cloudkit. Spent over a day on this. Is this normal, what am I doing wrong here? Can someone advise?