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

Activity

Stopping certain data models from syncing to cloudkit
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?
0
0
56
Apr ’25
Open child windows for a document in a document based SwiftData app
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?
0
0
28
Apr ’25
CKSyncEngine save existing CKRecord
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?
2
0
69
Apr ’25
Is it possible to use an additional local ModelContainer in a document based SwiftData app?
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?
0
0
33
Apr ’25
Core Data and Swift 6 concurrency: returning an NSManagedObject
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?
0
0
54
Apr ’25
Core Data transformable attribute problem in Xcode16
Hi everyone, Have anybody faced with Core Data issues, trying to migrate the project to Xcode16 beta 4? We are using transformableAttributeType in some entities, with attributeValueClassName = "[String]" and valueTransformerName = "NSSecureUnarchiveFromData". It is working just fine for years, but now I am trying to run the project from Xcode16 and have 2 issues: in Xcode logs I see warning and error: CoreData: fault: Declared Objective-C type "[String]" for attribute named alertBarChannels is not valid CoreData: Declared Objective-C type "[String]" for attribute named alertBarChannels is not valid periodically the app crashes when we are assigning value to this attribute, with error: Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: '-[__NSCFConstantString characterAtIndex:]: Range or index out of bounds' Once again, in Xcode 15 it works fine, and it was working for years. Cannot find any information about what was changed in the framework... Thank you in advance for any information, which could clarify what is going on.
15
14
3.9k
Apr ’25
CloudKit is not synchronizing with coredata for relationships
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?
0
0
55
Apr ’25
SwiftData - disable Persistent History Tracking
Hello, I am building a pretty large database (~40MB) to be used in my SwiftData iOS app as read-only. While inserting and updating the data, I noticed a substantial increase in size (+ ~10MB). A little digging pointed to ACHANGE and ATRANSACTION tables that apparently are dealing with Persistent History Tracking. While I do appreciate the benefits of that, I prefer to save space. Could you please point me in the right direction?
0
0
52
Apr ’25
Mac App Crashing with Illegal Instructions
I have made a Swift App for MacOS 15 under XCode 16.3, which runs fine. I also want to run it under the previous MacOS 14. Unfortunately it crashes without even starting up (it does not even reach the first log output statement on the first view) The crash reason is Crashed Thread: 0 Dispatch queue: com.apple.main-thread Exception Type: EXC_BAD_INSTRUCTION (SIGILL) Exception Codes: 0x0000000000000001, 0x0000000000000000 Termination Reason: Namespace SIGNAL, Code 4 Illegal instruction: 4 Terminating Process: exc handler [2970] I have set the miminium deployment to MacOS 14.0 but to no effect. The XCode machine is a MacOS 15.4 on Arm M3 and the target machine is MacOS 14.7.5 on Intel (MacBook Air) I think it might be related to the compiler and linker settings.
3
0
62
Apr ’25
SwiftData crashes on fetchHistory
Hi, would it be possible that instead of crashing when calling fetchHistory that function simply throws an error instead? fetchHistory seems to crash when it cannot understand the models if they are not compatible etc… which is understandable, but it makes it really difficult to handle and debug, there's not a lot of details, and honestly I would just rather that it throws an error and let me ignore a history entry that might be useless rather than crashing the entire app. Thank you!
1
1
46
Apr ’25
SwiftData iOS18: "Could not materialize Objective-C class named "Set" from declared attribute value type "Set<String>" of attribute named..."
Posting here to see if folks have workarounds or if I have a misunderstanding of SwiftData supported types. In adopting SwiftData, I have swiftData properties of collection type (Array or Set - both have this issue). E.g: @Model final class Item { var timestamp: Date var strings = ["aa", "bb"] var display: String { strings.joined(separator: " ") } init(timestamp: Date) { self.timestamp = timestamp } } So far in development I haven't had issues on iOS 17, but on the iOS 18 betas 4-5 the app logs show the following error: "fault: Could not materialize Objective-C class named "Array" from declared attribute value type "Array<String>" of attribute named strings" It happens immediately in my app when creating an object with a collection attribute. In a minimal test example, the error log appears only after a few minutes and doesn't seem to affect the template app's basic functionality. Anyone else running into this? Was filed as FB14397250
8
13
2.4k
Apr ’25
Debugging help
No matter what I do, I keep getting the error Thread 1: EXC_BREAKPOINT (code=1, subcode=0x2648fc364) for the line: transactions = try modelContext.fetch(descriptor) in the code below. My app opens, but freezes on the home page and I can't click anything. I am not sure how to fix initialization issues. I am creating a financial assistant app that connects plaid and opoenai api. var descriptor = FetchDescriptor&lt;ExpenseTransaction&gt;() descriptor.sortBy = [SortDescriptor(\.date, order: .reverse)] descriptor.fetchLimit = 200 transactions = try modelContext.fetch(descriptor) print("Successfully loaded \(transactions.count) transactions") } catch { print("Error in loadLocalTransactions: \(error)") transactions = [] } }
3
0
70
Apr ’25
SwiftData and iCloud
I'm a first time developer for Swift, (getting on a bit!) but after programming in VB back in the late 90s I wanted to write an app for iPhone. I think I might have gone about it the wrong way, but I've got an app that works great on my iPhone or works great on my iPad. It saves the data persistently on device, but, no matter how much I try, what I read and even resorting to AI (ChatGPT & Gemini) I still can't get it to save the data on iCloud to synchronise between the two and work across the devices. I think it must be something pretty fundamental I'm doing (or more likely not doing) that is causing the issue. I'm setting up my signing and capabilities as per the available instructions but I always get a fatal error. I think it might be something to do with making fields optional, but at this point I'm second guessing myself and feeling a complete failure. Any advice or pointers would be really gratefully appreciated. I like my app and would like eventually to get it on the App Store but at this point in time I feel it should be on the failed projects heap! I've even tried a new Xcode project for iOS and asking it to use SwiftData and CloudKit - the default project should work - right? But it absolutely doesn't for me. Please send help!!
2
0
98
Apr ’25
How is Record Zone Sharing done?
My use case is the following: Every user of my app can create as an owner a set of items.  These items are private until the owner invites other users to share all of them as participant. The participants can modify the shared items and/or add other items. So, sharing is not done related to individual items, but to all items of an owner. I want to use CoreData & CloudKit to have local copies of private and shared items. To my understanding, CoreData & CloudKit puts all mirrored items in a special zone „com.apple.coredata.cloudkit.zone“. So, this zone should be shared, i.e. all items in it. In the video it is said that NSPersistentCloudKitContainer uses Record Zone Sharing optionally in contrast to hierarchically record sharing using a root record. But how is this done? Maybe I can declare zone „com.apple.coredata.cloudkit.zone“ as a shared zone?
2
0
946
Apr ’25
SwiftData and @Query to find all records for the current date of a multidatepicker (Set = [])
I’m trying to build a CRUD app using SwiftData, @Query model and multidatepicker. The data from a multidatepicker is stored or persists in SwiftData as Set = []. My current dilemma is how to use SwiftData and @Query model Predicate to find all records on the current date. I can’t find any SwiftData documentation or examples @Query using Set = []. My CRUD app should retrieve all records for the current date. Unfortunately, I don’t know the correct @Query model syntax for Set = [].
0
0
39
Apr ’25
SwiftData JSONDataStore with relationships
I am trying to add a custom JSON DataStore and DataStoreConfiguration for SwiftData. Apple kindly provided some sample code in the WWDC24 session, "Create a custom data store with SwiftData", and (once updated for API changes since WWDC) that works fine. However, when I try to add a relationship between two classes, it fails. Has anyone successfully made a JSONDataStore with a relationship? Here's my code; firstly the cleaned up code from the WWDC session: import SwiftData final class JSONStoreConfiguration: DataStoreConfiguration { typealias Store = JSONStore var name: String var schema: Schema? var fileURL: URL init(name: String, schema: Schema? = nil, fileURL: URL) { self.name = name self.schema = schema self.fileURL = fileURL } static func == (lhs: JSONStoreConfiguration, rhs: JSONStoreConfiguration) -> Bool { return lhs.name == rhs.name } func hash(into hasher: inout Hasher) { hasher.combine(name) } } final class JSONStore: DataStore { typealias Configuration = JSONStoreConfiguration typealias Snapshot = DefaultSnapshot var configuration: JSONStoreConfiguration var name: String var schema: Schema var identifier: String init(_ configuration: JSONStoreConfiguration, migrationPlan: (any SchemaMigrationPlan.Type)?) throws { self.configuration = configuration self.name = configuration.name self.schema = configuration.schema! self.identifier = configuration.fileURL.lastPathComponent } func save(_ request: DataStoreSaveChangesRequest<DefaultSnapshot>) throws -> DataStoreSaveChangesResult<DefaultSnapshot> { var remappedIdentifiers = [PersistentIdentifier: PersistentIdentifier]() var serializedData = try read() for snapshot in request.inserted { let permanentIdentifier = try PersistentIdentifier.identifier(for: identifier, entityName: snapshot.persistentIdentifier.entityName, primaryKey: UUID()) let permanentSnapshot = snapshot.copy(persistentIdentifier: permanentIdentifier) serializedData[permanentIdentifier] = permanentSnapshot remappedIdentifiers[snapshot.persistentIdentifier] = permanentIdentifier } for snapshot in request.updated { serializedData[snapshot.persistentIdentifier] = snapshot } for snapshot in request.deleted { serializedData[snapshot.persistentIdentifier] = nil } try write(serializedData) return DataStoreSaveChangesResult<DefaultSnapshot>(for: self.identifier, remappedIdentifiers: remappedIdentifiers) } func fetch<T>(_ request: DataStoreFetchRequest<T>) throws -> DataStoreFetchResult<T, DefaultSnapshot> where T : PersistentModel { if request.descriptor.predicate != nil { throw DataStoreError.preferInMemoryFilter } else if request.descriptor.sortBy.count > 0 { throw DataStoreError.preferInMemorySort } let objs = try read() let snapshots = objs.values.map({ $0 }) return DataStoreFetchResult(descriptor: request.descriptor, fetchedSnapshots: snapshots, relatedSnapshots: objs) } func read() throws -> [PersistentIdentifier : DefaultSnapshot] { if FileManager.default.fileExists(atPath: configuration.fileURL.path(percentEncoded: false)) { let decoder = JSONDecoder() decoder.dateDecodingStrategy = .iso8601 let data = try decoder.decode([DefaultSnapshot].self, from: try Data(contentsOf: configuration.fileURL)) var result = [PersistentIdentifier: DefaultSnapshot]() data.forEach { s in result[s.persistentIdentifier] = s } return result } else { return [:] } } func write(_ data: [PersistentIdentifier : DefaultSnapshot]) throws { let encoder = JSONEncoder() encoder.dateEncodingStrategy = .iso8601 encoder.outputFormatting = [.prettyPrinted, .sortedKeys] let jsonData = try encoder.encode(data.values.map({ $0 })) try jsonData.write(to: configuration.fileURL) } } The data model classes: import SwiftData @Model class Settings { private(set) var version = 1 @Relationship(deleteRule: .cascade) var hack: Hack? = Hack() init() { } } @Model class Hack { var foo = "Foo" var bar = 42 init() { } } Container: lazy var mainContainer: ModelContainer = { do { let url = // URL to file let configuration = JSONStoreConfiguration(name: "Settings", schema: Schema([Settings.self, Hack.self]), fileURL: url) return try ModelContainer(for: Settings.self, Hack.self, configurations: configuration) } catch { fatalError("Container error: \(error.localizedDescription)") } }() Load function, that saves a new Settings JSON file if there isn't an existing one: @MainActor func loadSettings() { let mainContext = mainContainer.mainContext let descriptor = FetchDescriptor<Settings>() let settingsArray = try? mainContext.fetch(descriptor) print("\(settingsArray?.count ?? 0) settings found") if let settingsArray, let settings = settingsArray.last { print("Loaded") } else { let settings = Settings() mainContext.insert(settings) do { try mainContext.save() } catch { print("Error saving settings: \(error)") } } } The save operation creates a JSON file, which while it isn't a format I would choose, is acceptable, though I notice that the "hack" property (the relationship) doesn't have the correct identifier. When I run the app again to load the data, I get an error (that there wasn't room to include in this post). Even if I change Apple's code to not assign a new identifier, so the relationship property and its pointee have the same identifier, it still doesn't load. Am I doing something obviously wrong, or are relationships not supported in custom data stores?
2
0
664
Apr ’25
SwiftData "Auto Inserts" array into ModelContext
Definitely one of the stranger quirks of SwiftData I've come across. I have a ScriptView that shows Line entities related to a Production, and a TextEnterScriptView that’s presented in a sheet to input text. I’m noticing that every time I type in the TextEditor within TextEnterScriptView, a new Line shows up in ScriptView — even though I haven’t explicitly inserted it into the modelContext. I'm quite confused because even though I’m only assigning a new Line to a local @State array in TextEnterScriptView, every keystroke in the TextEditor causes a duplicate Line to appear in ScriptView. In other words, Why is SwiftData creating new Line entities every time I type in the TextEditor, even though I’m only assigning to a local @State array and not explicitly inserting them into the modelContext? Here is my minimal reproducible example: import SwiftData import SwiftUI @main struct testApp: App { var body: some Scene { WindowGroup { ContentView() .modelContainer(for: Line.self, isAutosaveEnabled: false) } } } struct ContentView: View { @Environment(\.modelContext) var modelContext @Query(sort: \Production.title) var productions: [Production] var body: some View { NavigationStack { List(productions) { production in NavigationLink(value: production) { Text(production.title) } } .navigationDestination(for: Production.self) { production in ScriptView(production: production) } .toolbar { Button("Add", systemImage: "plus") { let production = Production(title: "Test \(productions.count + 1)") modelContext.insert(production) do { try modelContext.save() } catch { print(error) } } } .navigationTitle("Productions") } } } struct ScriptView: View { @Query private var lines: [Line] let production: Production @State private var isShowingSheet: Bool = false var body: some View { List { ForEach(lines) { line in Text(line.content) } } .toolbar { Button("Show Sheet") { isShowingSheet.toggle() } } .sheet(isPresented: $isShowingSheet) { TextEnterScriptView(production: production) } } } struct TextEnterScriptView: View { @Environment(\.dismiss) var dismiss @State private var text = "" @State private var lines: [Line] = [] let production: Production var body: some View { NavigationStack { TextEditor(text: $text) .onChange(of: text, initial: false) { lines = [Line(content: "test line", production: production)] } .toolbar { Button("Done") { dismiss() } } } } } @Model class Production { @Attribute(.unique) var title: String @Relationship(deleteRule: .cascade, inverse: \Line.production) var lines: [Line] = [] init(title: String) { self.title = title } } @Model class Line { var content: String var production: Production? init(content: String, production: Production?) { self.content = content self.production = production } }
1
0
44
Apr ’25