iCloud & Data

RSS for tag

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

CloudKit Documentation

Post

Replies

Boosts

Views

Activity

CloudKit is not accessible from iOS extension targets
Hi! I'm using CoreData + CloudKit. It works well both on macOS and iOS, however, I can't make it work with extensions (share, action, keyboard). I get Invalid bundle ID for container error: <CKSchedulerActivity: 0x3029f4d20; identifier=com.apple.coredata.cloudkit.activity.export.A65D5B7A-18AA-400A-B25F-F042E46646F6, priority=2, container=iCloud.com.org.app.dev:Sandbox, relatedApplications=( "com.org.App.dev.App-Keyboard" ), xpcActivityCriteriaOverrides={ ActivityGroupName = "com.apple.coredata.cloudkit.App Keyboard.A65D5B7A-18AA-400A-B25F-F042E46646F6"; Delay = 0; Priority = Utility; }> error: CoreData+CloudKit: -[NSCloudKitMirroringDelegate _recoverFromPartialError:forStore:inMonitor:](2812): <NSCloudKitMirroringDelegate: 0x303fd82d0>: Error recovery failed because the following fatal errors were found: { "<CKRecordZoneID: 0x300ef9bc0; zoneName=com.apple.coredata.cloudkit.zone, ownerName=__defaultOwner__>" = "<CKError 0x300efa5e0: \"Permission Failure\" (10/2007); server message = \"Invalid bundle ID for container\"; op = xxxxxxx; uuid = zzzzz-xxxxx; container ID = \"iCloud.com.org.app.dev\">"; } I checked everything 10x: profiles, bundle ids, entitlements, etc. I even removed all local provisioning profiles and recreated them, I also tried setting different CloudKit container, but nothing helps. I tested it on a real device. My setup: main app bundle id: com.org.App.dev keyboard bundle id: com.org.App.dev.App-Keyboard action extension bundle id: com.org.App.dev.Action-Extension CloudKit container id: iCloud.com.org.app.dev I keep the CoreData database in the app group container, but I also tried locally and it doesn't really matter. This is how I setup my CoreData: self.persistentContainer = NSPersistentCloudKitContainer(name: "AppCoreModel") persistentContainer.persistentStoreDescriptions = [createCloudStoreDescription()] persistentContainer.loadPersistentStores { [self] _, error in if let error { logError("Could not load Core Data store \(error)") } else { persistentContainer.viewContext.automaticallyMergesChangesFromParent = true persistentContainer.viewContext.mergePolicy = NSMergeByPropertyObjectTrumpMergePolicy logDebug(persistentContainer.persistentStoreDescriptions.first?.url?.absoluteString ?? "") logDebug("Core Data store loaded") } } private func createCloudStoreDescription() -> NSPersistentStoreDescription { let cloudStoreOptions = NSPersistentCloudKitContainerOptions( containerIdentifier: "iCloud.com.org.app.dev" ) cloudStoreOptions.databaseScope = .private let documentsUrl = FileManager.default.containerURL(forSecurityApplicationGroupIdentifier: AppConstants.appGroupId)! let cloudStoreDescription = NSPersistentStoreDescription( url: documentsUrl.appendingPathComponent("cloud-database.sqlite") ) cloudStoreDescription.type = NSSQLiteStoreType cloudStoreDescription.cloudKitContainerOptions = cloudStoreOptions cloudStoreDescription.setOption(true as NSNumber, forKey: NSPersistentHistoryTrackingKey) cloudStoreDescription.setOption(true as NSNumber, forKey: NSPersistentStoreRemoteChangeNotificationPostOptionKey) return cloudStoreDescription } Any help would be highly appreciated. It seems like iOS bug, because everything seems to be configured properly. I even checked app identifiers if containers are properly assigned. Similar issue when using CloudKit directly (unresolved): https://developer.apple.com/forums/thread/665280
3
0
494
Jul ’24
CloudKit Integration Issue: Record Type Not Found
Hello everyone, I'm working on an iOS app that uses CloudKit for data synchronization. I'm encountering an issue where my app can't find the "JournalPrompt" record type in the public database. Here's the relevant code and error messages (I'm using placeholders like [APP_NAME] or [CONTAINER_IDENTIFIER]): private func fetchPromptsFromiCloud() { let container = CKContainer(identifier: "[CONTAINER_IDENTIFIER]") let publicDatabase = container.publicCloudDatabase // Create a predicate to query for the specific record let predicate = NSPredicate(format: "recordID.recordName == %@", "B6663053-FC2E-4645-938B-9FA528D59663") let query = CKQuery(recordType: "JournalPrompt", predicate: predicate) publicDatabase.perform(query, inZoneWith: nil) { [weak self] (records, error) in if let error = error as? CKError { if error.code == .unknownItem { print("JournalPrompt record type does not exist or the specific record was not found in the public database.") } else { print("Error fetching record from iCloud public database: \(error)") } return } guard let record = records?.first else { print("No record found with the specified ID in the public database.") return } print("Found record in public database:") print("Record ID: \(record.recordID.recordName)") print("Text: \(record["text"] as? String ?? "No text")") print("Creation Date: \(record.creationDate ?? Date())") print("Used Count: \(record["usedCount"] as? Int ?? 0)") print("Is Default: \(record["isDefault"] as? Bool ?? false)") } } Error When I run this code, I get the following error: Error fetching record from iCloud public database: <CKError 0x600000c072a0: "Invalid Arguments" (12/1009); "Invalid predicate: recordKey (recordID.recordName) contains invalid characters"> I've also implemented a function to check the CloudKit schema: func checkCloudKitSchema() { checkDatabase(scope: .private) checkDatabase(scope: .public) } private func checkDatabase(scope: CKDatabase.Scope) { let container = CKContainer(identifier: "[CONTAINER_IDENTIFIER]") let database = scope == .private ? container.privateCloudDatabase : container.publicCloudDatabase print("Checking \(scope == .private ? "private" : "public") database") database.fetchAllRecordZones { (zones, error) in if let error = error { print("Error fetching record zones: \(error)") return } print("Available record zones in \(scope == .private ? "private" : "public") database:") zones?.forEach { zone in print("- \(zone.zoneID.zoneName)") } let query = CKQuery(recordType: "JournalPrompt", predicate: NSPredicate(value: true)) database.perform(query, inZoneWith: nil) { (records, error) in if let error = error as? CKError, error.code == .unknownItem { print("JournalPrompt record type does not exist in the \(scope == .private ? "private" : "public") database.") } else if let error = error { print("Error fetching records from \(scope == .private ? "private" : "public") database: \(error)") } else if let records = records, !records.isEmpty { print("JournalPrompt record type exists in the \(scope == .private ? "private" : "public") database.") print("Fetched \(records.count) JournalPrompt records:") for record in records { print("Record ID: \(record.recordID.recordName)") print("Fields:") record.allKeys().forEach { key in print(" - \(key): \(type(of: record[key]))") } print("---") } } else { print("JournalPrompt record type exists in the \(scope == .private ? "private" : "public") database, but no records found.") } } } } When I run this, I get: Checking public database Available record zones in public database: _defaultZone JournalPrompt record type does not exist in the public database. CloudKit Database Setup I've set up my CloudKit Database as follows: And my data model is as follows: Despite this setup, my app can't seem to find or interact with the JournalPrompt record type. I've double-checked that my app's identifier matches the one in the CloudKit dashboard, and I've verified that the record type name is spelled correctly. Questions: Why might my app be unable to find the JournalPrompt record type, even though it's defined in the CloudKit dashboard? Is there anything wrong with my query or error handling that could be causing this issue? Are there any common pitfalls or setup steps I might have missed when integrating CloudKit? Any insights or suggestions would be greatly appreciated. I really appreciate any help you can provide.
2
0
274
Sep ’24
Xcode 16 broke my SwiftData application
I'm building an application with SwiftUI and SwiftData. Up until a couple days ago, everything was working fine. Then, Xcode auto-updated to v16 in the background, and the next time I opened Xcode and tried to build my app it wouldn't build anymore, citing some errors in expanding the SwiftData @Model macro on one of my objects. Attached are the errors specifically, shown where Xcode shows them (in the expanded @Model macro). In text, they are: Instance method 'access(_:keyPath:)' requires that 'Member' conform to 'Observable' Cannot convert value of type 'Risers.Member' to expected argument type 'Member' Instance method 'withMutation(of:keyPath:_:)' requires that 'Member' conform to 'Observable' Cannot convert value of type 'Risers.Member' to expected argument type 'Member' Here is the SwiftData class in full: import SwiftData import SwiftUI @Model class Member: Identifiable, Hashable { var chorus: Chorus? var id = UUID() var firstName: String var lastName: String var fullName: String { "\(firstName) \(lastName)" } var voicePart: Int var voicePartString: String? { chorus?.voicePartType.prettyName(forPart: voicePart) } @Attribute(.externalStorage) var pictureData: Data init(chorus: Chorus? = nil, firstName: String = "", lastName: String = "", voicePart: Int = 1, pictureData: Data = Data()) { self.chorus = chorus self.firstName = firstName self.lastName = lastName self.voicePart = voicePart self.pictureData = pictureData } init(member: Member) { self.chorus = member.chorus self.firstName = member.firstName self.lastName = member.lastName self.voicePart = member.voicePart self.pictureData = member.pictureData } I tried building again on Xcode 15.4, and it still builds successfully there. Xcode 16.1 beta has not made a difference. Is this my fault, or is Xcode 16 broken?
4
1
593
Sep ’24
SwiftData & CloudKit: getting info about current updates
I have an app which uses SwiftData and CloudKit all works fine and well. Now I wanted to implement a feature which lets the user know that there are data incoming from the cloud and they have to wait a little bit for the data to show up. Furthermore my app needs to do some data sanitation when it starts up. This sanitation should only be done after the CloudKit updates are processed. So is there a way that my app can know when CloudKit is doing updates and when it is finished? I was looking for some kind of notification but couldn’t find any info on that. I don’t need to know which data are updated or process the data. I just want to get notified when a sync starts and when it has ended. Actually it would suffice to know when a sync is finished.
2
0
334
Sep ’24
Help with 2 way relationships with classes (SwiftData / cloudkit)
Hi I’m having real problems trying to get a simple “to do” type app working with cloudkit. It works fine with SwiftData but as soon as I add CloudKit I get lots of “container not found errors “ which I think relates to the relationships between my classes. If I strip out the group sort order class it works fine. I’ve stripped the app back to basics to test - I want to be able to add a “task” (task data) to a “group list “ (group data) also also store the position of the task in that list (group sort order) as there may be lots of tasks in a list. The same task could also be in a different group list with a different position in the list (so another entry and value in group sort order) .. why does the following not work?? any help appreciated! //  TaskData.swift //  TaskOutApp // import Foundation import SwiftData `@Model class TaskData: Identifiable, Equatable { var id = UUID() var title: String = "No Title"     var isDone: Bool = false     var isToday: Bool = false     var creationDate: Date = Date()          var doneDate: Date = Date()     var todayDate: Date = Date()     // Use an array of GroupSortOrder to maintain both group and sort order     var groupSortOrders: [GroupSortOrder]? = nil          init(id: UUID = UUID(), title: String = "No Title", isDone: Bool = false, isToday: Bool = false, creationDate: Date = Date(), doneDate: Date = Date(), todayDate: Date = Date(), groupSortOrders: [GroupSortOrder]? = nil) {         self.id = id         self.title = title         self.isDone = isDone         self.isToday = isToday         self.creationDate = creationDate         self.doneDate = doneDate         self.todayDate = todayDate         self.groupSortOrders = groupSortOrders     }          static func currentDateString() -> String {         let formatter = DateFormatter()         formatter.dateStyle = .short         formatter.timeStyle = .short         return formatter.string(from: Date())     }          static func == (lhs: TaskData, rhs: TaskData) -> Bool {         lhs.id == rhs.id     } } @Model class GroupData: Identifiable {     var id = UUID()     var title: String = "no title"     var icon: String = "no icon"     var creationDate: Date = Date()     var task: [TaskData]? = []          init(id: UUID = UUID(), title: String, icon: String, creationDate: Date = Date(), task: [TaskData] = []) {         self.id = id         self.title = title         self.icon = icon         self.creationDate = creationDate         self.task = task     }          static func currentDateString() -> String {         let formatter = DateFormatter()         formatter.dateStyle = .short         formatter.timeStyle = .short         return formatter.string(from: Date())     } } @Model class GroupSortOrder: Identifiable {     var id = UUID()     var group: GroupData? = nil     var sortOrder: Int = 0          init(id: UUID = UUID(), group: GroupData? = nil, sortOrder: Int = 0) {         self.id = id         self.group = group         self.sortOrder = sortOrder     } }`
3
0
362
Sep ’24
Xcode 16.0. SwiftData Schema Fatal error: Inverse already set to another relationship
Hi, after upgrading to Xcode 16.0 I encountered the fact that when loading preview, I get an error: Fatal error: Inverse already set to another relationship - secondAccount - cannot assign to - firstAccount import Foundation import SwiftData import SwiftUI @Model class TransactionModel { @Relationship(inverse: \AccountModel.accountTransactions) var firstAccount: AccountModel? /// <- here @Relationship(inverse: \AccountModel.accountTransactions) var secondAccount: AccountModel? /// <- here @Relationship(inverse: \DebtorModel.transactions) var debtor: DebtorModel? @Relationship(inverse: \CategoryModel.transactions) var category: CategoryModel? init(account: AccountModel? = nil, secondAccount: AccountModel? = nil, debtor: DebtorModel? = nil, category: CategoryModel? = nil) { self.firstAccount = account self.secondAccount = secondAccount self.debtor = debtor self.category = category } } import SwiftData import SwiftUI @Model final public class AccountModel: Identifiable, Hashable { var accountId: UUID = UUID() var accountTransactions: [TransactionModel]? init(id: UUID = UUID()) { self.accountId = id } } Is this really not possible to implement? In Xcode 15, this worked both on the device and in the editor. Thanks!
1
0
393
Sep ’24
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.
6
10
1.5k
Jul ’24
Reverse relationships for models are not always set (iOS 18 RC)
I'm running into an odd case where a model's reverse relationship is sometimes not set despite the forward relationship being there. If the app is closed and reopened however, the reverse relationship for previously added data works. For example, given three models Shelf, Item and ItemDetails: @Model final class Shelf { @Relationship(deleteRule: .cascade, inverse: \Item.primaryShelf) var items: [Item] = [] init() {} } @Model final class Item { var primaryShelf: Shelf? var timestamp: Date @Relationship(deleteRule: .cascade, inverse: \ItemDetail.item) public var detail: ItemDetail? init(primaryShelf: Shelf) { self.primaryShelf = primaryShelf self.timestamp = .now } } @Model final class ItemDetail { var item: Item? init(item: Item) { self.item = item } } Now I want to simply create a shelf, some items and some itemdetails. @Test func testRelationshipsThroughInit() async throws { let schema = Schema([Shelf.self, Item.self, ItemDetail.self]) let config = ModelConfiguration(schema: schema, isStoredInMemoryOnly: true) let container = try ModelContainer(for: schema, configurations: [config]) let modelContext = ModelContext(container) let shelf = Shelf() modelContext.insert(shelf) for _ in 0..<10 { let item = Item(primaryShelf: shelf) modelContext.insert(item) let itemDetail = ItemDetail(item: item) modelContext.insert(itemDetail) } try modelContext.save() let fetchDescriptor = FetchDescriptor<Shelf>() let shelves = try modelContext.fetch(fetchDescriptor) // fails with a random number between 0 and 9 typically #expect(shelves.first?.items.count == 10) } There seem to be two ways that this problem goes away. The first is changing the order of properties set in ItemDetail.init() so that the relationship is set after everything else: @Model final class Item { // ... init(primaryShelf: Shelf) { self.timestamp = .now self.primaryShelf = primaryShelf } With this, everything seems to work fine. The other way seems to be manually setting the reverse relationship. So the loop above gets changed to: for _ in 0..<10 { let item = Item(primaryShelf: shelf) modelContext.insert(item) let itemDetail = ItemDetail(item: item) modelContext.insert(itemDetail) // add reverse relationship even though forward was set shelf.items.append(item) } My question is, is this the expected behavior and If so, is there any place this is documented?
1
0
474
Sep ’24
Batch delete many-to-one not working
Doing a batch delete on a many-to-one relationship seems to throw this error CoreData: error: Unhandled opt lock error from executeBatchDeleteRequest Constraint trigger violation: Batch delete failed due to mandatory OTO nullify inverse on Student/school and userInfo { NSExceptionOmitCallstacks = 1; NSLocalizedFailureReason = "Constraint trigger violation: Batch delete failed due to mandatory OTO nullify inverse on Student/school"; "_NSCoreDataOptimisticLockingFailureConflictsKey" = ( ); } If I try to delete the School in the one-to-many relationship, both the school and the students are deleted as expected. However, If I try to delete all students the error is thrown. I would expect all students to be removed, while keeping the School intact. Do SwiftData support this? import XCTest import SwiftData @Model class School { var name: String @Relationship(deleteRule: .cascade, inverse: \Student.school) var students: [Student] = [] init(name: String) { self.name = name } } @Model class Student { var name: String var school: School? init(name: String) { self.name = name } } final class Test: XCTestCase { func testScenario() throws { let config = ModelConfiguration(isStoredInMemoryOnly: true) let modelContainer = try ModelContainer(for: School.self, Student.self, configurations: config ) let context = ModelContext(modelContainer) context.autosaveEnabled = false let school = School(name: "school") context.insert(school) let student1 = Student(name: "1") let student2 = Student(name: "2") context.insert(student1) context.insert(student2) student1.school = school student2.school = school XCTAssertEqual(school.students.count, 2) XCTAssertEqual(student1.school?.id, school.id) XCTAssertEqual(student2.school?.id, school.id) try context.save() let newContext = ModelContext(modelContainer) // try newContext.delete(model: School.self) // This works try newContext.delete(model: Student.self) // This one fails } }
1
2
263
Sep ’24
SwiftData inverse relationship not updating
Given the code below the students array on the school is not being updated. Why? Since the relationship is explicit and non-optional I would expect this to work. import XCTest import SwiftData @Model class School { var name: String @Relationship(deleteRule: .cascade, inverse: \Student.school) var students: [Student] init(name: String, students: [Student]) { self.name = name self.students = students } } @Model class Student { var name: String var school: School init(name: String, school: School) { self.name = name self.school = school } } final class Test: XCTestCase { func testScenario() throws { let modelContainer = try ModelContainer(for: School.self, Student.self ) let context = ModelContext(modelContainer) context.autosaveEnabled = false let school = School(name: "school", students: []) context.insert(school) let student1 = Student(name: "1", school: school) let student2 = Student(name: "2", school: school) context.insert(student1) context.insert(student2) XCTAssertEqual(school.students.count, 2) // XCTAssertEqual failed: ("0") is not equal to ("2") } }
2
0
372
Sep ’24
Custom struct Codable for SwiftData
I'm encountering an issue encoding/decoding a custom struct from SwiftData. As it's all happening behind the generated code of SwiftData and a decoder, I'm not really sure what's going on. I have a custom type defined kind of like this: public struct Group<Key: Hashable, Element: Hashable> { private var elementGroups: [Element: Key] private var groupedElements: [Key: [Element]] } In short, it allows multiple elements (usually a string), to be grouped, referenced by some key. I have Codable conformance to this object, so I can encode and decode it. For simplicity, the elementGroups is encoded/decoded, and the groupedElements is rebuilt when decoding. My implementation is similar to this: extension Group: Codable where Key: Codable, Element: Codable { private enum Keys: CodingKey { case groups } public init(from decoder: Decoder) throws { let container = try decoder.container(keyedBy: Keys.self) let decoded = try container.decode([Element: Key].self, forKey: .groups) // Enumerate over the element groups, and populate the list of elements. // var elements: [Key: [Element]] = [:] for group in decoded { elements[group.value] = (elements[group.value] ?? []) + [group.key] } elementGroups = decoded groupedElements = elements } public func encode(to encoder: Encoder) throws { var container = encoder.container(keyedBy: Keys.self) try container.encode(elementGroups, forKey: .groups) } } This works fine when encoding and decoding to JSON, but when I attempt to use this structure as a value within a SwiftData model, then decoding the type crashes my application. @Model final class MyModel { var id: UUID = UUID() var groups: Group<UUID, String> = Group<UUID, String>() init(id: UUID) { self.id = id } } When something attempts to decode the groups property on the MyModel object, it crashes with the following error: Could not cast value of type 'Swift.Optional<Any>' (0x1ed7d0290) to 'Swift.Dictionary<Swift.String, Foundation.UUID>' (0x121fa9448). I would guess that there is a nil value stored for groups in SwiftData, and attempting to decode it to a Group<UUID, String> type is failing. It's odd that it doesn't throw an exception though, and hard crashes. Also, I'm not sure why it's optional, as a value is being written out. Does anyone have any ideas?
4
1
1.3k
Oct ’23
How can I sync users with code/invite like that (image) with cloudkit, is it possible?
Users will receive a unique ID, if a user enters another user's ID they will go to a view where both have access to the information, being able to change, add, delete... (Paired, available on App Store) Public container is not secure, private with ckshare doesn't seem to work for what I would like, plus the content is very confusing I need something that uses native Apple technologies to build this system.
1
0
254
Sep ’24
Is SwiftData's #Unique currently broken or am I missing something?
Hi, I am inserting two models where the "unique" attribute is the same. I was under the impression, that this should result in an upsert and not two inserts of the model, but that is not the case. See the test coding below for what I am doing (it is self contained, so if you want to try it out, just copy it into a test target). The last #expect statement fails because of the two inserts. Not sure if this is a bug (Xcode 16 beta 2 on Sonoma running an iOS 18 simulator) or if I am missing something here... // MARK: - UniqueItem - @Model final class UniqueItem { #Unique<UniqueItem>([\.no]) var timestamp = Date() var title: String var changed = false var no: Int init(title: String, no: Int) { self.title = title self.no = no } } // MARK: - InsertTests - @Suite("Insert Tests", .serialized) struct InsertTests { var sharedModelContainer: ModelContainer = { let schema = Schema([ UniqueItem.self, ]) let modelConfiguration = ModelConfiguration(schema: schema, isStoredInMemoryOnly: false) do { return try ModelContainer(for: schema, configurations: [modelConfiguration]) } catch { fatalError("Could not create ModelContainer: \(error)") } }() @Test("Test unique.") @MainActor func upsertAndModify() async throws { let ctx = sharedModelContainer.mainContext try ctx.delete(model: UniqueItem.self) let item = UniqueItem(title: "Item \(1)", no: 0) ctx.insert(item) let allFD = FetchDescriptor<UniqueItem>() let count = try ctx.fetchCount(allFD) #expect(count == 1) let updatedItem = UniqueItem(title: "Item \(1)", no: 0) updatedItem.changed = true ctx.insert(updatedItem) // we should still have only 1 item because of the unique constraint let allCount = try ctx.fetchCount(allFD) #expect(allCount == 1) } }
2
2
661
Jun ’24
Troubleshooting Core Data Lightweight Migration: A Real-World Challenge
In my recent endeavor, I aimed to introduce new Fetch Index Elements to the Core Data model of my iOS application. To achieve this, I followed a process of lightweight migration, detailed as follows: Navigate to Editor > Add Model Version to create a new version of the data model. Name the new version with a sequential identifier (e.g., MyAppModelV3.xcdatamodel) based on the naming convention of previous models. Select the newly created version, MyAppModelV3.xcdatamodel, as the active model. Mark this new version as the "Current" model in the Xcode properties panel on the right. In the new version of the model, MyAppModelV3.xcdatamodel, and add the new Fetch Index Elements there. Also, insert "v3" in the Versioning Hash Modifier field of affected entity, to indicate this modification. Upon reflection, I realized that creating a new version of the xcdatamodel might not have been necessary for this particular case. However, it appears to have caused no adverse effects on the application's functionality. During testing, I executed the application in a simulated environment, initially running an older version of the app to inspect the database content with SQLite DB Browser. I then upgraded to the latest app version to verify that the migration was successfully completed without causing any crashes. Throughout this testing phase, I employed the -com.apple.CoreData.MigrationDebug 1 flag to monitor all SQL operations, ensuring that indexes were appropriately dropped and recreated for the affected entity. Following thorough testing, I deployed the update to production. The majority of users were able to upgrade to the new app version seamlessly. However, a small fraction reported crashes at startup, indicated by the following error message: Fatal error: Unresolved error Error Domain=NSCocoaErrorDomain Code=134110 "An error occurred during persistent store migration." UserInfo={NSUnderlyingError=0x2820ad3e0 {Error Domain=NSCocoaErrorDomain Code=134100 "The managed object model version used to open the persistent store is incompatible with the one that was used to create the persistent store." UserInfo={metadata={ NSPersistenceFrameworkVersion = 1338; NSStoreModelVersionChecksumKey = "qcPf6+DfpsPrDQ3j1EVXcBIrFe1O0R6IKd30sJf4IrI="; NSStoreModelVersionHashes = { NSAttachment = {length = 32, ... Strangely, the only way I could replicate this issue in the simulator was by running the latest version of the app followed by reverting to an older version, a scenario unlikely to occur in a real-world setting. This raises the question: How could this situation arise with actual users, considering they would typically move from an old to a new version rather than the reverse? I am reaching out to the community for insights or advice on this matter. Has anyone else encountered a similar problem during the Core Data migration process? How did you resolve it?
1
1
628
Mar ’24
Name of .xcdatamodel gets cleared out automtically from pbxproj file.
I have added core data to my project and below is the code block of how it's section in pbxproj file looks like. However whenever I make any changes to the project i.e. changing build version or adding a deleting new files to the project, I have noticed "name = "sample-app.xcdatamodeld";" gets deleted automatically from the project file and I have to manually reverse that change. Am I missing any setting for core data or is it a bug somewhere in XCode or Core Data? I am using XCode 15. /* Begin XCVersionGroup section */ 4683EC5B2C10F8B800A5081B /* sample-app.xcdatamodeld */ = { isa = XCVersionGroup; children = ( 4683EC5C2C10F8B800A5081B /* sample-app.xcdatamodel */, ); currentVersion = 4683EC5C2C10F8B800A5081B /* sample-app.xcdatamodel */; name = "sample-app.xcdatamodeld"; path = "sample-app.xcdatamodeld"; sourceTree = "<group>"; versionGroupType = wrapper.xcdatamodel; }; /* End XCVersionGroup section */
5
0
454
Sep ’24
SwiftData serious bug with relationships and CloudKit in iOS 18.0 (Xcode 16 Beta)
Hi guys. Can someone please confirm this bug so I report it? The issue is that SwiftData relationships don't update the views in some specific situations on devices running iOS 18 Beta. One clear example is with CloudKit. I created a small example for testing. The following code creates two @models, one to store bands and another to store their records. The following code works with no issues. (You need to connect to a CloudKit container and test it on two devices) import SwiftUI import SwiftData struct ContentView: View { @Environment(\.modelContext) private var modelContext @Query private var records: [Record] var body: some View { NavigationStack { List(records) { record in VStack(alignment: .leading) { Text(record.title) Text(record.band?.name ?? "Undefined") } } .toolbar { ToolbarItem { Button("Add Record") { let randomNumber = Int.random(in: 1...100) let newBand = Band(name: "New Band \(randomNumber)", records: nil) modelContext.insert(newBand) let newRecord = Record(title: "New Record \(randomNumber)", band: newBand) modelContext.insert(newRecord) } } } } } } @Model final class Record { var title: String = "" var band: Band? init(title: String, band: Band?) { self.title = title self.band = band } } @Model final class Band { var name: String = "" var records: [Record]? init(name: String, records: [Record]?) { self.name = name self.records = records } } This view includes a button at the top to add a new record associated with a new band. The data appears on both devices, but if you include more views inside the List, the views on the second device are not updated to show the values of the relationships. For example, if you extract the row to a separate view, the second device shows the relationships as "Undefined". You can try the following code. struct ContentView: View { @Environment(\.modelContext) private var modelContext @Query private var records: [Record] var body: some View { NavigationStack { List { ForEach(records) { record in RecordRow(record: record) } } .toolbar { ToolbarItem { Button("Add Record") { let randomNumber = Int.random(in: 1...100) let newBand = Band(name: "New Band \(randomNumber)", records: nil) modelContext.insert(newBand) let newRecord = Record(title: "New Record \(randomNumber)", band: newBand) modelContext.insert(newRecord) } } } } } } struct RecordRow: View { let record: Record var body: some View { VStack(alignment: .leading) { Text(record.title) Text(record.band?.name ?? "Undefined") } } } Here I use a ForEach loop and move the row to a separate view. Now on the second device the relationships are nil, so the row shows the text "Undefined" instead of the name of the band. I attached an image from my iPad. I inserted all the information on my iPhone. The first three rows were inserted with the first view. But the last two rows were inserted after I extracted the rows to a separate view. Here you can see that the relationships are nil and therefore shown as "Undefined". The views are not updated to show the real value of the relationship. This example shows the issue with CloudKit, but this also happens locally in some situations. The system doesn't detect updates in relationships and therefore doesn't refresh the views. Please, let me know if you can reproduce the issue. I'm using Mac Sequoia 15.1, and two devices with iOS 18.0.
2
0
488
Sep ’24
Issue storing SIMD3<Float> (and other simd’s) in a SwiftData model
I'm encountering an issue when trying to store a SIMD3<Float> in a SwiftData model. Since SIMD3<Float> already conforms to Codable, I expected it to work. However, attempting to store a single SIMD3<Float> crashes with the following error: Fatal error: Unexpected property within Persisted Struct/Enum: Builtin.Vec4xFPIEEE32 Interestingly, storing an array of vectors, [SIMD3<Float>], works perfectly fine. The issue only arises when trying to store a single SIMD3<Float>. I’m not looking for a workaround (I can break the vector into individual floats in a custom codable struct to get by) , but I’d like to understand why storing a codable SIMD3<Float> in SwiftData results in this crash. Is this a limitation of SwiftData, or is there something I’m missing about how vectors are handled? Any insights would be greatly appreciated!
3
0
335
Sep ’24
How to access SwiftData from a background thread?
Consider a sample SwiftData project var body: some View { List(recipes) { recipe in NavigationLink(recipe.name, destination: RecipeView(recipe)) } } For SwiftUI views that uses SwiftData it's very straight forward. However, if I need to read/write to SwiftData while in the background (let's say after a network call), the app crashes. Imagine a simple workflow where (1) user selects some model objects (via SwiftData modelContext) (2) now I need to pass these objects to some API server (3) in a background thread somewhere I need to use the modelActor but it now cannot read/write to the original model objects without risk of crashing So instead, the only way I thought of is to create a separate modelContext for background processing. There I passed down the container from the main app, and creates a new ModelActor that takes in the container and owns a new modelContext of its own. This works, without crash. However it introduces many side effects: (1) read/write from this modelActor seems to trigger view changes for SwiftData's modelContext. But not vice versa. (2) model objects fetched from one context cannot be used in another context. (3) changes made in the actor also doesn’t not automatically sync with icloud but changes in SwiftData’s modelContext do. So I guess the bigger question here is, what is the proper usage of SwiftData in a background thread?
9
0
576
Sep ’24