Search code examples
swiftuicore-data

"'StateObject' requires that ' conform to 'ObservableObject'" when creating a dual entry (add/edit) form for Core Data without needing a ViewModel


How do I create a dual entry (add/edit) form for Core Data without needing a ViewModel? If I have a Core Data entity named Scenario and am building a detail view to add or edit where I have a:

@StateObject var scenario: Scenario

but want it to support a nil value, so I can use it for an add as well as an update, but when I change it to:

@StateObject var scenario: Scenario?

I get this error:

Generic struct 'StateObject' requires that 'Scenario?' conform to 'ObservableObject'

I've watched this video by Paul Hudson, but it is just a simple read detail. Also this one is close as well, by Swift Arcade, but it's an edit and not an edit/add. I think for most demos, simple screens which have one attribute, such as "name", seem to be dominate.

In this core data video, Stewart Lynch has a dual form for add/edit, but he's using a ViewModel. I've seen a lot of content regarding not using View Models including this one, which seemed to make sense. Paul Hudson covers the MVVM topic as well.

My form is complex, with relationship records, so I don't want to create two separate forms, one for adding and one for editing.

I'm trying to keep it simple. Any help would be appreciated.


Solution

  • I followed the pattern in a post @loremipsum recommended, but adapted it so I would not have to incorporate a view model, which is a design decision (lots of debate on this but SwiftUI + Core Data can suffice for this example).

    I added an object to the view:

    @State var newScenario: Scenario? = nil
    

    Added a sheet to view the new item:

            .sheet(item: $newScenario, onDismiss: {
                moc.rollback()
            }, content: { newItem in
                NavigationView{
                    ScenarioDetailView(scenario: newItem)
                }
            })
    

    A button to create it:

            .toolbar {
                ToolbarItemGroup (placement: .navigationBarTrailing) {
                    Button {
                        createNewScenario()
                    } label: {
                        
                        Image(systemName: "plus")
                    }
                    .padding()
                }
            }
    

    and a function to create the new Scenario causing the sheet to display:

    func createNewScenario() {
        var scenario = Scenario(context: moc)
        scenario.name = "New Scenario"
        newScenario = scenario
    }