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

SwiftData crashes after updating to MacOS 15.2
After updating to 15.2 I am seeing frequent crashes in my in-development app related to SwiftData. For instance, I have a 100% reproducible crash when I make the app lose and regain focus. There is also a crash that seem to be triggered by a modelContext.save() call in one of my ModelActors. With both of these crashes, the issue seems to be around keeping SwiftData models up to date. The first item in the stacktrace that is not machinecode is always some getter on a SwiftData collection or object. In the console, these crashes are accompanied by output along the lines of: === AttributeGraph: cycle detected through attribute 820680 === precondition failure: setting value during update: 930592 error: the replacement path doesn't exist: "/var/folders/b7/0dw7ztp13fgfxlj19by851tw0000gn/T/swift-generated-sources/@__swiftmacro_10SpaceDebug8TodoListV5todos33_5575DE008494C519BB9FA49C405133E1LL5QueryfMa_.swift" error: the replacement path doesn't exist: "/var/folders/b7/0dw7ztp13fgfxlj19by851tw0000gn/T/swift-generated-sources/@__swiftmacro_10SpaceDebug8TodoListV5todos33_5575DE008494C519BB9FA49C405133E1LL5QueryfMa_.swift" Can't show file for stack frame : <DBGLLDBStackFrame: 0x35a57c4e0> - stackNumber:27 - name:TodoList.todos.getter. The file path does not exist on the file system: /var/folders/b7/0dw7ztp13fgfxlj19by851tw0000gn/T/swift-generated-sources/@__swiftmacro_10SpaceDebug8TodoListV5todos33_5575DE008494C519BB9FA49C405133E1LL5QueryfMa_.swiftCan't show file for stack frame : <DBGLLDBStackFrame: 0x35a57c4e0> - stackNumber:27 - name:TodoList.todos.getter. The file path does not exist on the file system: /var/folders/b7/0dw7ztp13fgfxlj19by851tw0000gn/T/swift-generated-sources/@__swiftmacro_10SpaceDebug8TodoListV5todos33_5575DE008494C519BB9FA49C405133E1LL5QueryfMa_.swiftCan't show file for stack frame : <DBGLLDBStackFrame: 0x35a5a82f0> - stackNumber:62 - name:TodoList.todos.getter. The file path does not exist on the file system: /var/folders/b7/0dw7ztp13fgfxlj19by851tw0000gn/T/swift-generated-sources/@__swiftmacro_10SpaceDebug8TodoListV5todos33_5575DE008494C519BB9FA49C405133E1LL5QueryfMa_.swift Has anyone run into something similar? I'm looking for suggestions on how to debug this. Cheers, Bastiaan
2
0
437
Jan ’25
SwiftData Migration Fail: What kind of backing data is this?
I'm trying to test migration between schemas but I cannot get it to work properly. I've never been able to get a complex migration to work properly unfortunately. I've removed a property and added 2 new ones to one of my data models. This is my current plan. enum MigrationV1toV2: SchemaMigrationPlan { static var schemas: [any VersionedSchema.Type] { [SchemaV1.self, SchemaV2.self] } static let migrateV1toV2 = MigrationStage.custom( fromVersion: SchemaV1.self, toVersion: SchemaV2.self, willMigrate: { context in print("Inside will migrate") // Get old months let oldMonths = try context.fetch(FetchDescriptor<SchemaV1.Month>()) print("Number of old months:\(oldMonths.count)") for oldMonth in oldMonths { // Convert to new month let newMonth = Month(name: oldMonth.name, year: oldMonth.year, limit: oldMonth.limit) print("Number of transactions in oldMonth: \(oldMonth.transactions?.count)") print("Number of transactions in newMonth: \(newMonth.transactions?.count)") // Convert transactions for transaction in oldMonth.transactions ?? [] { // Set action and direction let action = getAction(from: transaction) let direction = getDirection(from: transaction) // Update category if necessary var category: TransactionCategory? = nil if let oldCategory = transaction.category { category = TransactionCategory( name: oldCategory.name, color: SchemaV2.Category.Colors.init(rawValue: oldCategory.color?.rawValue ?? "") ?? .blue, icon: getCategoryIcon(oldIcon: oldCategory.icon) ) // Remove old category context.delete(oldCategory) } // Create new let new = Transaction( date: transaction.date, action: action, direction: direction, amount: transaction.amount, note: transaction.note, category: category, month: newMonth ) // Remove old transaction from month oldMonth.transactions?.removeAll(where: { $0.id == transaction.id }) // Delete transaction from context context.delete(transaction) // Add new transaction to new month newMonth.transactions?.append(new) } // Remove old month context.delete(oldMonth) print("After looping through transactions and deleting old month") print("Number of transactions in oldMonth: \(oldMonth.transactions?.count)") print("Number of transactions in newMonth: \(newMonth.transactions?.count)") // Insert new month context.insert(newMonth) print("Inserted new month into context") } // Save try context.save() }, didMigrate: { context in print("In did migrate") let newMonths = try context.fetch(FetchDescriptor<SchemaV2.Month>()) print("Number of new months after migration: \(newMonths.count)") } ) static var stages: [MigrationStage] { [migrateV1toV2] } } It seems to run fine until it gets the the line: try context.save(). At this point it fails with the following line: SwiftData/PersistentModel.swift:726: Fatal error: What kind of backing data is this? SwiftData._KKMDBackingData<Monthly.SchemaV1.Transaction> Anyone know what I can do about this?
1
0
299
Jan ’25
Triggering a Live Activity from a Widget-Based App Intent
Hello everyone, I have an app leveraging SwiftData, App Intents, Interactive Widgets, and a Control Center Widget. I recently added Live Activity support, and I’m using an App Intent to trigger the activity whenever the model changes. When the App Intent is called from within the app, the Live Activity is created successfully and appears on both the Lock Screen and in the Dynamic Island. However, if the same App Intent is invoked from a widget, the model is updated as expected, but no Live Activity is started. Here’s the relevant code snippet where I call the Live Activity: ` await LiveActivityManager.shared.newSessionActivity(session: session) And here’s how my attribute is defined: struct ContentState: Codable, Hashable { var session: Session } } Is there any known limitation or workaround for triggering a Live Activity when the App Intent is initiated from a widget? Any guidance or best practices would be greatly appreciated. Thank you! David
1
0
419
Jan ’25
SwiftData and async functions
Hello, I recently published an app that uses Swift Data as its primary data storage. The app uses concurrency, background threads, async await, and BLE communication. Sadly, I see my app incurs many fringe crashes, involving EXC_BAD_ACCESS, KERN_INVALID_ADDRESS, EXC_BREAKPOINT, etc. I followed these guidelines: One ModelContainer that is stored as a global variable and used throughout. ModelContexts are created separately for each task, changes are saved manually, and models are not passed around. Threads with different ModelContexts might manipulate and/or read the same data simultaneously. I was under the impression this meets the usage requirements. I suspect perhaps the issue lies in my usage of contexts in a single await function, that might be paused and resumed on a different thread (although same execution path). Is that the case? If so, how should SwiftData be used in async scopes? Is there anything else particularly wrong in my approach?
3
0
826
Jan ’25
Model instance invalidated, backing data could no longer be found in the store
I have been recently getting the following error seemingly randomly, when an event handler of a SwiftUI view accesses a relationship of a SwiftData model the view holds a reference to. I haven't yet found a reliable way of reproducing it: SwiftData/BackingData.swift:866: Fatal error: This model instance was invalidated because its backing data could no longer be found the store. PersistentIdentifier(id: SwiftData.PersistentIdentifier.ID(url: COREDATA_ID_URL), implementation: SwiftData.PersistentIdentifierImplementation) What could cause this error? Could you suggest me a workaround?
2
0
584
Dec ’24
SwiftData Query filter().first crashed in iOS18.2
Hi There, I have a iOS App which has been published and purely managing data by SwiftData. I use following simple codes everywhere in Views: ... @Query var items: [Item] .... if let firstItem = items.first( where: {...}) { ... Then I encountered crash at Query that _items.wrapperdValue has some errors. Then I tried to split first(where...) into ordinary way: let filteredItems = items.filter(...) if let firstItem = filteredItems.first { ... It runs OK. Is it a bug in SwiftData in 18.2 or I missed some steps to facilitate SwiftData macros?
1
0
289
Dec ’24
Filter a list of Item objects on a related Product Object passed as a Bindable
I'm currently developing a SwiftUI application that utilizes SwiftData for data management. I am facing a challenge when trying to filter a query. Specifically, I want to filter a list of Item objects to match a Product instance that is passed to my View. error : Instance member 'product' cannot be used on type 'MainItemListEncap'; did you mean to use a value of this type instead The view // // 311.1.1. MainRefToItem.swift // ComparePrice // // Created by Herman VAN CAUWELAERT on 11/12/2024. // import SwiftUI import SwiftData struct MainItemListEncap: View { @Bindable var product: Product @Query( filter: #Predicate { item in item.productGroup == product }, sort: [SortDescriptor(\Item.name)] ) var items: [Item] @Environment(\.modelContext) var modelContext var body: some View { ForEach(items) { item in VStack(alignment: .leading) { Text(item.name) Text(item.description) } } } } the product class. import SwiftData import Foundation @Model final class Product: CustomStringConvertible, CustomDebugStringConvertible { @Attribute(.unique) var productName: String var productDescription: String var outputCurrency: String // Gebruik een `String` als opslag voor `outputSystem` var outputSystemRawValue: String = MeasurementSystem.metric.rawValue // Computed property om `MeasurementSystem` te gebruiken var outputSystem: MeasurementSystem { get { MeasurementSystem(rawValue: outputSystemRawValue) } set { outputSystemRawValue = newValue.rawValue } } // er zijn verschillend item versies voor een product // als er een hoofdproduct gedelete wordt, dan zullen alle onderliggende items ook gedelete worden @Relationship(deleteRule: .cascade, inverse: \Item.productGroup) var refToItems = [Item]() init(productName: String = "", productDescription: String = "what is this product", outputCurrency: String = "EUR", outputSystem: MeasurementSystem = MeasurementSystem.metric) { self.productName = productName self.productDescription = productDescription self.outputCurrency = outputCurrency self.outputSystem = outputSystem } } the item class import Foundation import SwiftData @Model final class Item: CustomStringConvertible, CustomDebugStringConvertible { var timestamp: Date @Attribute(.unique) var name: String var productGroup: Product? var shop: String //TODO: becomes Shop var price: Double var currency: String var quantity: Double var unit: String //TODO: becomes Unit var isShown : Bool var minimumQuantity: String var rateStr: String { conversionRate != nil ? " (rate: \(ValueFormatter.shared.format(conversionRate!)))" : "" } init(timestamp: Date = Date() , name: String , shop: String = "Colruyt" , price:Double = 1.00 , currency: String = "EUR" , quantity: Double = 1.0 , unit: String = "g" , isShown:Bool = true , conversionRate: Decimal? = 1 , minimumQuantity: String = "1" ) { self.timestamp = timestamp self.name = name self.shop = shop self.price = price self.currency = currency self.quantity = quantity self.unit = unit self.isShown = isShown self.conversionRate = conversionRate self.minimumQuantity = minimumQuantity } }
2
0
280
Dec ’24
Use CoreData alongside SwiftData for the "Sharing" feature in the app.
Hello! 😊 I currently manage an app called MoneyKeeper that uses SwiftData for its data storage framework. Many users have requested a "sharing" feature, but unfortunately, SwiftData does not yet support this functionality, and it’s unclear when it will. 😭 Initially, I considered using CloudKit and CKSyncEngine to implement quick synchronization and sharing. However, due to the complexity of the current data model’s relationships, modeling it with CloudKit’s schema alone seemed overly complicated and likely to introduce bugs. As a result, I’ve decided to implement the sharing feature using CoreData and CloudKit. My plan to avoid conflicts with SwiftData includes: Keeping the local storage locations for SwiftData and CoreData separate. Using entirely separate CloudKit containers for SwiftData and CoreData. I believe these measures will minimize potential issues, but I’m wondering if there’s anything else I should consider. Using both SwiftData (for the personal database) and CoreData (for the shared database) feels like it could lead to significant technical debt in the future, and I anticipate encountering even more challenges during actual implementation. I’d greatly appreciate your valuable insights on this matter. 🙏 The app MoneyKeeper, currently operated using SwiftData. https://apps.apple.com/app/id6514279917
1
0
726
Dec ’24
SwiftData + CKSyncEngine
Hi, I'm building a habit tracking app for iOS and macOS. I want to use up to date technologies, so I'm using SwiftUI and SwiftData. I want to store user data locally on device and also sync data between device and iCloud server so that the user could use the app conveniently on multiple devices (iPhone, iPad, Mac). I already tried SwiftData + NSPersistentCloudKitContainer, but I need to control when to sync data, which I can't control with NSPersistentCloudKitContainer. For example, I want to upload data to server right after data is saved locally and download data from server on every app open, on pull-to-refresh etc. I also need to monitor sync progress, so I can update the UI and run code based on the progress. For example, when downloading data from server to device is in progress, show "Loading..." UI, and when downloading finishes, I want to run some app business logic code and update UI. So I'm considering switching from NSPersistentCloudKitContainer to CKSyncEngine, because it seems that with CKSyncEngine I can control when to upload and download data and also monitor the progress. My database schema (image below) has relationships - "1 to many" and "many to many" - so it's convenient to use SwiftData (and underlying CoreData). Development environment: Xcode 16.1, macOS 15.1.1 Run-time configuration: iOS 18.1.1, macOS 15.1.1 My questions: 1-Is it possible to use SwiftData for local data storage and CKSyncEngine to sync this local data storage with iCloud? 2-If yes, is there any example code to implement this? I've been studying the "CloudKit Samples: CKSyncEngine" demo app (https://github.com/apple/sample-cloudkit-sync-engine), but it uses a very primitive approach to local data storage by saving data to a JSON file on disk. It would be very helpful to have the same demo app with SwiftData implementation! 3-Also, to make sure I don't run into problems later - is it okay to fire data upload (sendChanges) and download (fetchChanges) manually with CKSyncEngine and do it often? Are there any limits how often these functions can be called to not get "blocked" by the server? 4-If it's not possible to use SwiftData for local data storage and CKSyncEngine to sync this local data storage with iCloud, then what to use for local storage instead of SwiftData to sync it with iCloud using CKSyncEngine? Maybe use SwiftData with the new DataStore protocol instead of the underlying CoreData? All information highly appreciated! Thanks, Martin
3
0
1k
Dec ’24
Ongoing Issues with ModelActor in SwiftData?
After the problems with the ModelActor in iOS 18, it seemed like the ModelActor became more stable with iOS 18.1 and macOS 15.1. However, I’m still encountering many problems and crashes. I wanted to ask if these issues are related to my persistence layer architecture or if they’re still inherent to the ModelActor itself. I’ve generally followed the blog posts: https://fatbobman.com/en/posts/practical-swiftdata-building-swiftui-applications-with-modern-approaches/ and https://brightdigit.com/tutorials/swiftdata-modelactor/ and aim to achieve the following: I have a single DataProvider that holds the ModelContainer and uses it to configure and initialize a single DataHandler. These are created once at app launch and injected into the SwiftUI view hierarchy as EnvironmentObjects. Since I need to access the SwiftData models not only in SwiftUI but also indirectly in ViewModels or UIKit views, all read operations on the models should go through the DataProvider ( ModelContainrs MainContext), while all other CRUD operations are handled centrally via the single DataHandler (executed within a single ModelActor). Additionally, I want to monitor the entire container using another ModelActor, initialized in the DataProvider, which tracks changes to objects using TransactionHistory. I’ve managed to implement this to some extent, but I’m facing two main issues: ModelActor and Main Actor Requirement The ModelActor only updates SwiftUI views when initialized via the main context of the ModelContainer and therefore runs on the Main Actor. It would be ideal for this to work in the background, but the issue with the ModelActor that existed previously doesn’t seem to have been resolved in iOS 18.1/macOS 15.1—am I wrong about this? Frequent Crashes (more severe) Crashes occur, especially when multiple windows on macOS or iPadOS access the same DataHandler to update models. This often leads to crashes during read operations on models by a SwiftUI view, with logs like: Object 0x111f15480 of class _ContiguousArrayStorage deallocated with non-zero retain count 3. This object's deinit, or something called from it, may have created a strong reference to self which outlived deinit, resulting in a dangling reference. error: the replacement path doesn't exist: "/var/folders/gs/8rwdjczj225d1pj046w3d97c0000gn/T/swift-generated-sources/@__swiftmacro_12SwiftDataTSI3TagC4uuID18_PersistedPropertyfMa_.swift" Can't show file for stack frame : <DBGLLDBStackFrame: 0x34d28e170> - stackNumber:1 - name:Tag.uuID.getter. The file path does not exist on the file system: /var/folders/gs/8rwdjczj225d1pj046w3d97c0000gn/T/swift-generated-sources/@__swiftmacro_12SwiftDataTSI3TagC4uuID18_PersistedPropertyfMa_.swift This error usually happens when there are multiple concurrent accesses to the DataHandler/ModelActor. However, crashes also occur sporadically during frequent accesses from a single view with an error like "the replacement path doesn't exist." It also seems like having multiple ModelActors, as in this case (one for observation and one for data changes), causes interference and instability. The app appears to crash less frequently when the observer is not initialized, but I can’t verify this—it might just be a coincidence. My Question: Am I fundamentally doing something wrong with the ModelActors or the architecture of my persistence layer?
3
0
1k
Dec ’24
Ongoing Issues with ModelActor in SwiftData?
After the significant issues with the ModelActor in iOS 18, it seemed like the ModelActor became more stable with iOS 18.1 and macOS 15.1. However, I’m still encountering problems and crashes. I wanted to ask if these issues are related to my persistence layer architecture or if they’re still inherent to the ModelActor itself. I’ve generally followed the blog posts: https://fatbobman.com/en/posts/practical-swiftdata-building-swiftui-applications-with-modern-approaches/ and https://brightdigit.com/tutorials/swiftdata-modelactor/ and aim to achieve the following: I have a single DataProvider that holds the ModelContainer and uses it to configure and initialize a single DataHandler. These are created once at app launch and injected into the SwiftUI view hierarchy as EnvironmentObjects. Since I need to access the SwiftData models not only in SwiftUI but also indirectly in ViewModels or UIKit views, all read operations on the models should go through the DataProvider (and its MainContext), while all other CRUD operations are handled centrally via the single DataHandler (executed within a single ModelActor). Additionally, I want to monitor the entire container using another ModelActor, initialized in the DataProvider, which tracks changes to objects using TransactionHistory. I’ve managed to implement this to some extent, but I’m facing two main issues: 1. ModelActor and Main Actor Requirement The ModelActor only updates SwiftUI views when initialized via the maincontext of the ModelContainer and therefore runs on the Main Actor. It would be ideal for this to work in the background, but the issue with the ModelActor that existed previously doesn’t seem to have been resolved in iOS 18.1/macOS 15.1—am I wrong about this? 2. Frequent Crashes (more severe) Crashes occur, especially when multiple windows on macOS or on iPad access the same DataHandler to update models. This often leads to crashes during read operations on models by a SwiftUI view (but not only), with logs like: error: the replacement path doesn't exist: "/var/folders/gs/8rwdjczj225d1pj046w3d97c0000gn/T/swift-generated-sources/@__swiftmacro_12SwiftDataTSI3TagC4uuID18_PersistedPropertyfMa_.swift" Can't show file for stack frame : <DBGLLDBStackFrame: 0x34d28e170> - stackNumber:1 - name:Tag.uuID.getter. The file path does not exist on the file system: /var/folders/gs/8rwdjczj225d1pj046w3d97c0000gn/T/swift-generated-sources/@__swiftmacro_12SwiftDataTSI3TagC4uuID18_PersistedPropertyfMa_.swift This error usually happens when there are multiple concurrent accesses to the DataHandler/ModelActor. However, crashes also occur sporadically during frequent accesses from a single view with an error like "the replacement path doesn't exist." It also seems like having multiple ModelActors, as in this case (one for observation and one for data changes), causes interference and instability. The app appears to crash less frequently when the observer is not initialized, but I can’t verify this—it might just be a coincidence. My Question: Am I fundamentally doing something wrong with the ModelActors or the architecture of my persistence layer?
1
0
438
Dec ’24
Correct way to remove arrays containing model objects in SwiftData
Are there any differences (either performance or memory considerations) between removing an array of model objects directly using .removeAll() vs using modelContext? Or, are they identical? Attached below is an example to better illustrate the question (i.e., First Way vs Second Way) // Model Definition @Model class GroupOfPeople { let groupName: String @Relationship(deleteRule: .cascade, inverse: \Person.group) var people: [Person] = [] init() { ... } } @Model class Person { let name: String var group: GroupOfPeople? init() { ... } } // First way struct DemoView: View { @Query private groups: [GroupOfPeople] var body: some View { List(groups) { group in DetailView(group: group) } } } struct DetailView: View { let group: GroupOfPeople var body: some View { Button("Delete All Participants") { group.people.removeAll() } } // Second way struct DemoView: View { @Query private groups: [GroupOfPeople] var body: some View { List(groups) { group in DetailView(group: group) } } } struct DetailView: View { @Environment(\.modelContext) private var context let group: GroupOfPeople var body: some View { Button("Delete All Participants") { context.delete(model: Person.self, where: #Predicate { $0.group.name == group.name }) } // assuming group names are unique. more of making a point using modelContext instead }
0
0
267
Dec ’24
SwiftData Quakes Sample app decoding errors from null magnitudes
Hi! I believe there might be a small bug in the SwiftData Quakes Sample App.^1 The Quakes app requests a JSON feed from USGS.^2 What seems to be breaking is that apparently earthquake entities from USGS can return with null magnitudes. That is throwing errors from the decoder: struct GeoFeatureCollection: Decodable { let features: [Feature] struct Feature: Decodable { let properties: Properties let geometry: Geometry struct Properties: Decodable { let mag: Double let place: String let time: Date let code: String } struct Geometry: Decodable { let coordinates: [Double] } } } which is expecting mag to not be nil. Here is my workaround: struct GeoFeatureCollection: Decodable { let features: [Feature] struct Feature: Decodable { let properties: Properties let geometry: Geometry struct Properties: Decodable { let mag: Double? let place: String let time: Date let code: String } struct Geometry: Decodable { let coordinates: [Double] } } } And then: extension Quake { /// Creates a new quake instance from a decoded feature. convenience init(from feature: GeoFeatureCollection.Feature) { self.init( code: feature.properties.code, magnitude: feature.properties.mag ?? 0.0, time: feature.properties.time, name: feature.properties.place, longitude: feature.geometry.coordinates[0], latitude: feature.geometry.coordinates[1] ) } }
1
0
589
Dec ’24
SwiftData Crash: Incorrect actor executor assumption.
I have this actor actor ConcurrentDatabase: ModelActor { nonisolated let modelExecutor: any ModelExecutor nonisolated let modelContainer: ModelContainer init(modelContainer: ModelContainer) { self.modelExecutor = DefaultSerialModelExecutor(modelContext: ModelContext(modelContainer)) self.modelContainer = modelContainer } /// Save pending changes in the model context. private func save() { if self.modelContext.hasChanges { do { try self.modelContext.save() } catch { ... } } } } I am getting a runtime crash on: try self.modelContext.save() when trying to insert something into the database and save Thread 1: Fatal error: Incorrect actor executor assumption; Expected same executor as MainActor. Can anyone explain why this is happening?
2
0
417
Dec ’24
Undo in SwiftData deletes all data at once.
When the following models in SwiftData, @Model final class UndoRedoData { var id: [Int] init(id: [Int]) { self.id = id } } I created the following code. struct ContentView: View { @ObservedObject var swiftDataViewModel = SwiftDataArrayViewModel.shared @State private var idArray: [Int] = [1,2,3,4] @State private var firstviewSwich: Bool = true @State private var twoviewSwich: Bool = false @State private var threeviewSwich: Bool = false var body: some View { VStack { if firstviewSwich == true { Button(action: addItem) { Text("1.New Item") } } if twoviewSwich == true { Button { forArrayData() } label: { Text("2.Data Road") } } if threeviewSwich == true { Button(action: undoItem) { Text("3.Undo") } } } } private func addItem() { withAnimation { let newItem = UndoRedoData(id: [1,2,3,4]) swiftDataViewModel.taskContext.insert(newItem) do { try swiftDataViewModel.taskContext.save() } catch { print(error) } swiftDataViewModel.fetchItems() firstviewSwich.toggle() twoviewSwich.toggle() } } private func forArrayData() { twoviewSwich.toggle() for data in idArray { swiftDataViewModel.idUndoCreate(id: data, undoManager: swiftDataViewModel.arrayItemUndoManager) } threeviewSwich.toggle() } private func undoItem() { swiftDataViewModel.arrayItemUndoManager.undo() threeviewSwich.toggle() firstviewSwich.toggle() } } class SwiftDataArrayViewModel: ObservableObject { static let shared = SwiftDataArrayViewModel() let modelContainer: ModelContainer @ObservationIgnored lazy var taskContext: ModelContext = { return ModelContext(modelContainer) }() @Published var arrayItems = [UndoRedoData]() @Published var arrayItemUndoManager = UndoManager() init() { let schema = Schema([UndoRedoData.self]) let modelConfiguration = ModelConfiguration(schema: schema, isStoredInMemoryOnly: false) do { modelContainer = try ModelContainer(for: schema, configurations: [modelConfiguration]) } catch { fatalError(error) } fetchItems() } func fetchItems() { let fetchDescriptor = FetchDescriptor<UndoRedoData>() do { arrayItems = try taskContext.fetch(fetchDescriptor) } catch { fatalError(error) } } func idUndoCreate(id: Int, undoManager: UndoManager?) { undoManager?.registerUndo(withTarget: self) { target in target.removeID() } } func removeID() { if let firstUndoRedoData = arrayItems.first { print("Before Delete:\(firstUndoRedoData.id)") if !firstUndoRedoData.id.isEmpty { firstUndoRedoData.id.removeLast() } print("After Delete:\(firstUndoRedoData.id)") } do { try taskContext.save() } catch { print(error) } fetchItems() } } In this code, 1. Create an Item in New Item, 2. Execute Data Road and register the data in the array that is the same value as the data created in New Item in SwiftData one by one in UndoManager by for data in idArray. This is done because the data in the array and the data created by New Item in SwiftData can be known in advance. private func forArrayData() { twoviewSwich.toggle() for data in idArray { swiftDataViewModel.idUndoCreate(id: data, undoManager: swiftDataViewModel.arrayItemUndoManager) } // class SwiftDataArrayViewModel: ObservableObject func idUndoCreate(id: Int, undoManager: UndoManager?) { undoManager?.registerUndo(withTarget: self) { target in target.removeID() } } After registering in UndoManager, when Undo is executed with 3. Undo, instead of being able to perform Undo where one id is deleted each time, all the data of the id in SwiftData is deleted in a one-time Undo. I would like to be able to delete one id each time Undo is performed and restore them in sequence, but I can only delete them all once. Does this mean that such registration to UndoManager should not be done with for statements, etc.? Or is there another problem in the code? I want to make sure that one id is deleted for each Undo executed.
1
0
637
Dec ’24
SwiftData Predicates crashes when using Generic
I have been dealing with an error for almost 2 days now that caused my programme to crash on runtime with Thread 10: EXC_BAD_ACCESS (code=1, address=0x0) error , only when using Release mode. After many trial and errors and narrowing down the root problem I became suspicious to #Predicate and Generics being the root cause of the problem so I made these views to test it out. import SwiftUI import SwiftData struct DataBaseTestGeneric<Model : PersistentModel>: View { @State private var models: [Model] = [] var body: some View { viewLoader{ let reporter = Reporter() let pred = #Predicate<Model>{ model in return true } models = await reporter.fetch(pred) }content: { List{ ForEach(models){ model in Text("\(model.id)") } } } } } and a non-Generic version : import SwiftData struct DatabaseTest: View { @State private var transactions: [Transaction] = [] var body: some View { viewLoader { let reporter = Reporter() let pred = #Predicate<Transaction>{ dec in return true } let decs = await reporter.fetch(pred) transactions = decs }content:{ List{ ForEach(transactions){transaction in Text("\(transaction.id)") } } } } } to give you an insight viewLoader implentations is : struct viewLoader<Content : View>: View { var state : LoadingView.States = .loading let loadingTask : () async -> Void @State private var isLoading = true @ViewBuilder var content : Content var body: some View { if isLoading{ LoadingView(state) .task { await Task.detached(priority:.high){ await loadingTask() }.value isLoading = false } }else{ content } } } and I am accessing SwiftData using a background thread ( by implementing @ModelActor . the problem is that the code always crash on runtime when I am trying to fetch the data using reporter.fetch function. To make things even more weird I have to add the fact that Reporter class also have another function called fetchAll as follow : func fetchAll<T>(_ model : T.Type) async -> [T] where T : PersistentModel { let desc = FetchDescriptor<T>() let result = try? await context.fetch(desc) guard let result else { assertionFailure("Error fetching \(model) from context") return [] } return result } if i replace this function with reporter.fetch (which takes a predicate) the code will not crash either with or without using Generics , which brings me to the point that #Predicate is causing mayhem somehow ! PS: I am using Xcode Version 16.1 (16B40) , on macOs Sequoia Version 15.2 Beta (24C5089c)
2
0
443
Dec ’24
Automatic lightweight migrations in SwiftData VersionedSchema?
Currently, I have an Unversioned Schema, and lightweight changes are automatically applied to the Models. However, I'm planning to transition to a VersionedSchema, and I have a few questions: Do I need to write all lightweight migrations in the Migration Plan? or is it automatically applied like the Unversioned Schema? What happens if I do not create a lightweight migration? and just directly do lightweight changes to the latest VersionedSchema (example: Add a new property)?
2
1
417
Dec ’24
Infinite view loop
If I add items at the root level, this code works, but if I attempt to add a child to any item, it runs an infinite loop where it goes from the AddItemView back to the SubItemView and starts all over. I suspect it has something to do with the Predicate in SubItemView, but the debugger is crap, so I'm just guessing. Minimal code: @Model final class Item { var id: String var name: String var children: [Item] = [] @Relationship(deleteRule: .cascade) var parent: Item? init(name: String, parent: Item?, id: String = UUID().uuidString) { self.name = name self.parent = parent self.id = id } } struct ContentView: View { @Environment(\.modelContext) private var modelContext @Query( filter: #Predicate<Item> { item in item.parent == nil }, sort: \Item.name ) public var rootItems: [Item] var body: some View { NavigationStack { List { ForEach(rootItems) { item in HStack { NavigationLink ( destination: SubItemView(parent: item)) { Text(item.name) } } } } .toolbar { ToolbarItem(placement: .navigationBarTrailing) { EditButton() } ToolbarItem { NavigationLink(destination: AddItemView(itemParent: nil)) { Text("Add To Do") } } } } } } struct SubItemView: View { @Environment(\.dismiss) private var dismiss @Environment(\.modelContext) private var modelContext @State var parent: Item @State private var todo: String = "" @State var selectedDate = Date() @State var showPicker: Bool = false @Query var children: [Item] init(parent: Item) { self.parent = parent let parentID = parent.id _children = Query(filter: #Predicate<Item> { $0.parent.flatMap(\.id) == parentID && $0.parent.flatMap(\.id) != nil }, sort: \Item.name ) } var body: some View { Form { LabeledContent { TextField("Name", text: $parent.name) } label: { Text("Name:") } } Text("Parent: \(parent.name)\n") NavigationStack { Text("Child count: \(children.count)") List(children) { child in HStack { if(child.children.isEmpty) { Text(child.name) NavigationLink ( destination: SubItemView(parent: child)) { Text("").foregroundColor(.white).background(Color.blue) } .opacity(0) .background( Text("") ) } else { Text(child.name) NavigationLink(destination: SubItemView(parent: child)) { Text("") } } } } } .navigationTitle("Sub Items") .toolbar { ToolbarItem(placement: .navigationBarTrailing) { EditButton() } ToolbarItem { NavigationLink(destination: AddItemView(itemParent: parent)) { Text("Add To Do") } } ToolbarItem { Button("Save") { try? modelContext.save() dismiss() } } } } } struct AddItemView: View { @Environment(\.dismiss) private var dismiss @Environment(\.modelContext) private var context @State var itemParent: Item? @State private var name = "" @State private var showWarning: Bool = false @State var child = Item(name: "", parent: nil) var body: some View { NavigationStack { Form { LabeledContent { TextField("Item", text: $name) } label: { Text("Todo:") } } .navigationTitle("Add New Item") .toolbar { Button("Save") { let tmp = Item(name: name, parent: itemParent) if(itemParent != nil) { itemParent!.children.append(tmp) } context.insert(tmp) try? context.save() dismiss() } } } } }
4
0
389
Nov ’24