Model your schema with SwiftData

RSS for tag

Discuss the WWDC23 Session Model your schema with SwiftData

Posts under wwdc2023-10195 tag

31 Posts

Post

Replies

Boosts

Views

Activity

@Attribute 'unique' and complex keys
The 'unique' attribute is a really nice feature, BUT. In some of my apps, the unique identifier for an object is a combination of multiple attributes. (Example: a book title is not unique, but a combination of book title and author list is.) How do I model this with SwiftData? I cannot use @Attribute(.unique) on either the title OR the author list, but I want SwiftData to provide the same "insert or update" logic. Is this possible?
5
4
3.2k
Sep ’25
#Predicate doesn't work with enum
Problem The following code doesn't work: let predicate = #Predicate<Car> { car in car.size == size //This doesn't work } Console Error Query encountered an error: SwiftData.SwiftDataError(_error: SwiftData.SwiftDataError._Error.unsupportedPredicate) Root cause Size is an enum, #Predicate works with other type such as String however doesn't work with enum Enum value is saved however is not filtered by #Predicate Environment Xcode: 15.0 (15A240d) - App Store macOS: 14.0 (23A339) - Release Candidate Steps to reproduce Run the app on iOS 17 or macOS Sonoma Press the Add button Notice that the list remains empty Expected behaviour List should show the newly created small car Actual behaviour List remains empty inspite of successfully creating the small car. Feedback FB13194334 Code Size enum Size: String, Codable { case small case medium case large } Car import SwiftData @Model class Car { let id: UUID let name: String let size: Size init( id: UUID, name: String, size: Size ) { self.id = id self.name = name self.size = size } } ContentView struct ContentView: View { var body: some View { NavigationStack { CarList(size: .small) } } CarList import SwiftUI import SwiftData struct CarList: View { let size: Size @Environment(\.modelContext) private var modelContext @Query private var cars: [Car] init(size: Size) { self.size = size let predicate = #Predicate<Car> { car in car.size == size //This doesn't work } _cars = Query(filter: predicate, sort: \.name) } var body: some View { List(cars) { car in VStack(alignment: .leading) { Text(car.name) Text("\(car.size.rawValue)") Text(car.id.uuidString) .font(.footnote) } } .toolbar { Button("Add") { createCar() } } } private func createCar() { let name = "aaa" let car = Car( id: UUID(), name: name, size: size ) modelContext.insert(car) } }
6
1
2.5k
May ’25
Do I need to add my own unique id?
Hi, if I have a @Model class there's always an id: PersistentIdentifier.ID underneath which, according to the current documentation "The value that uniquely identifies the associated model within the containing store.". So I am wondering if it is (good) enough to rely on this attribute to uniquely identify @Model class entities, or if there are edge cases where it does not work (like maybe when using CloudKit)? If anybody saw some information regarding this, please let me know :-) Cheers, Michael
5
3
3.4k
Aug ’24
SwiftData modelContainer Error
It's been frustrating to solve this error. My iOS device and Xcode are fully updated. I can easily run app on simulator, but issue happens on my iPhone. dyld[23479]: Symbol not found: _$s9SwiftData12ModelContextC6insert6objectyx_tAA010PersistentC0RzlFTj Referenced from: <6FC773BB-E68B-35A9-B334-3FFC8B951A4E> Expected in: /System/Library/Frameworks/SwiftData.framework/SwiftData
2
3
1.5k
May ’24
SwiftData with two Stores
Hi, has anybody managed to get two sqlite stores working? If I define the stores with a configuration for each it seems like that only the first configuration and and therefore the store is recognised. This is how I define the configuration and container: import SwiftData @main struct SwiftDataTestApp: App { var modelContainer: ModelContainer init() { let fullSchema = Schema([ SetModel.self, NewsModel.self ]) let setConfiguration = ModelConfiguration( "setconfig", schema: Schema([SetModel.self]), url: FileManager.default.urls(for: .documentDirectory, in: .userDomainMask).first!.appendingPathComponent("Sets.sqlite"), readOnly: false) let newsConfiguration = ModelConfiguration( "newsconfig", schema: Schema([NewsModel.self]), url: FileManager.default.urls(for: .cachesDirectory, in: .userDomainMask).first!.appendingPathComponent("News.sqlite"), readOnly: false) modelContainer = try! ModelContainer(for: fullSchema, configurations: [setConfiguration,newsConfiguration]) } var body: some Scene { WindowGroup { ContentView() } .modelContainer(modelContainer) } } ContentView is just a basic TabView with a tab for news and a tab for sets. If I run the program this way the sets tab is shown correctly but switching to News fails. If I change the order of the configurations and write the one for news first like this: modelContainer = try! ModelContainer(for: fullSchema, configurations: [newsConfiguration, setConfiguration]) then the news tab is shown correctly and switching to sets tab fails. NewsModel and SetModel only differ in the class name Import Foundation import SwiftData @Model public class NewsModel{ public var name: String init(name: String) { self.name = name } } Also the tab content differs only for referencing the respecting model and the name: import SwiftData struct NewsTab: View { @Query private var news: [NewsModel] @Environment(\.modelContext) private var modelContext var body: some View { ScrollView{ LazyVStack{ ForEach(news){actNews in Text("Hello, News \(actNews.name)") } } .onAppear { let news = NewsModel(name: "News from \(Date())") modelContext.insert(news) try! modelContext.save() } } } } The error message is "NSFetchRequest could not locate an NSEntityDescription for entity name 'NewsModel'" (and SetsModel respectively when change the order of the configuration) Do I explicitly need to tell the modelContext which configuration it should use or is this done automatically? I'm a little lost here and hope someone can help me. Best regards, Sven
5
0
3.5k
Feb ’24
SwiftData @Query crashes when trying to filter or sort using an enum or relationship
Like the title says, I've realised that when I try to use filter or sort on properties that aren't standard supported data types i.e. Using a transformable or a value type like an enum, I seem to be getting the following crash... SwiftData/DataUtilities.swift:1140: Fatal error: Unexpected type for Expansion: Optional<UIColor> Xcode expands and shows me when trying to access the wrapped value it's crashing. I'm assumung that the query property wrapper can't handle these custom data types @Query private var items: [Item] { get { _items.wrappedValue <--- Crash here } } Which seems to be pointing to a transferable property in one of my models. Below are my two models i'm using. enum Priority: Int, Codable, Identifiable, CaseIterable { case low case medium case high var title: String { switch self { case .low: return "Low" case .medium: return "Medium" case .high: return "High" } } var image: Image? { switch self { case .medium: return Image(systemName: "exclamationmark.2") case .high: return Image(systemName: "exclamationmark.3") default: return nil } } var id: Self { self } } @Model final class Item: Codable { var title: String @Attribute(originalName: "timestamp") var dueDate: Date var isCompleted: Bool var isFlagged: Bool = false var isArchived: Bool = false var isCritical: Bool? var priority: Priority? @Relationship(deleteRule: .nullify, inverse: \Category.items) var category: Category? @Attribute(.externalStorage) var image: Data? enum CodingKeys: String, CodingKey { case title case timestamp case isCritical case isCompleted case category case imageName } init(title: String = "", dueDate: Date = .now, priority: Priority? = nil, isCompleted: Bool = false) { self.title = title self.dueDate = dueDate self.priority = priority self.isCompleted = isCompleted } init(from decoder: Decoder) throws { let container = try decoder.container(keyedBy: CodingKeys.self) self.title = try container.decode(String.self, forKey: .title) self.dueDate = Date.randomDateNextWeek() ?? .now self.isCompleted = try container.decode(Bool.self, forKey: .isCompleted) self.category = try container.decodeIfPresent(Category.self, forKey: .category) if let imageName = try container.decodeIfPresent(String.self, forKey: .imageName), let imageData = UIImage(named: imageName) { self.image = imageData.jpegData(compressionQuality: 0.8) } if let isCritical = try container.decodeIfPresent(Bool.self, forKey: .isCritical), isCritical == true { self.priority = .high } } func encode(to encoder: Encoder) throws { var container = encoder.container(keyedBy: CodingKeys.self) try container.encode(title, forKey: .title) try container.encode(dueDate, forKey: .timestamp) try container.encode(isCompleted, forKey: .isCompleted) try container.encode(category, forKey: .category) } } @Model class Category: Codable { @Attribute(.unique) var title: String var items: [Item]? @Attribute(.transformable(by: ColorValueTransformer.self)) var color: UIColor? init(title: String = "", color: UIColor) { self.title = title self.color = color } enum CodingKeys: String, CodingKey { case title } required init(from decoder: Decoder) throws { let container = try decoder.container(keyedBy: CodingKeys.self) self.title = try container.decode(String.self, forKey: .title) self.color = UIColor(possibleColors.randomElement()!) } func encode(to encoder: Encoder) throws { var container = encoder.container(keyedBy: CodingKeys.self) try container.encode(title, forKey: .title) } } And below is an example of me sorting based on my enum (Priority) & Relationship (Category name) func sort() -> [SortDescriptor<Item>]{ switch self { case .title: [SortDescriptor(\Item.title)] case .date: [SortDescriptor(\Item.dueDate)] case .category: [SortDescriptor(\Item.category?.title)] case .priority: [SortDescriptor(\Item.priority?.rawValue)] } } And a filter example below creating a predicate that we will execute to return and matches found in the title or category title let highPriority = Priority.high if let query { return #Predicate { $0.priority == highPriority && ($0.title.contains(query) || $0.category?.title.contains(query) == true) && $0.isArchived == false } } I'm pretty sure this is a SwiftData bug since when using strings, bools and dates it's all fine using anything outside of that box causes these crashes...
8
1
3.5k
Feb ’24
Lightweight Migration Issues
I am trying to run a lightweight migration in which I am changing the name of a model property from name to title. The database is already populated with few records. Those records must be preserved. Here is my schema versions: enum TripsSchemaV1: VersionedSchema { static var versionIdentifier: String? = "Initial version" static var models: [any PersistentModel.Type] { [Trip.self] } @Model class Trip { var name: String init(name: String) { self.name = name } } } enum TripsSchemaV2: VersionedSchema { static var versionIdentifier: String? = "name changed to title" static var models: [any PersistentModel.Type] { [Trip.self] } @Model class Trip { @Attribute(originalName: "name") var title: String init(title: String) { self.title = title } } } Migration plan: enum TripsMigrationPlan: SchemaMigrationPlan { static var schemas: [any VersionedSchema.Type] { [TripsSchemaV1.self, TripsSchemaV2.self] } static var stages: [MigrationStage] { [migrateV1toV2] } static let migrateV1toV2 = MigrationStage.lightweight(fromVersion: TripsSchemaV1.self, toVersion: TripsSchemaV2.self) } And finally the usage: @main struct TripsApp: App { let container: ModelContainer init() { do { container = try ModelContainer(for: [Trip.self], migrationPlan: TripsMigrationPlan.self, ModelConfiguration(for: [Trip.self])) } catch { fatalError("Could not initialize the container.") } } var body: some Scene { WindowGroup { ContentView() .modelContainer(container) } } } When I run the app, all my data for the Trips is gone and I get the following message on the output window. Unresolved error loading container Error Domain=NSCocoaErrorDomain Code=134504 "Cannot use staged migration with an unknown coordinator model version." UserInfo={NSLocalizedDescription=Cannot use staged migration with an unknown coordinator model version.} Any ideas?
7
0
1.9k
Dec ’23
SwiftData Configurations for Private and Public CloudKit
I did manage to save my Entities to CloudKit with SwiftData but the default database is the private database. I need to store some Entities in the private and other Entities in the public CloudKit database. How do I manage that with SwiftData? With CoreData I always used different configurations for both private and public and added the entities to one or the other.
6
4
6.8k
Nov ’23
Can't query for the existence of an optional to-one relationship?
Hi, say in my model I have members and each member optionally can have a relationship to a Club. So the relationship in the Member entity would be modelled like so: @Relationship(.nullify, inverse: \Club.members) var club: Club? Now I would like to fetch al Members with no Club relationship. I would assume that this would work with a predicate like this: let noClubPred = #Predicate<Member> { member in member.club == nil } Unfortunately this gives me the following error when compiling: Generic parameter 'RHS' could not be inferred. Has anybody an idea how to phrase this predicate correctly, or is this a beta issue and it should actually work? Thank you! Cheers, Michael
2
1
1.4k
Sep ’23
Xcode 15.0 Beta 5, Symbol not found: _$s9SwiftData014DefaultBackingB0C3forACyxGxm_tcfC
When running a macOS app on Xcode 15 Beta 5 (15A5209g), I get a Symbol Not Found error. Problem: App will not run. Error received. Symbol Not Found: SwiftData Default Backing For AC Environment: Version 15.0 beta 5 (15A5209g), macOS 14.0 Beta (23A5257q) App: macOS SwiftData Model @Model class Audit { init() { } } Error: dyld[875]: Symbol not found: _$s9SwiftData014DefaultBackingB0C3forACyxGxm_tcfC Referenced from: <75DF3350-4DD5-3AF4-80DA-B17B0EDD26C2> /Users/dking/Library/Developer/Xcode/DerivedData/{redacted}-bigzojxvffztaaaepdczriowvoie/Build/Products/Debug/{redacted}.app/Contents/MacOS/{redacted} Expected in: /System/Library/Frameworks/SwiftData.framework/Versions/A/SwiftData
22
7
4.5k
Sep ’23
SwiftData Many to One Cascade Delete not working as I expect in Xcode15 Beta 7 & 8
This approach worked in Beta 5, but does not work in Betas 7 & 8. (I skipped Beta 6, so I don't know if it worked then). I'm not sure if it's a SwiftData bug or a mistake on my part since the API has changed since the WWDC video. (I opened Apple Feedback: FB13120831) I am trying to implement a cascade delete on a many to one relationship. See the two models below. When I deleted the Recipe Model object, I expect that any of the FoodMenus that have that recipe will also be deleted. But they are not. The Recipe is deleted and the FoodMenu is still there. When I put a deleteRule: .cascade on the FoodMenu.recipe, the cascade does work. The Menu and the Associated recipe are both deleted. But that's not the behavior I want. @Model final class Recipe { var name: String var id: UUID @Relationship(deleteRule: .cascade, inverse: \FoodMenu.recipe) var menus: [FoodMenu]? init(name: String, id: UUID, menus: [FoodMenu]? = []) { self.name = name self.id = id self.menus = menus } } @Model final class FoodMenu { var name: String var id: UUID var recipe: Recipe? = nil //I believe the below definition should be the same behavior as above and it works as expected. relationship is nullified when the FoodMenu is deleted. Recipe left in place. // @Relationship (deleteRule: .nullify, inverse: \Recipe.menus) // var recipe: Recipe? = nil init(name: String, id: UUID) { self.name = name self.id = id } }
1
0
1.6k
Sep ’23
Error in SwiftData migrationPlan execution
I'm testing the new SwiftData to see if I can use it on one of my apps. Specifically I'm trying to learn how to use the schema migrations. I made a new project using Xcode 15 beta 4. I selected to use SwiftData and iCloud. I ran the generated project and I added a couple of items to the screen. Then, I wanted to make a schema migration, so I changed the Item model from: @Model final class Item { var timestamp: Date init(timestamp: Date) { self.timestamp = timestamp } } to also include a string title: @Model final class Item { var timestamp: Date var title: String init(timestamp: Date, title: String) { self.timestamp = timestamp self.title = title } } I also made some changes to the SwiftUI code to include an empty string when adding a new item, so it could compile: let newItem = Item(timestamp: Date(), title: "") And then I added a new file containing the migration, according to what I saw on the video sessions: // // SwiftDataMigrations.swift // Test 1 // // Created by Diego on 12/07/23. // import Foundation import SwiftData enum ItemSchemaV1: VersionedSchema { static var versionIdentifier: String? static var models: [any PersistentModel.Type] { [Item.self] } @Model final class Item { var timestamp: Date init(timestamp: Date) { self.timestamp = timestamp } } } enum ItemSchemaV2: VersionedSchema { static var versionIdentifier: String? static var models: [any PersistentModel.Type] { [Item.self] } @Model final class Item { var timestamp: Date var title: String init(timestamp: Date, title: String) { self.timestamp = timestamp self.title = title } } } enum ItemsMigrationPlan: SchemaMigrationPlan { static var schemas: [any VersionedSchema.Type] { [ItemSchemaV1.self, ItemSchemaV2.self] } static var stages: [MigrationStage] { [migrateV1toV2] } static let migrateV1toV2 = MigrationStage.lightweight(fromVersion: ItemSchemaV1.self, toVersion: ItemSchemaV2.self) } After that, I specified the migrationPlan in the app: // // Test_1App.swift // Test 1 // // Created by Diego on 12/07/23. // import SwiftUI import SwiftData @main struct Test_1App: App { var container = try! ModelContainer( for: Item.self, migrationPlan: ItemsMigrationPlan.self ) var body: some Scene { WindowGroup { ContentView() } .modelContainer(container) } } And then I tried to run the app but it crashed on launch with this error: SwiftData/ModelContext.swift:179: Fatal error: Container does not have any data stores Does anyone have any idea about what I might be missing? The only thing that didn't matched the videos was that when creating a ModelContainer, the code on the video showed var container = ModelContainer... but the compiler showed the error Call can throw, but errors cannot be thrown out of a property initializer, so I added the try!. Also, on the VersionedSchema, I had to add static var versionIdentifier: String?. Other than that I have no idea. Thanks in advance.
5
1
2.2k
Sep ’23
SwiftData framework crashes application when enumerations are part of arrays
If I use a Codable enum as the type in an array property on a Model, I get a crash. The message is something like: Illegal attempt to use a Optional(Swift.Mirror.DisplayStyle.enum) as a Property Minor change code example from a fresh project using SwiftData: @Model final class Item { var timestamp: Date var choices: [Choice] init(timestamp: Date, choices: [Choice]) { self.timestamp = timestamp self.choices = choices } } enum Choice: String, Codable { case beep, bloop } ContentView (largely unchanged): import SwiftUI import SwiftData struct ContentView: View { @Environment(\.modelContext) private var modelContext @Query private var items: [Item] var body: some View { NavigationView { List { ForEach(items) { item in NavigationLink { VStack { Text("Item at \(item.timestamp, format: Date.FormatStyle(date: .numeric, time: .standard))") Text("\(item.choices.map({"\($0.rawValue)"}).joined())") } } label: { Text(item.timestamp, format: Date.FormatStyle(date: .numeric, time: .standard)) } } .onDelete(perform: deleteItems) } .toolbar { #if os(iOS) ToolbarItem(placement: .navigationBarTrailing) { EditButton() } #endif ToolbarItem { Button(action: addItem) { Label("Add Item", systemImage: "plus") } } } Text("Select an item") } } private func addItem() { withAnimation { let newItem = Item(timestamp: Date(), choices: [.bloop, .beep]) modelContext.insert(newItem) } } private func deleteItems(offsets: IndexSet) { withAnimation { for index in offsets { modelContext.delete(items[index]) } } } } #Preview { ContentView() .modelContainer(for: Item.self, inMemory: true) } This is strange and seems like a bug as if I use Codable structs in this instance it is fine. Yet, if that Codable struct has an enum as a property, it also crashes with a different error. Working: import Foundation import SwiftData @Model final class Item { var timestamp: Date var choices: [Choice] init(timestamp: Date, choices: [Choice]) { self.timestamp = timestamp self.choices = choices } } struct Choice: Codable { let foo: String } Also doesn't work: import Foundation import SwiftData @Model final class Item { var timestamp: Date var choices: [Choice] init(timestamp: Date, choices: [Choice]) { self.timestamp = timestamp self.choices = choices } } struct Choice: Codable { enum SpecificChoice: String, Codable { case beep, bloop } let foo: SpecificChoice } It seems really odd and arbitrary that an enum would cause these issues. There may be other cases involving enums, but to wrap up it seems enums as properties of Codables that live within an array on a SwiftData model, or an array of enums on a SwiftData model cause crashes.
5
3
2.2k
Sep ’23
Inserting a Model entity with a relationship results in a runtime error.
Hi, when inserting an entity with a relationship I get the following runtime error: Illegal attempt to establish a relationship 'group' between objects in different contexts [...]. The model looks like this: @Model class Person { var name: String @Relationship(.nullify, inverse: \Group.members) var group: Group init(name: String) { self.name = name } } @Model class Group { var name: String @Relationship(.cascade) public var members: [Person] init(name: String) { self.name = name } } It can be reproduced using this (contrived) bit of code: let group = Group(name: "Group A") ctx.insert(group) try! ctx.save() let descriptor = FetchDescriptor<Group>() let groups = try ctx.fetch(descriptor) XCTAssertFalse(groups.isEmpty) XCTAssertEqual(groups.count, 1) XCTAssertTrue(groups.first?.name == "Group A") let person = Person(name: "Willy") person.group = group ctx.insert(person) try ctx.save() (See also full test case below). Anybody experiencing similar issues? Bug or feature? Cheers, Michael Full test case: import SwiftData import SwiftUI import XCTest // MARK: - Person - @Model class Person { var name: String @Relationship(.nullify, inverse: \Group.members) var group: Group init(name: String) { self.name = name } } // MARK: - Group - @Model class Group { var name: String @Relationship(.cascade) public var members: [Person] init(name: String) { self.name = name } } // MARK: - SD_PrototypingTests - final class SD_PrototypingTests: XCTestCase { var container: ModelContainer! var ctx: ModelContext! override func setUpWithError() throws { let fullSchema = Schema([Person.self, Group.self,]) let dbCfg = ModelConfiguration(schema: fullSchema) container = try ModelContainer(for: fullSchema, dbCfg) ctx = ModelContext(container) _ = try ctx.delete(model: Group.self) _ = try ctx.delete(model: Person.self) } override func tearDownWithError() throws { guard let dbURL = container.configurations.first?.url else { XCTFail("Could not find db URL") return } do { try FileManager.default.removeItem(at: dbURL) } catch { XCTFail("Could not delete db: \(error)") } } func testRelAssignemnt_FB12363892() throws { let group = Group(name: "Group A") ctx.insert(group) try! ctx.save() let descriptor = FetchDescriptor<Group>() let groups = try ctx.fetch(descriptor) XCTAssertFalse(groups.isEmpty) XCTAssertEqual(groups.count, 1) XCTAssertTrue(groups.first?.name == "Group A") let person = Person(name: "Willy") person.group = group ctx.insert(person) try ctx.save() } }
4
3
2.7k
Aug ’23
How the new relationships in SwiftData work?
There is a new Relationship macro in Xcode Beta 6. The macro includes two new arguments: minimumModelCount and maximumModelCount. I wonder if anyone knows what these values are for and if there is another change under the hood. Relationship( _ options: PropertyOptions..., deleteRule: Schema.Relationship.DeleteRule = .nullify, minimumModelCount: Int? = 0, maximumModelCount: Int? = 0, originalName: String? = nil, inverse: AnyKeyPath? = nil, hashModifier: String? = nil )
1
1
867
Aug ’23
No "upsert" when working with .unique attributes
Hi, in the session the following is mentioned: If a trip already exists with that name, then the persistent back end will update to the latest values. This is called an upsert. An upsert starts as an insert. If the insert collides with existing data, it becomes an update and updates the properties of the existing data. Nevertheless, if I have a unique constraint on an (String) attribute and try to insert the same again, I end up in the debugger in the generated getter of the attribute: @Attribute(.unique) public var name: String { get { _$observationRegistrar.access(self, keyPath: \.name) return self.getValue(for: \.name) // <- here } EXC_BREAKPOINT (code=1, subcode=0x1a8d6b724) Am I missing something? If this is expected behaviour, how should I prevent this crash (other than checking for uniqueness before every insert)? Thank you! Cheers, Michael
4
5
2.8k
Aug ’23
@Attribute 'unique' and complex keys
The 'unique' attribute is a really nice feature, BUT. In some of my apps, the unique identifier for an object is a combination of multiple attributes. (Example: a book title is not unique, but a combination of book title and author list is.) How do I model this with SwiftData? I cannot use @Attribute(.unique) on either the title OR the author list, but I want SwiftData to provide the same "insert or update" logic. Is this possible?
Replies
5
Boosts
4
Views
3.2k
Activity
Sep ’25
#Predicate doesn't work with enum
Problem The following code doesn't work: let predicate = #Predicate<Car> { car in car.size == size //This doesn't work } Console Error Query encountered an error: SwiftData.SwiftDataError(_error: SwiftData.SwiftDataError._Error.unsupportedPredicate) Root cause Size is an enum, #Predicate works with other type such as String however doesn't work with enum Enum value is saved however is not filtered by #Predicate Environment Xcode: 15.0 (15A240d) - App Store macOS: 14.0 (23A339) - Release Candidate Steps to reproduce Run the app on iOS 17 or macOS Sonoma Press the Add button Notice that the list remains empty Expected behaviour List should show the newly created small car Actual behaviour List remains empty inspite of successfully creating the small car. Feedback FB13194334 Code Size enum Size: String, Codable { case small case medium case large } Car import SwiftData @Model class Car { let id: UUID let name: String let size: Size init( id: UUID, name: String, size: Size ) { self.id = id self.name = name self.size = size } } ContentView struct ContentView: View { var body: some View { NavigationStack { CarList(size: .small) } } CarList import SwiftUI import SwiftData struct CarList: View { let size: Size @Environment(\.modelContext) private var modelContext @Query private var cars: [Car] init(size: Size) { self.size = size let predicate = #Predicate<Car> { car in car.size == size //This doesn't work } _cars = Query(filter: predicate, sort: \.name) } var body: some View { List(cars) { car in VStack(alignment: .leading) { Text(car.name) Text("\(car.size.rawValue)") Text(car.id.uuidString) .font(.footnote) } } .toolbar { Button("Add") { createCar() } } } private func createCar() { let name = "aaa" let car = Car( id: UUID(), name: name, size: size ) modelContext.insert(car) } }
Replies
6
Boosts
1
Views
2.5k
Activity
May ’25
Do I need to add my own unique id?
Hi, if I have a @Model class there's always an id: PersistentIdentifier.ID underneath which, according to the current documentation "The value that uniquely identifies the associated model within the containing store.". So I am wondering if it is (good) enough to rely on this attribute to uniquely identify @Model class entities, or if there are edge cases where it does not work (like maybe when using CloudKit)? If anybody saw some information regarding this, please let me know :-) Cheers, Michael
Replies
5
Boosts
3
Views
3.4k
Activity
Aug ’24
SwiftData modelContainer Error
It's been frustrating to solve this error. My iOS device and Xcode are fully updated. I can easily run app on simulator, but issue happens on my iPhone. dyld[23479]: Symbol not found: _$s9SwiftData12ModelContextC6insert6objectyx_tAA010PersistentC0RzlFTj Referenced from: <6FC773BB-E68B-35A9-B334-3FFC8B951A4E> Expected in: /System/Library/Frameworks/SwiftData.framework/SwiftData
Replies
2
Boosts
3
Views
1.5k
Activity
May ’24
SwiftData with two Stores
Hi, has anybody managed to get two sqlite stores working? If I define the stores with a configuration for each it seems like that only the first configuration and and therefore the store is recognised. This is how I define the configuration and container: import SwiftData @main struct SwiftDataTestApp: App { var modelContainer: ModelContainer init() { let fullSchema = Schema([ SetModel.self, NewsModel.self ]) let setConfiguration = ModelConfiguration( "setconfig", schema: Schema([SetModel.self]), url: FileManager.default.urls(for: .documentDirectory, in: .userDomainMask).first!.appendingPathComponent("Sets.sqlite"), readOnly: false) let newsConfiguration = ModelConfiguration( "newsconfig", schema: Schema([NewsModel.self]), url: FileManager.default.urls(for: .cachesDirectory, in: .userDomainMask).first!.appendingPathComponent("News.sqlite"), readOnly: false) modelContainer = try! ModelContainer(for: fullSchema, configurations: [setConfiguration,newsConfiguration]) } var body: some Scene { WindowGroup { ContentView() } .modelContainer(modelContainer) } } ContentView is just a basic TabView with a tab for news and a tab for sets. If I run the program this way the sets tab is shown correctly but switching to News fails. If I change the order of the configurations and write the one for news first like this: modelContainer = try! ModelContainer(for: fullSchema, configurations: [newsConfiguration, setConfiguration]) then the news tab is shown correctly and switching to sets tab fails. NewsModel and SetModel only differ in the class name Import Foundation import SwiftData @Model public class NewsModel{ public var name: String init(name: String) { self.name = name } } Also the tab content differs only for referencing the respecting model and the name: import SwiftData struct NewsTab: View { @Query private var news: [NewsModel] @Environment(\.modelContext) private var modelContext var body: some View { ScrollView{ LazyVStack{ ForEach(news){actNews in Text("Hello, News \(actNews.name)") } } .onAppear { let news = NewsModel(name: "News from \(Date())") modelContext.insert(news) try! modelContext.save() } } } } The error message is "NSFetchRequest could not locate an NSEntityDescription for entity name 'NewsModel'" (and SetsModel respectively when change the order of the configuration) Do I explicitly need to tell the modelContext which configuration it should use or is this done automatically? I'm a little lost here and hope someone can help me. Best regards, Sven
Replies
5
Boosts
0
Views
3.5k
Activity
Feb ’24
SwiftData @Query crashes when trying to filter or sort using an enum or relationship
Like the title says, I've realised that when I try to use filter or sort on properties that aren't standard supported data types i.e. Using a transformable or a value type like an enum, I seem to be getting the following crash... SwiftData/DataUtilities.swift:1140: Fatal error: Unexpected type for Expansion: Optional<UIColor> Xcode expands and shows me when trying to access the wrapped value it's crashing. I'm assumung that the query property wrapper can't handle these custom data types @Query private var items: [Item] { get { _items.wrappedValue <--- Crash here } } Which seems to be pointing to a transferable property in one of my models. Below are my two models i'm using. enum Priority: Int, Codable, Identifiable, CaseIterable { case low case medium case high var title: String { switch self { case .low: return "Low" case .medium: return "Medium" case .high: return "High" } } var image: Image? { switch self { case .medium: return Image(systemName: "exclamationmark.2") case .high: return Image(systemName: "exclamationmark.3") default: return nil } } var id: Self { self } } @Model final class Item: Codable { var title: String @Attribute(originalName: "timestamp") var dueDate: Date var isCompleted: Bool var isFlagged: Bool = false var isArchived: Bool = false var isCritical: Bool? var priority: Priority? @Relationship(deleteRule: .nullify, inverse: \Category.items) var category: Category? @Attribute(.externalStorage) var image: Data? enum CodingKeys: String, CodingKey { case title case timestamp case isCritical case isCompleted case category case imageName } init(title: String = "", dueDate: Date = .now, priority: Priority? = nil, isCompleted: Bool = false) { self.title = title self.dueDate = dueDate self.priority = priority self.isCompleted = isCompleted } init(from decoder: Decoder) throws { let container = try decoder.container(keyedBy: CodingKeys.self) self.title = try container.decode(String.self, forKey: .title) self.dueDate = Date.randomDateNextWeek() ?? .now self.isCompleted = try container.decode(Bool.self, forKey: .isCompleted) self.category = try container.decodeIfPresent(Category.self, forKey: .category) if let imageName = try container.decodeIfPresent(String.self, forKey: .imageName), let imageData = UIImage(named: imageName) { self.image = imageData.jpegData(compressionQuality: 0.8) } if let isCritical = try container.decodeIfPresent(Bool.self, forKey: .isCritical), isCritical == true { self.priority = .high } } func encode(to encoder: Encoder) throws { var container = encoder.container(keyedBy: CodingKeys.self) try container.encode(title, forKey: .title) try container.encode(dueDate, forKey: .timestamp) try container.encode(isCompleted, forKey: .isCompleted) try container.encode(category, forKey: .category) } } @Model class Category: Codable { @Attribute(.unique) var title: String var items: [Item]? @Attribute(.transformable(by: ColorValueTransformer.self)) var color: UIColor? init(title: String = "", color: UIColor) { self.title = title self.color = color } enum CodingKeys: String, CodingKey { case title } required init(from decoder: Decoder) throws { let container = try decoder.container(keyedBy: CodingKeys.self) self.title = try container.decode(String.self, forKey: .title) self.color = UIColor(possibleColors.randomElement()!) } func encode(to encoder: Encoder) throws { var container = encoder.container(keyedBy: CodingKeys.self) try container.encode(title, forKey: .title) } } And below is an example of me sorting based on my enum (Priority) & Relationship (Category name) func sort() -> [SortDescriptor<Item>]{ switch self { case .title: [SortDescriptor(\Item.title)] case .date: [SortDescriptor(\Item.dueDate)] case .category: [SortDescriptor(\Item.category?.title)] case .priority: [SortDescriptor(\Item.priority?.rawValue)] } } And a filter example below creating a predicate that we will execute to return and matches found in the title or category title let highPriority = Priority.high if let query { return #Predicate { $0.priority == highPriority && ($0.title.contains(query) || $0.category?.title.contains(query) == true) && $0.isArchived == false } } I'm pretty sure this is a SwiftData bug since when using strings, bools and dates it's all fine using anything outside of that box causes these crashes...
Replies
8
Boosts
1
Views
3.5k
Activity
Feb ’24
Lightweight Migration Issues
I am trying to run a lightweight migration in which I am changing the name of a model property from name to title. The database is already populated with few records. Those records must be preserved. Here is my schema versions: enum TripsSchemaV1: VersionedSchema { static var versionIdentifier: String? = "Initial version" static var models: [any PersistentModel.Type] { [Trip.self] } @Model class Trip { var name: String init(name: String) { self.name = name } } } enum TripsSchemaV2: VersionedSchema { static var versionIdentifier: String? = "name changed to title" static var models: [any PersistentModel.Type] { [Trip.self] } @Model class Trip { @Attribute(originalName: "name") var title: String init(title: String) { self.title = title } } } Migration plan: enum TripsMigrationPlan: SchemaMigrationPlan { static var schemas: [any VersionedSchema.Type] { [TripsSchemaV1.self, TripsSchemaV2.self] } static var stages: [MigrationStage] { [migrateV1toV2] } static let migrateV1toV2 = MigrationStage.lightweight(fromVersion: TripsSchemaV1.self, toVersion: TripsSchemaV2.self) } And finally the usage: @main struct TripsApp: App { let container: ModelContainer init() { do { container = try ModelContainer(for: [Trip.self], migrationPlan: TripsMigrationPlan.self, ModelConfiguration(for: [Trip.self])) } catch { fatalError("Could not initialize the container.") } } var body: some Scene { WindowGroup { ContentView() .modelContainer(container) } } } When I run the app, all my data for the Trips is gone and I get the following message on the output window. Unresolved error loading container Error Domain=NSCocoaErrorDomain Code=134504 "Cannot use staged migration with an unknown coordinator model version." UserInfo={NSLocalizedDescription=Cannot use staged migration with an unknown coordinator model version.} Any ideas?
Replies
7
Boosts
0
Views
1.9k
Activity
Dec ’23
SwiftData Configurations for Private and Public CloudKit
I did manage to save my Entities to CloudKit with SwiftData but the default database is the private database. I need to store some Entities in the private and other Entities in the public CloudKit database. How do I manage that with SwiftData? With CoreData I always used different configurations for both private and public and added the entities to one or the other.
Replies
6
Boosts
4
Views
6.8k
Activity
Nov ’23
Can't query for the existence of an optional to-one relationship?
Hi, say in my model I have members and each member optionally can have a relationship to a Club. So the relationship in the Member entity would be modelled like so: @Relationship(.nullify, inverse: \Club.members) var club: Club? Now I would like to fetch al Members with no Club relationship. I would assume that this would work with a predicate like this: let noClubPred = #Predicate<Member> { member in member.club == nil } Unfortunately this gives me the following error when compiling: Generic parameter 'RHS' could not be inferred. Has anybody an idea how to phrase this predicate correctly, or is this a beta issue and it should actually work? Thank you! Cheers, Michael
Replies
2
Boosts
1
Views
1.4k
Activity
Sep ’23
Xcode 15.0 Beta 5, Symbol not found: _$s9SwiftData014DefaultBackingB0C3forACyxGxm_tcfC
When running a macOS app on Xcode 15 Beta 5 (15A5209g), I get a Symbol Not Found error. Problem: App will not run. Error received. Symbol Not Found: SwiftData Default Backing For AC Environment: Version 15.0 beta 5 (15A5209g), macOS 14.0 Beta (23A5257q) App: macOS SwiftData Model @Model class Audit { init() { } } Error: dyld[875]: Symbol not found: _$s9SwiftData014DefaultBackingB0C3forACyxGxm_tcfC Referenced from: <75DF3350-4DD5-3AF4-80DA-B17B0EDD26C2> /Users/dking/Library/Developer/Xcode/DerivedData/{redacted}-bigzojxvffztaaaepdczriowvoie/Build/Products/Debug/{redacted}.app/Contents/MacOS/{redacted} Expected in: /System/Library/Frameworks/SwiftData.framework/Versions/A/SwiftData
Replies
22
Boosts
7
Views
4.5k
Activity
Sep ’23
SwiftData Many to One Cascade Delete not working as I expect in Xcode15 Beta 7 & 8
This approach worked in Beta 5, but does not work in Betas 7 & 8. (I skipped Beta 6, so I don't know if it worked then). I'm not sure if it's a SwiftData bug or a mistake on my part since the API has changed since the WWDC video. (I opened Apple Feedback: FB13120831) I am trying to implement a cascade delete on a many to one relationship. See the two models below. When I deleted the Recipe Model object, I expect that any of the FoodMenus that have that recipe will also be deleted. But they are not. The Recipe is deleted and the FoodMenu is still there. When I put a deleteRule: .cascade on the FoodMenu.recipe, the cascade does work. The Menu and the Associated recipe are both deleted. But that's not the behavior I want. @Model final class Recipe { var name: String var id: UUID @Relationship(deleteRule: .cascade, inverse: \FoodMenu.recipe) var menus: [FoodMenu]? init(name: String, id: UUID, menus: [FoodMenu]? = []) { self.name = name self.id = id self.menus = menus } } @Model final class FoodMenu { var name: String var id: UUID var recipe: Recipe? = nil //I believe the below definition should be the same behavior as above and it works as expected. relationship is nullified when the FoodMenu is deleted. Recipe left in place. // @Relationship (deleteRule: .nullify, inverse: \Recipe.menus) // var recipe: Recipe? = nil init(name: String, id: UUID) { self.name = name self.id = id } }
Replies
1
Boosts
0
Views
1.6k
Activity
Sep ’23
Error in SwiftData migrationPlan execution
I'm testing the new SwiftData to see if I can use it on one of my apps. Specifically I'm trying to learn how to use the schema migrations. I made a new project using Xcode 15 beta 4. I selected to use SwiftData and iCloud. I ran the generated project and I added a couple of items to the screen. Then, I wanted to make a schema migration, so I changed the Item model from: @Model final class Item { var timestamp: Date init(timestamp: Date) { self.timestamp = timestamp } } to also include a string title: @Model final class Item { var timestamp: Date var title: String init(timestamp: Date, title: String) { self.timestamp = timestamp self.title = title } } I also made some changes to the SwiftUI code to include an empty string when adding a new item, so it could compile: let newItem = Item(timestamp: Date(), title: "") And then I added a new file containing the migration, according to what I saw on the video sessions: // // SwiftDataMigrations.swift // Test 1 // // Created by Diego on 12/07/23. // import Foundation import SwiftData enum ItemSchemaV1: VersionedSchema { static var versionIdentifier: String? static var models: [any PersistentModel.Type] { [Item.self] } @Model final class Item { var timestamp: Date init(timestamp: Date) { self.timestamp = timestamp } } } enum ItemSchemaV2: VersionedSchema { static var versionIdentifier: String? static var models: [any PersistentModel.Type] { [Item.self] } @Model final class Item { var timestamp: Date var title: String init(timestamp: Date, title: String) { self.timestamp = timestamp self.title = title } } } enum ItemsMigrationPlan: SchemaMigrationPlan { static var schemas: [any VersionedSchema.Type] { [ItemSchemaV1.self, ItemSchemaV2.self] } static var stages: [MigrationStage] { [migrateV1toV2] } static let migrateV1toV2 = MigrationStage.lightweight(fromVersion: ItemSchemaV1.self, toVersion: ItemSchemaV2.self) } After that, I specified the migrationPlan in the app: // // Test_1App.swift // Test 1 // // Created by Diego on 12/07/23. // import SwiftUI import SwiftData @main struct Test_1App: App { var container = try! ModelContainer( for: Item.self, migrationPlan: ItemsMigrationPlan.self ) var body: some Scene { WindowGroup { ContentView() } .modelContainer(container) } } And then I tried to run the app but it crashed on launch with this error: SwiftData/ModelContext.swift:179: Fatal error: Container does not have any data stores Does anyone have any idea about what I might be missing? The only thing that didn't matched the videos was that when creating a ModelContainer, the code on the video showed var container = ModelContainer... but the compiler showed the error Call can throw, but errors cannot be thrown out of a property initializer, so I added the try!. Also, on the VersionedSchema, I had to add static var versionIdentifier: String?. Other than that I have no idea. Thanks in advance.
Replies
5
Boosts
1
Views
2.2k
Activity
Sep ’23
SwiftData framework crashes application when enumerations are part of arrays
If I use a Codable enum as the type in an array property on a Model, I get a crash. The message is something like: Illegal attempt to use a Optional(Swift.Mirror.DisplayStyle.enum) as a Property Minor change code example from a fresh project using SwiftData: @Model final class Item { var timestamp: Date var choices: [Choice] init(timestamp: Date, choices: [Choice]) { self.timestamp = timestamp self.choices = choices } } enum Choice: String, Codable { case beep, bloop } ContentView (largely unchanged): import SwiftUI import SwiftData struct ContentView: View { @Environment(\.modelContext) private var modelContext @Query private var items: [Item] var body: some View { NavigationView { List { ForEach(items) { item in NavigationLink { VStack { Text("Item at \(item.timestamp, format: Date.FormatStyle(date: .numeric, time: .standard))") Text("\(item.choices.map({"\($0.rawValue)"}).joined())") } } label: { Text(item.timestamp, format: Date.FormatStyle(date: .numeric, time: .standard)) } } .onDelete(perform: deleteItems) } .toolbar { #if os(iOS) ToolbarItem(placement: .navigationBarTrailing) { EditButton() } #endif ToolbarItem { Button(action: addItem) { Label("Add Item", systemImage: "plus") } } } Text("Select an item") } } private func addItem() { withAnimation { let newItem = Item(timestamp: Date(), choices: [.bloop, .beep]) modelContext.insert(newItem) } } private func deleteItems(offsets: IndexSet) { withAnimation { for index in offsets { modelContext.delete(items[index]) } } } } #Preview { ContentView() .modelContainer(for: Item.self, inMemory: true) } This is strange and seems like a bug as if I use Codable structs in this instance it is fine. Yet, if that Codable struct has an enum as a property, it also crashes with a different error. Working: import Foundation import SwiftData @Model final class Item { var timestamp: Date var choices: [Choice] init(timestamp: Date, choices: [Choice]) { self.timestamp = timestamp self.choices = choices } } struct Choice: Codable { let foo: String } Also doesn't work: import Foundation import SwiftData @Model final class Item { var timestamp: Date var choices: [Choice] init(timestamp: Date, choices: [Choice]) { self.timestamp = timestamp self.choices = choices } } struct Choice: Codable { enum SpecificChoice: String, Codable { case beep, bloop } let foo: SpecificChoice } It seems really odd and arbitrary that an enum would cause these issues. There may be other cases involving enums, but to wrap up it seems enums as properties of Codables that live within an array on a SwiftData model, or an array of enums on a SwiftData model cause crashes.
Replies
5
Boosts
3
Views
2.2k
Activity
Sep ’23
Inserting a Model entity with a relationship results in a runtime error.
Hi, when inserting an entity with a relationship I get the following runtime error: Illegal attempt to establish a relationship 'group' between objects in different contexts [...]. The model looks like this: @Model class Person { var name: String @Relationship(.nullify, inverse: \Group.members) var group: Group init(name: String) { self.name = name } } @Model class Group { var name: String @Relationship(.cascade) public var members: [Person] init(name: String) { self.name = name } } It can be reproduced using this (contrived) bit of code: let group = Group(name: "Group A") ctx.insert(group) try! ctx.save() let descriptor = FetchDescriptor<Group>() let groups = try ctx.fetch(descriptor) XCTAssertFalse(groups.isEmpty) XCTAssertEqual(groups.count, 1) XCTAssertTrue(groups.first?.name == "Group A") let person = Person(name: "Willy") person.group = group ctx.insert(person) try ctx.save() (See also full test case below). Anybody experiencing similar issues? Bug or feature? Cheers, Michael Full test case: import SwiftData import SwiftUI import XCTest // MARK: - Person - @Model class Person { var name: String @Relationship(.nullify, inverse: \Group.members) var group: Group init(name: String) { self.name = name } } // MARK: - Group - @Model class Group { var name: String @Relationship(.cascade) public var members: [Person] init(name: String) { self.name = name } } // MARK: - SD_PrototypingTests - final class SD_PrototypingTests: XCTestCase { var container: ModelContainer! var ctx: ModelContext! override func setUpWithError() throws { let fullSchema = Schema([Person.self, Group.self,]) let dbCfg = ModelConfiguration(schema: fullSchema) container = try ModelContainer(for: fullSchema, dbCfg) ctx = ModelContext(container) _ = try ctx.delete(model: Group.self) _ = try ctx.delete(model: Person.self) } override func tearDownWithError() throws { guard let dbURL = container.configurations.first?.url else { XCTFail("Could not find db URL") return } do { try FileManager.default.removeItem(at: dbURL) } catch { XCTFail("Could not delete db: \(error)") } } func testRelAssignemnt_FB12363892() throws { let group = Group(name: "Group A") ctx.insert(group) try! ctx.save() let descriptor = FetchDescriptor<Group>() let groups = try ctx.fetch(descriptor) XCTAssertFalse(groups.isEmpty) XCTAssertEqual(groups.count, 1) XCTAssertTrue(groups.first?.name == "Group A") let person = Person(name: "Willy") person.group = group ctx.insert(person) try ctx.save() } }
Replies
4
Boosts
3
Views
2.7k
Activity
Aug ’23
Does SwiftData support UIImage
Does SwiftData support UIImage as in CoreData specified here: https://www.swiftdevjournal.com/saving-images-in-core-data/ If it does, how to specify that in the @Model schema, especially using external storage to save the image in a separate file. Thanks.
Replies
3
Boosts
1
Views
2.8k
Activity
Aug ’23
How the new relationships in SwiftData work?
There is a new Relationship macro in Xcode Beta 6. The macro includes two new arguments: minimumModelCount and maximumModelCount. I wonder if anyone knows what these values are for and if there is another change under the hood. Relationship( _ options: PropertyOptions..., deleteRule: Schema.Relationship.DeleteRule = .nullify, minimumModelCount: Int? = 0, maximumModelCount: Int? = 0, originalName: String? = nil, inverse: AnyKeyPath? = nil, hashModifier: String? = nil )
Replies
1
Boosts
1
Views
867
Activity
Aug ’23
No "upsert" when working with .unique attributes
Hi, in the session the following is mentioned: If a trip already exists with that name, then the persistent back end will update to the latest values. This is called an upsert. An upsert starts as an insert. If the insert collides with existing data, it becomes an update and updates the properties of the existing data. Nevertheless, if I have a unique constraint on an (String) attribute and try to insert the same again, I end up in the debugger in the generated getter of the attribute: @Attribute(.unique) public var name: String { get { _$observationRegistrar.access(self, keyPath: \.name) return self.getValue(for: \.name) // <- here } EXC_BREAKPOINT (code=1, subcode=0x1a8d6b724) Am I missing something? If this is expected behaviour, how should I prevent this crash (other than checking for uniqueness before every insert)? Thank you! Cheers, Michael
Replies
4
Boosts
5
Views
2.8k
Activity
Aug ’23
SwiftData relationships — are they really areas?
In CoreData, many-relationships are implemented as NSSet objects. In the SwiftData examples, they are shown as arrays. Are they really arrays (i.e., order-preserving and duplicates allowed) or are they arrays made from CoreData NSSets? In my applications, this makes a HUGE difference.
Replies
1
Boosts
3
Views
1.6k
Activity
Jul ’23
SwiftData equivalent of the CoreData lifecycle methods
Anyone know the SwiftData equivalet of the CoreData lifecycle methods. and how do we override them or do something similar? eg: awakeFromInsert() willSave() didSave() willTurnIntoFault() prepareForDeletion()
Replies
0
Boosts
0
Views
914
Activity
Jul ’23
Custom backing store for SwiftData?
I’m working on an app where I have some sensitive data that I want to encrypt when persisting. It looks like SwiftData has a BackingData protocol but it mentions it's for in-memory only. Is there a way to provide a custom storage for SwiftData so that I can encrypt the data before saving?
Replies
0
Boosts
1
Views
918
Activity
Jul ’23