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

Is there anything wrong in operating with ModelContainer.mainContext?
Consider this code @UIApplicationDelegateAdaptor(AppDelegate.self) var appDelegate init() { let schema = Schema([ ... ]) let modelConfiguration = ModelConfiguration(schema: schema, isStoredInMemoryOnly: false) do { sharedModelContainer = try ModelContainer(for: schema, configurations: [modelConfiguration]) } catch { fatalError("Could not create ModelContainer: \(error)") } SettingsViewModel.shared = SettingsViewModel(modelContext: sharedModelContainer.mainContext) } I'm basically saving a copy of mainContext in a viewModel. And then later on uses that viewModel to operate on the models while using the mainActor. Is this ok? That same container is also pass into the view using .modelContainer(sharedModelContainer) Can it be used in both ways like that?
3
0
366
Sep ’24
Sign in With Apple email, firstName, lastName returning nil
I have implemented basic Sign in with Apple functionality to my app. When the app is launched, the user will be presented with a Continue with Apple view, where they can authenticate. Once signed in, they will return to a view that reads "Hello, [User's Name]". However, I cannot seem to figure out why the email and name return nil after authentication. For example, when authentication is successful, the HelloView simply reads "Hello, " with no name. I have it setup so that the users name and email are saved to AppStorage variables, and then inserted into a Profile class with SwiftData. import AuthenticationServices import SwiftData import SwiftUI struct ContentView: View { @Environment(\.colorScheme) var colorScheme @Environment(\.modelContext) var modelContext @AppStorage("email") var email: String = "" @AppStorage("firstName") var firstName: String = "" @AppStorage("lastName") var lastName: String = "" @AppStorage("userID") var userID: String = "" @Query var userProfile: [Profile] private var isSignedIn: Bool { !userID.isEmpty } var body: some View { VStack { if !isSignedIn { SignInView() } else { HomeView() } } } } struct SignInView: View { @Environment(\.colorScheme) var colorScheme @Environment(\.modelContext) var modelContext @AppStorage("email") var email: String = "" @AppStorage("firstName") var firstName: String = "" @AppStorage("lastName") var lastName: String = "" @AppStorage("userID") var userID: String = "" @Query var userProfile: [Profile] var body: some View { NavigationStack { Spacer() SignInWithAppleButton(.continue) { request in request.requestedScopes = [.email, .fullName] } onCompletion: { result in switch result { case .success(let auth): switch auth.credential { case let credential as ASAuthorizationAppleIDCredential: // User ID let userID = credential.user // User Info let email = credential.email print(email!) let firstName = credential.fullName?.givenName print(firstName!) let lastName = credential.fullName?.familyName print(lastName!) self.email = email ?? "" self.userID = userID self.firstName = firstName ?? "" self.lastName = lastName ?? "" createProfile() default: break } case .failure(let error): print("Error signing in with Apple: \(error.localizedDescription)") } } .signInWithAppleButtonStyle(colorScheme == .dark ? .white : .black) .frame(height: 50) .padding() .cornerRadius(12) .navigationTitle("[App Name]") } } func createProfile() { let newProfile = Profile(firstName: firstName, lastName: lastName, email: email, userID: userID) modelContext.insert(newProfile) } } This is how I have my HomeView setup: import SwiftData import SwiftUI struct HomeView: View { @Environment(\.modelContext) var modelContext @Query var user: [Profile] var body: some View { ForEach(user) { user in Text("Hello, \(user.firstName)") } } } #Preview { HomeView() } And here's the Profile class: import Foundation import SwiftData @Model class Profile { var firstName: String var lastName: String var email: String var userID: String init(firstName: String, lastName: String, email: String, userID: String) { self.firstName = firstName self.lastName = lastName self.email = email self.userID = userID } }
1
0
252
Sep ’24
Change in iOS 18 prevents SwiftData synchronization between main app and extension
I have a Live Activity with a button that updates a SwiftData model. This used to work in iOS 17, but not on iOS 18. The reason is that in iOS 17, when you run an AppIntent from a Live Activity, the perform() method would run in the main app's process, meaning it had access to the app's ModelContainer/ModelContext. However, in iOS 18, this is no longer the case, and the perform() method of an AppIntent now runs in the extension's process. While I can still construct a new ModelContainer & ModelContext in the AppIntent's perform() method, the main app's container and context will not see these changes until the app is relaunched. How can I make this work in iOS 18 now that an AppIntent executed from an extension runs in a different process from the main app?
1
0
411
Sep ’24
Syncing changes between main app and extension
I have an app that starts a Live Activity on a certain user action. This Live Activity contains a button that the user can tap, which updates a SwiftData model instance. However, when you return to the main app after tapping the button on the Live Activity, the views do not update to reflect the changes, even though the changes were written to the database. The underlying issue here is that the ModelContainer/ModelContext used by the AppIntent (performed from the LiveActivity when the button is tapped), are different from the instances in the main app. Meaning that while the changes are written to the underlying storage, the in-memory instances of ModelContext/ModelContainer in the main app don't get the changes from the extension, so SwiftUI doesn't update either. What is the recommended way to handle this scenario? Or is there one? :) Shared access to a SwiftData container is clearly supported through App Groups, so is there not a mechanism to ensure changes made by an extension are updated in real-time for the main app? Otherwise, it seems I would have to go through and manually rerun queries that views depend on to make sure they are showing the most recent data. This is cumbersome and error-prone. Perhaps I'm missing something? Any suggestions would be greatly appreciated.
3
0
455
Sep ’24
SectionedFetchRequest in SwiftData
With Core Data and SwiftUI we can use @SectionedFetchRequest. Does SwiftData support something similar to @SectionedFetchRequest? For example, I want to create a lazy-loaded list that groups posts by their date. @Model Post { let title: String let dateString: String // YYYY-MM-DD let createdAt: Date } @SectionedFetchRequest( entity: \Post.self, sectionIdentifier: \Post.dateString, sortDescriptors: [\Post.createdAt] ) var postsByDate: SectionedFetchResults ForEach(postsByDate) { section in Section(header: Text(section.id)) { ForEach(section) { post in PostView(post) } } }
1
0
212
Sep ’24
iOS 18 strange SwiftData fatal error
Hey developers! I updated to Xcode 16 and iOS 18. I wanted to publish my first iOS 18 update but I keep getting a very strange error after building and launching the app: "Fatal error: This model instance was destroyed by calling ModelContext.reset and is no longer usable." (I haven't changed anything regarding swift data and I never call ModelContext.reset) This error happens only after building. When I close the app and open it again (without running it through Xcode) the app never crashes and all the data is still there. I couldn't find much bout this error online. Is anyone experiencing the same? I wonder if this is a bug in Xcode 16 or there is something wrong with my code. I also wonder if I can safely publish my update to App Store, since the error only happens after building. Thank you!
3
2
722
Sep ’24
Is it possible to track history using HistoryDescriptor in SwiftData?
Is it possible to track history using the new HistoryDescriptor feature in SwiftData? Or can I only get the current most recent data? Or is it possible to output the changed data itself, along with timestamps? I am hoping that it is possible to track by a standard feature like NSPersistentHistoryTransaction in CoreData. Do we still have to use a method in SwiftData that creates more tracking data itself?
3
0
351
Sep ’24
SwiftData: SwiftData.PersistentIdentifierImplementation) was remapped to a temporary identifier during save
I'm seeing a lot of these in my logs: PersistentIdentifier PersistentIdentifier(id: SwiftData.PersistentIdentifier.ID(url: x-swiftdata://Course/BC9CF99A-DE6A-46F1-A18D-8034255A56D8), implementation: SwiftData.PersistentIdentifierImplementation) was remapped to a temporary identifier during save: PersistentIdentifier(id: SwiftData.PersistentIdentifier.ID(url: x-coredata:///Course/t58C849CD-D895-4773-BF53-3F63CF48935B210), implementation: SwiftData.PersistentIdentifierImplementation). This is a fatal logic error in DefaultStore ... though everything seems to work. Does anyone know what this means in this context? Anything I can do to not have this appear?
4
4
509
Sep ’24
iOS 18 SwiftData Bug: Codable Models Cause Relationship Mapping Error
Here we have yet another bug, I suppose, in SwiftData that happens on iOS18 but it is not an issue on iOS17. There are 2 models defined as follows @Model final public class Note: Identifiable, Codable, Hashable { public private(set) var uuid = UUID().uuidString var heading: String = "" var tags: [Tag]? init(heading: String = "") { self.heading = heading } required public init(from decoder: Decoder) throws { ... } public func encode(to encoder: Encoder) throws { ... } } @Model final public class Tag: Identifiable, Codable { var name: String = "" @Relationship(deleteRule: .nullify, inverse: \Note.tags) var notes: [Note]? init(_ name: String) { self.name = name } required public init(from decoder: Decoder) throws { … } public func encode(to encoder: Encoder) throws { ... } } and a function o add new tags as follows private func addTags(note: Note, tagNames: [String]) { if note.tags == nil { note.tags = [] } for tagName in tagNames { if let tag = fetchTag(tagName) { if !note.tags!.contains(where: {$0.name == tagName}) { note.tags!.append(tag) } } else { // The following line throws the exception on iOS18 when Tag conforms to Codable: // Illegal attempt to map a relationship containing temporary objects to its identifiers. note.tags!.append(Tag(tagName)) } } } This code works perfectly well on iOS17 but on iOS18 I get the exception “Illegal attempt to map a relationship containing temporary objects to its identifiers.” What I noticed that this happens only when Tag model conforms to Codable protocol. Is it a bug? It looks like, otherwise we've got some undocumented changes have been made. In my previous post I mentioned about the other issue about ModelContext that is broken too on iOS18 - I mean it works perfectly well on iOS17. Demo app with an example how to workaround this problem is available here on GitHub. Repro steps: Add a note with some tags (separated by space) Edit this note and add a new tag (tag that does not exists in database) and tap Save. You should noticed that the tag hasn't been added. It works occasionally but hardly to be seen.
3
5
503
Oct ’24
Migrating schemas in SwiftData + CloudKit
Hello, I’m struggling to go from unversioned data model in SwiftData, to starting to version it. Some FYI: I’m using CloudKit I’m using a widget, where I also pass in my data model and setup my container, this is shared over a group container/app group. My migration is very simple, I’m adding a property which is not optional ( has default value set, and a default value in initialiser ). Model: @Model class NicotineModel { var nicotineType: NicotineType = NicotineType.snus var startDate: Date = Date() + 30 var spendingAmount: Int = 0 var nicotinePerDay: Int = 0 var quittingMethod: QuittingMethod = QuittingMethod.coldTurkey // this is the change in the model, V1 doesn't have the quittingMethod property var setupComplete: Bool = false I’ve tried with: static let migrateV1toV2 = MigrationStage.lightweight( fromVersion: SchemaV1.self, toVersion: SchemaV2.self ) But also static let migrateV1toV2 = MigrationStage.custom( fromVersion: SchemaV1.self, toVersion: SchemaV2.self, willMigrate: nil, didMigrate: { context in let nicotineModels2 = try context.fetch(FetchDescriptor<SchemaV2.NicotineModel>()) let nicotineModels = try context.fetch(FetchDescriptor<SchemaV1.NicotineModel>()) for model in nicotineModels { let newModel = SchemaV2.NicotineModel( nicotineType: model.nicotineType, startDate: model.startDate, spendingAmount: model.spendingAmount, nicotinePerDay: model.nicotinePerDay, setupComplete: model.setupComplete, quittingMethod: .coldTurkey ) context.insert(newModel) context.delete(model) } try context.save() } ) and simply static let migrateV1toV2 = MigrationStage.custom( fromVersion: SchemaV1.self, toVersion: SchemaV2.self, willMigrate: nil, didMigrate: { context in let nicotineModels = try context.fetch(FetchDescriptor<SchemaV2.NicotineModel>()) for model in nicotineModels { model.quittingMethod = .coldTurkey } try context.save() } ) This gives me the error on startup SwiftData/ModelCoders.swift:1762: Fatal error: Passed nil for a non-optional keypath \NicotineModel.quittingMethod On https://icloud.developer.apple.com I can see that the record doesn't include my quittingMethod. I'm loosing my mind, what am I doing wrong?
1
2
329
Sep ’24
Xcode 16 MacOS Sequoia SwiftData not saving data after running application
After upgrading to MacOS Sequoia and Xcode 16 my app was behaving oddly. While running the application from Xcode, the app appears to save data, but running the app again from Xcode, the data is not there. If I open the app on the iPhone, run it and create data, the data persists even after I kill the app from the app switcher. I went many rounds through my code and found nothing that helped. I finally ran the sample app that appears when you begin a new project and that is behaving the same way. There are errors thrown. error: the replacement path doesn't exist: "/var/folders/qv/m7sk8kcd3713j3l4bg8wt1lw0000gn/T/swift-generated-sources/@_swiftmacro_13SwiftDataTest11ContentViewV5items33_BCE1062261466603F5F86A688789BF68LL5QueryfMa.swift" error: the replacement path doesn't exist: "/var/folders/qv/m7sk8kcd3713j3l4bg8wt1lw0000gn/T/swift-generated-sources/@_swiftmacro_13SwiftDataTest11ContentViewV5items33_BCE1062261466603F5F86A688789BF68LL5QueryfMa.swift" error: the replacement path doesn't exist: "/var/folders/qv/m7sk8kcd3713j3l4bg8wt1lw0000gn/T/swift-generated-sources/@_swiftmacro_13SwiftDataTest11ContentViewV5items33_BCE1062261466603F5F86A688789BF68LL5QueryfMa.swift"
7
4
1.2k
Oct ’24
SwiftData Multiple ModelConfigurations
A ModelContainer with two configurations for two seperate models with one set to isStoredInMemoryOnly:false and the other one isStoredInMemoryOnly:true crashes after insertion. Anyone git this to work? import SwiftData @main struct RecipeBookApp: App { var container: ModelContainer init() { do { let config1 = ModelConfiguration(for: Recipe.self) let config2 = ModelConfiguration(for: Comment.self, isStoredInMemoryOnly: true) container = try ModelContainer(for: Recipe.self, Comment.self, configurations: config1, config2) } catch { fatalError("Failed to configure SwiftData container.") } } var body: some Scene { WindowGroup { ContentView() .modelContainer(container) } } } struct ContentView: View { @Environment(\.modelContext) private var context @Query private var recipes: [Recipe] @Query private var comments: [Comment] var body: some View { VStack { Button("Insert") { context.insert(Recipe(name:"Test")) context.insert(Comment(name:"Test")) } List(recipes){ recipe in Text(recipe.name) } List(comments){ comment in Text(comment.name) } } .padding() .onAppear(){ context.insert(Recipe(name:"Test")) context.insert(Comment(name:"Test")) } } } @Model class Recipe { internal init(name:String ) { self.name = name } var id: UUID = UUID() var name: String = "" } @Model class Comment { internal init(name:String ) { self.name = name } var id: UUID = UUID() var name: String = "" }
2
0
454
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
606
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
365
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
404
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
431
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
269
Sep ’24