Post

Replies

Boosts

Views

Activity

[SwiftData] Bugs with `autosaveEnabled` and `undoManager` + Observation
Hello, my goal is it to implement an Edit Sheet of a Model with Discard changes in SwiftData. Xcode Version 15.0 beta 6 (15A5219j) Approaches In the following modelContext refers to the context fetched from the environment by the View. Also code for showing the sheet (eg. toggling isPresented) is omitted. // App ContentView() .modelContainer(for: Model.self, isAutosaveEnabled: true, isUndoEnabled: true) // In Content View @Query var models: [Model] //... ForEach(models) { model in ModelView(model: model) //eg. longpresgesture that calls openEditSheet .sheet(..., content: { EditModelView(model: model) } } Disabling autosave + rollback // In ContentView func openEditSheet() { modelContext.autosaveEnabled = false } // In EditModelView func discardEditSheet() { modelContext.rollback() modelContext.autosaveEnabled = true } func saveEditSheet() { try? modelContext.save() // probably not needed if autosave gets enabled anyway modelContext.autosaveEnabled = true } However this approach does not work, as SwiftData continues to save anyway and therefore there is nothing to rollback. modelContext in Memory // In ContentView let context = ModelContext(modelContext.container) context.autosaveEnabled = false context.container.configuration.removeAll() context.container.configuration.insert(ModelConfiguration(..., inMemory: true) //... EditModelView() .modelContext(context) // Also tried: .enviroment(\.modelContext, context) // In EditModelView func discardEditSheet() { modelContext.rollback() // probably not needed as the container is never saved } func saveEditSheet() { try? modelContext.save() // move to persistent storage from memory } The idea was to use something like parent in CoreData. However as this is (currently) not supported. Also tried this by modifying the modelContext directly (instead of creating a new context) and its container directly or before creating the context. The child can not be a ModelContainer as you can not pass a context to it or set its mainContext (get-only). However this approach does not work, as the container does not seem to stay in memory. Maybe related to previous. BackingData EditModelView(model: Model(backingData: model.persistentBackingData) not sure about this one, played around a little with it but not sure what it means / should mean and how I would expect it to work. I have never been great at cooking :). UndoManager (preferred) // In ContentView func openEditSheet() { modelContext.undoManager?.beginUndoGrouping() } // In EditModelView func discardEditSheet() { modelContext.undoManager?.endUndoGrouping() modelContext.undoManager?.undoNestedGroup() // Also tried adding: try? modelContext.save() } func saveEditSheet() { modelContext.undoManager?.endUndoGrouping() } This approach does works kinda weird: The ModelView of the updated model in ContentView's ForEach is not updated regarding the undo action. However when I re-open the EditTaskView of the updated model it has the expected state, even though the model to edit is passed by the ContentView (which does not have the correct state?). After relaunching the app or when modifying the model again (this time not undoing) the previous sate changes are recognised Therefore this looks like it does what I want, with the ContentView having the correct state, but not displaying it. Only reason I can think of is the Model: Observable not getting triggered and therefore no View update. Conclusion After playing around with the above approaches and combining them in every possible way, i think that: disabling autosave at runtime is currently not working and this is a bug (otherwise autosaveEnabled should be get-only). UndoManager does not trigger a View update of a Model/Observable and this is a bug Would be happy to hear other opinions on this. Am I missing something here? Am I ******? Question However as my conclusion does not fix my problem I am wondering: Are my approaches (theoretically) correct? How do I fix / workaround the UndoManager issue? If I identified it correctly, how can I manually notify the view that a Observable model has changed?
3
3
1.7k
Aug ’23