0

Very simple SwiftUI/SwiftData app below to demonstrate concepts. I've been using the same DetailView to handle add & update in SwiftData. Using modelConext.insert(person) seems to handle BOTH additions of a new record AND updating of the existing record without any problem. Is there any reason not to use this approach in a simple app like this? Minimalist UI below just to get to the modelContext.insert point: enter image description here

import SwiftUI
import SwiftData

@Model
class Person {
    var first: String
    var last: String
    init(first: String = "", last: String = "") {
        self.first = first
        self.last = last
    }
}

struct DetailView: View {
    @State var person: Person
    @State private var first = ""
    @State private var last = ""
    @Environment(\.dismiss) private var dismiss
    @Environment(\.modelContext) var modelContext
    var body: some View {
        VStack {
            TextField("first", text: $first)
            TextField("last", text: $last)
            HStack {
                Button("Cancel") {
                    dismiss()
                }
                Button("Save") {
                    person.first = first
                    person.last = last
                    modelContext.insert(person)
                    dismiss()
                }
            }
            Spacer()
        }
        .onAppear() {
            first = person.first
            last = person.last
        }
        .padding()
        .textFieldStyle(.roundedBorder)
    }
}

struct ContentView: View {
    @Query var people: [Person]
    @State private var sheetIsPresented = false
    @Environment(\.modelContext) var modelContext
    var body: some View {
        NavigationStack {
            Button("Add") {
                print("Link clicked!")
                sheetIsPresented.toggle()
            }
            List {
                ForEach(people) { person in
                    NavigationLink {
                        DetailView(person: person)
                    } label: {
                        Text("\(person.last), \(person.first)")
                    }
                }
            }
            .listStyle(.plain)
            Spacer()
        }
        .sheet(isPresented: $sheetIsPresented) {
            NavigationStack {
                DetailView(person: Person())
            }
        }
    }
}

#Preview {
    ContentView()
        .modelContainer(for: Person.self, inMemory: true)
}

AutoSave is on, but BTW I frequently add save code after, just to push changes out to watch updates in DB Browser in real-time (which the simulator doesn't seem to push & may otherwise loose if you quit before doing one of the "save-pushing" tasks like swiping to another app. e.g. follow the modelContext.insert(person) with:

guard let _ = try? modelContext.save() else {
    print("😡 ERROR: Save on DetailView did not work.")
    return
}

Thanks!

3
  • Not clear what your problem is, but your code works well for me. I would use var person: Person without the @State in your DetailView. Commented Sep 22, 2024 at 22:58
  • Insert seems to work for both. I was t able to find something online that verified that insert was the approach to use when wanting to both create and save so I was looking for verification or correction if I have this wrong or am doing something dangerous. Saw one person on Mastodon mention this approach causes probs with relational files. Commented Sep 23, 2024 at 14:19
  • 1
    Yes it will work since SwiftData uses upsert, that is it changes an insert to an update if an object with a unique identifier already exists in the storage. In your case the unique identifier is the persistentModelID property. Commented Sep 23, 2024 at 15:27

0

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.