SwiftData

RSS for tag

SwiftData is an all-new framework for managing data within your apps. Models are described using regular Swift code, without the need for custom editors.

Posts under SwiftData tag

200 Posts
Sort by:

Post

Replies

Boosts

Views

Activity

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
617
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
371
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
408
Sep ’24
@Relationship crash on ios17.5 but not on ios18
In my app, I worked with ios18 by default and I had no issue, everything was working fine. However, when I wanted to test it with an iOS 17.5 simulator or real device, it is unusable because it crash when object with a relationship are created. on the first line you can see my relationship, and under it it is the extended relationship macro unmodifiable. has someone already encountered this? any clue of a hide modification of relationship working behavior in ios18? this is really annoying cause it make me unable to reduce the minimum deployment target of my app to ios17
4
0
434
Oct ’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
281
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
345
Sep ’24
Live queries on SwiftData DB but without @Query macro?
I switched from using @Query to @ModelActor because of the following reasons: Performance Issues: With @Query, my app became unresponsive with large datasets because data fetching occurred on the main thread. Integration with CKSyncEngine: I needed to implement @ModelActor anyway to allow CKSyncEngine to add data to local persistent storage from the background. In my current setup, the onAppear() method for my view calls the getItems() function on my model actor, which returns [ItemsDTO] and then View renders them. However, I'm now facing a challenge in achieving the same automatic data refreshing and view updates that @Query provided. Here are a few potential solutions I'm considering: Periodic Data Fetching: Fetch data at regular intervals to keep the view updated. But this seems expensive. Local Write Monitoring: Monitor all local writes to automatically trigger updates when changes occur. But this is quite a lot of code I would have to write myself. Switch to manual refresh: Users would have to manually trigger UI updates by pressing button. Reintroduce @Query: Writes would happen from ModelActor, but reads would still happen from Main thread. But then again app would become unresponsive on reads. If you have any additional ideas or best practices for maintaining reactivity with @ModelActor, I'd love to hear them!
1
1
479
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
390
Sep ’24
I Keep getting this error in my swift project.
Since the iOS 18 and Xcode 16, I've been getting some really strange SwiftData errors when passing @Model classes around. SwiftData/BackingData.swift:409: Fatal error: This model instance was destroyed by calling ModelContext.reset and is no longer usable. PersistentIdentifier(id: SwiftData.PersistentIdentifier.ID(url: x-coredata://D0F0E233-8D1D-4020-924B-BA56959140FD/ListModel/p10), implementation: SwiftData.PersistentIdentifierImplementation) The same issue also happens when I try to retrieve a model from the ModelContext using its PersistentIdentifier and try to do anything with it. I have no idea what could be causing this. This is my actor @ModelActor actor ListCrudOperations:ObservableObject{ func add(list:ListModel){ modelContext.insert(list) try? modelContext.save() } func delete(identifier:PersistentIdentifier){ guard let list = self[identifier, as: ListModel.self] else { print("error") return } if list.listType == .task{ list.reminders!.forEach { reminder in TaskModel.delete(modelContext: modelContext, reminder: reminder) NotificationService.deleteReminders(name: reminder.title!, Id: reminder.id) } } else if list.listType == .subscription { list.subscription!.forEach({ sub in Subscription.delete(modelContext: modelContext, subscription: sub) NotificationService.deleteReminders(name: sub.ServiceName, Id: sub.id) }) } else if list.listType == .link { list.links!.forEach ({link in Links.delete(modelContext: modelContext, link: link) NotificationService.deleteNotificationForLink(title: link.name, linkID: link.id) } ) } modelContext.delete(list) try? modelContext.save() } func addReminder(reminder:TaskModel, identifier:PersistentIdentifier){ guard let list = self[identifier, as: ListModel.self] else { print("error") return } list.reminders!.append(reminder) reminder.list = list try? modelContext.save() } func addSubscription(subscription:Subscription, identifier:PersistentIdentifier){ guard let list = self[identifier, as: ListModel.self] else { print("error") return } list.subscription!.append(subscription) subscription.list = list try? modelContext.save() } func addLink(link:Links, identifier: PersistentIdentifier) { guard let list = self[identifier, as: ListModel.self] else { print("error") return } list.links?.append(link) link.list = list try? modelContext.save() } func fetchListByType(type:ListType) -> [ListModel] { let type = SwiftTaskSchemaV8.ListModel.ListType(rawValue: type.rawValue)! let fetchDescriptor = FetchDescriptor<ListModel>() do { let list = try modelContext.fetch(fetchDescriptor) let list2 = try list.filter(#Predicate { $0.listType == type }) return list2 }catch{ return [] } } func fetchListsForMultipleTypes(_ types: [ListType]) -> [ListModel] { return types.flatMap { type in fetchListByType(type: type) } } func fetchAllList() -> [ListModel] { let fetchDescriptor = FetchDescriptor<ListModel>(sortBy: [.init(\.createdDate)]) do { let list = try modelContext.fetch(fetchDescriptor) return list }catch{ return [] } } }``` and this is how i am calling it @Environment(.modelContext) private var context let listOperation = ListCrudOperations(modelContainer: context.container) let list = ListModel(name: name, color: self.color, icon: self.icon, listType: ListModel.ListType(rawValue: picked.rawValue)!) Task { await listOperation.add(list: list) await MainActor.run{ withAnimation(.bouncy){ self.list.append(list) } CrashServices.shared.addLogs(message: "folder added") } }
4
1
560
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
498
Sep ’24
Renaming SwiftData model causing an error
I'm still working on version 1.0.0 of my app so don't yet need to migrate swiftdata versions. I renamed my data model(s) but I get this error when running the app after changing the name of the model from SectionsSD to HelpSection error: the replacement path doesn't exist: "/var/folders/fk/m2x_2qzj44508m9zzcll2j_w0000gn/T/swift-generated-sources/@__swiftmacro_11OpExShellV112FilteredListV11helpSection5QueryfMa_.swift" Despite cleaning the build, deleting the derived data and deleting the app and data on the simulator (and resetting it) I still get the replacement path error whenever I run the app in the simulator. When I run this code against the model I get the error: @Query var helpSection: [HelpSection] let searchString: String let isExpanded: Bool var filteredSections: [HelpSection] { helpSection.filter { section in section.toArticles?.contains { $0.search.contains(searchString) } == true } .sorted { $0.rank < $1.rank } } deleting derived data, cleaning builds and restarts do not resolve the issue.
2
2
568
Sep ’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
486
Sep ’24
Swift Data issue when property implements custom Codable conformance.
I have a type which represents an ID. This ID comes from a backend as an untyped string. The Swift type looks something like this: struct TypedID: Codable { init(_ string: String) { stringValue = string } init(from decoder: any Decoder) throws { stringValue = try decoder.singleValueContainer().decode(String.self) } func encode(to encoder: any Encoder) throws { try stringValue.encode(to: encoder) } let stringValue: String } If I use this type in a SwiftData @Model, or even as a property of another Codeable struct which is contained in the @Model, SwiftData fails to create instances of my model and emits the following error: CoreData: error: CoreData: error: Row (pk = #) for entity '<@Model type>' is missing mandatory text data for property 'stringValue' This issue goes away if I remove the custom implementation of init(from:) and encode(to:), but doing so makes the coded representation of my type an object instead of a string, which is not what I want. /// JSON without custom implementations: { "stringValue": "<the ID>" } /// JSON with custom implementations: "<the ID>" Is there anything I can do to be able to serialize a Codable type with a custom coding implementation like this to a SwiftData model?
3
0
450
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
349
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
608
Sep ’24
SwiftData regression in iOS 18 RC, contexts don't sync
In iOS 18 RC, and the iOS 18 simulator shipped with Xcode 16.0 RC, there is a regression where ModelContexts on the same ModelContainer do not sync changes. A minimal example is below, but briefly: create an object in context1. Retrieve and update that object in context2, then save context2. The changes cannot be found in context1 in iOS 18 RC, but can in iOS 17 and earlier betas of iOS 18. I've submitted this as FB15092827 but am posting here for visibility to others. I'm going to have to scramble to see if I can mitigate this in our impacted app before iOS 18 launches. It's affecting us when doing background inserts in a ModelActor to populate our app UI, but you can see below the effects are seen even on the same thread in a very simple two-context example. @Test("updates sync between contexts") func crossContextSync() async throws { // overview: // create an employee in context 1 // update the employee in context 2 // check that the update is available in context 1 let context1 = ModelContext(demoAppContainer) let context2 = ModelContext(demoAppContainer) // create an employee in context 1 let newEmployee = Employee(salary: 0) context1.insert(newEmployee) try context1.save() #expect(newEmployee.salary == 0, "Created with salary 0") // update the employee in context 2 let employeeID = newEmployee.uuid let predicate: Predicate<Employee> = #Predicate<Employee> { employee in employee.uuid == employeeID } let fetchedEmployee = try #require(try? context2.fetch(FetchDescriptor<Employee>(predicate: predicate)).first) #expect(fetchedEmployee.uuid == newEmployee.uuid, "We got the correct employee in the new context") let updatedSalary = 1 fetchedEmployee.salary = updatedSalary try context2.save() // FAILURE IS HERE. This passes in earlier iOS betas and in iOS 17.X #expect(newEmployee.salary == updatedSalary, "Salary was update in context 1") // Create a new modelContext on the same container, since the container does have the changes in it. // By creating this new context we can get updated data and the test below passes in all iOS versions tested. This may be a mitigation path but creating new contexts any time you need to access data is painful. let context3 = ModelContext(demoAppContainer) let fetchedEmployeeIn3 = try #require(try? context3.fetch(FetchDescriptor<Employee>(predicate: predicate)).first) #expect(fetchedEmployeeIn3.uuid == newEmployee.uuid, "We got the correct employee in the new context3") #expect(fetchedEmployeeIn3.salary == updatedSalary, "Salary was update in context 1") } Code below if you want to build a working example, but the test above is very simple let demoAppContainer = try! ModelContainer(for: Employee.self) @main struct ModelContextsNotSyncedToContainerApp: App { init() { } var body: some Scene { WindowGroup { ContentView() .modelContainer(demoAppContainer) } } } @Model final class Employee { var uuid: UUID = UUID() var salary: Int init(salary: Int = 0) { self.salary = salary } }
4
0
498
Sep ’24
SwiftData/ModelContext.swift:3253: Fatal error: Failed to identify a store that can hold instances of SwiftData._KKMDBackingData<Presents_2024.Item> from [:]
I'm still getting this error (SwiftData/ModelContext.swift:3253: Fatal error: Failed to identify a store that can hold instances of SwiftData._KKMDBackingData<Presents_2024.Item> from [:]) in Xcode 16.1 Beta (16B5001e). The app works for a limited amount of time and then crashes with this error. It looks like the SwiftData model isn't being created properly and when a context is saved it crashes. Can you tell me if this error will be fixed in the next beta?
9
5
853
2w
SwiftData Relationship Persistence
I'm still getting started with SwiftData and having trouble understanding how to persist a model with a relationship in a container. I've tried to distill the example below to be pretty simple. Here are some example model definitions. I would like to be able to define a n Item. That Item can have SubItems related to it in a one-to-many fashion. Each SubItem is required to be attached to a parent Item. final class Item { var name: String var subitems: [Item]? init( name: String, subitems: [SubItem]? = nil ) { self.name = name } } @Model final class SubItem { var name: String init(name: String) { self.name = name } } In my app I am then defining a preview container with some pre-saved data already. Item( name: "item1", subitems: [ SubItem(name: "subItemA"), SubItem(name: "subItemB") ] ), Item( name: "item2", subitems: [ SubItem(name: "subItemC"), SubItem(name: "subItemD") ] ) ] @MainActor let PreviewContainer: ModelContainer = { do { let schema = Schema([ Item.self, SubItem.self, ]) let container = try ModelContainer( for: schema, configurations: ModelConfiguration(isStoredInMemoryOnly: true) ) for item in PreviewItems { container.mainContext.insert(item) for subItem in item.subitems! { container.mainContext.insert(subItem) } } return container } catch { fatalError("Failed to create container") } }() My app is then defined as follows... struct SwiftDataTestApp: App { var body: some Scene { WindowGroup { ContentView() } .modelContainer(PreviewContainer) } } struct ContentView: View { @Environment(\.modelContext) private var modelContext @Query private var items: [Item] var body: some View { HStack { VStack { Text(items[0].name) Text(String(items[0].subitems!.count)) // Text(items[0].subitems![0].name) // Text(items[0].subitems![1].name) } Spacer() VStack { Text(items[1].name) Text(String(items[1].subitems!.count)) // Text(items[0].subitems![0].name) // Text(items[0].subitems![1].name) } } .padding(100) } } #Preview { ContentView() .modelContainer(PreviewContainer) } The preview loads without an issue, but if I uncomment the lines that access the SubItems it crashes. In the preview I can also see that each Item has 0 SubItems related to it. For some reason, the model container is not actually storing the `SubItem even though they are defined in PreviewItems. Some things I've tried Explicitly adding the relationship Adding a Item property in SubItem to link back to it's parent In the PreviewContainer definition, manually insert the SubItems as well as the parent Items Any help is appreciated, thanks
1
0
255
Sep ’24