I implemented swiftdata to my code and now the list is not refreshing after adding an item. Before implementing swiftdata it worked.
Thats the model:
@Model
class Einkauf {
let name: String
var anzahl: Int
init(name: String, anzahl: Int) {
self.name = name
self.anzahl = anzahl
}
}
Thats the ViewModel with the func:
class EinkaufViewModel: ObservableObject {
//@Environment(\.modelContext) var context
//Before SwiftData there was @Published and it worked.
@Query var items: [Einkauf] = [
Einkauf(name: "Apfel", anzahl: 1),
Einkauf(name: "Fanta", anzahl: 10)
]
func addItem(context: ModelContext) {
let newItem = Einkauf(name: "Neuer Artikel", anzahl: 0)
context.insert(newItem)
//items.append(newItem)
}
}
Thats the View:
struct EinkaufslisteDetailView: View {
@Environment (\.modelContext) var context
@ObservedObject var vm: EinkaufViewModel
@State private var isDone: Bool = false
@State private var anzahl: Int = 5
@State private var textFieldText: String = ""
let kategorieName: String
var body: some View {
NavigationStack {
List {
ForEach(vm.items) { item in
EinkaufItemRowView(name: item.name, anzahl: item.anzahl, isDone: isDone, textFieldText: textFieldText)
.padding(.vertical)
}
}
.listStyle(.plain)
.navigationBarItems(
trailing: Button(action: { vm.addItem(context: context) },
label: {
Image(systemName: "plus.circle")
.resizable()
.frame(width: 30, height: 30)
})
/*.sheet(isPresented: $showSheet) {
EinkaufAddView(vm: vm)
.presentationDetents([.fraction(0.7)])
}*/
)
.navigationTitle(kategorieName)
}
}
}
#Preview {
EinkaufslisteDetailView(vm: EinkaufViewModel(), kategorieName: "Testname")
}
struct EinkaufItemRowView: View {
@State var name: String
@State var anzahl: Int
@State var isDone: Bool
@State var textFieldText: String
var body: some View {
HStack {
Image(systemName: isDone ? "checkmark.circle.fill" : "checkmark.circle")
.resizable()
.frame(width: 30, height: 30)
.foregroundStyle(isDone ? .green : .primary)
.padding(.horizontal)
.onTapGesture {
isDone.toggle()
}
TextField(name, text: $textFieldText)
.strikethrough(isDone ? true : false)
.font(.title2)
Spacer()
Button(action: {
//guard item.anzahl <= 1 else { return }
anzahl -= 1
}, label: {
Image(systemName: "minus.circle")
.resizable()
.foregroundStyle(.red)
.frame(width: 30, height: 30)
})
Button(action: {
anzahl += 1
}, label: {
Image(systemName: "plus.circle")
.resizable()
.foregroundStyle(.green)
.frame(width: 30, height: 30)
})
Text("\(anzahl)")
.frame(width: 40)
.padding(.horizontal, 5)
.font(.title2)
.bold()
}
}
}
Maybe I am totally wrong but I assume there is a problem at the view where the add Buttons is with the "context:context" passing.
In SwiftData, the concept is a little different.
The @Query
macro works in the view and keeps data in your view in sync with data in your ModelContext
.
You insert data into the ModelContext
and your View updates automatically!
Here's a diagram from the book "SwiftData Mastery in SwiftUI" that shows the concept:
Here is your modified code with the @Query
in the View:
@Model
class Einkauf {
let name: String
var anzahl: Int
init(name: String, anzahl: Int) {
self.name = name
self.anzahl = anzahl
}
}
@Observable
class EinkaufViewModel {
func addItem(context: ModelContext) {
let newItem = Einkauf(name: "Neuer Artikel", anzahl: 0)
context.insert(newItem)
}
}
struct EinkaufslisteDetailView: View {
@Query var items: [Einkauf] // Query on the view
@Environment (\.modelContext) var context
@State var vm: EinkaufViewModel
@State private var isDone: Bool = false
@State private var anzahl: Int = 5
@State private var textFieldText: String = ""
let kategorieName: String
var body: some View {
NavigationStack {
List {
ForEach(items) { item in
Text(item.name)
}
}
.toolbar {
Button("", systemImage: "plus.circle") {
vm.addItem(context: context)
}
}
.navigationTitle(kategorieName)
}
}
}
#Preview {
EinkaufslisteDetailView(vm: EinkaufViewModel(), kategorieName: "Testname")
.modelContainer(for: Einkauf.self, inMemory: true)
}