[Basics] Saving in SwiftData doesn't work

GM, I'm kinda new in SwiftData, I'm trying to build an app with Steppers and I need to acess the value of steppers in other views and save them. I started with this code but it doen't save the results, can someone please help? Thanks alot.

import SwiftData
@main
struct TesteStepApp: App {
var body: some Scene {
WindowGroup {
ContentView()
}
.modelContainer(for: GroceryListItem.self)
}
}
@Model
class GroceryListItem {
let stepperValue: Int
init(stepperValue: Int = 0) {
self.stepperValue = stepperValue
}
}
struct ContentView: View {
@Environment(\.modelContext) var context
@State private var stepperValue: Int = 0
var body: some View {
VStack {
Stepper("Value: \(stepperValue)", value: $stepperValue, in: 0...100)
.padding()
}
.padding()
.onChange(of: stepperValue) { oldValue, newValue in
insertValue(newValue)
}
}
private func insertValue(_ value: Int) {
}
}
#Preview {
ContentView()
}
Answered by mcomisso in 794402022

The stepper should act on the model that is stored in memory. Here you are inserting a new value, but it should increase the stepperValue in an entity of GroceryListItem.

First you need to create an entity, and save it.

let item = GroceryListItem()
context.insert(item)

Then you need to load that item, and use it in a binding to directly bind the stepper to its value.

This could become your list of elements:

List {
ForEach(items) { item in
@Bindable var bindingItem = item
NavigationLink {
// Destination view, a simple Stepper.
Stepper(value: $bindingItem.stepperValue) {
Text("\(item.stepperValue)")
}
} label: {
Text("Item with value: \(item.stepperValue)")
}
}
}

onChange is not needed for this. :)

Accepted Answer

The stepper should act on the model that is stored in memory. Here you are inserting a new value, but it should increase the stepperValue in an entity of GroceryListItem.

First you need to create an entity, and save it.

let item = GroceryListItem()
context.insert(item)

Then you need to load that item, and use it in a binding to directly bind the stepper to its value.

This could become your list of elements:

List {
ForEach(items) { item in
@Bindable var bindingItem = item
NavigationLink {
// Destination view, a simple Stepper.
Stepper(value: $bindingItem.stepperValue) {
Text("\(item.stepperValue)")
}
} label: {
Text("Item with value: \(item.stepperValue)")
}
}
}

onChange is not needed for this. :)

Thank you for your help, but how can I create that entity? :/

is it inside the @Model?

I tried with addItem but it didn't work :(

can you please show me the full code? i would be grateful :)

anyone pls? :)

Don't know if you already got your answer elsewhere but I included a working sample app below.

Like mcomisso already said, to create a new entity, you have to insert it into the database using the ModelContext.

let newItem = GroceryListItem(name: "New Item", stepperValue: 1)
context.insert(newItem)

Where you do this depends on your app.

GroceryListApp.swift

import SwiftUI
import SwiftData
@main
struct GroceryListApp: App {
var body: some Scene {
WindowGroup {
ContentView()
}
.modelContainer(for: GroceryListItem.self)
}
}

GroceryListItem.swift

import SwiftData
@Model
final class GroceryListItem {
var name: String
var stepperValue: Int
init(name: String, stepperValue: Int = 1) {
self.name = name
self.stepperValue = stepperValue
}
}

ContentView.swift

import SwiftUI
import SwiftData
struct ContentView: View {
@Environment(\.modelContext) var context
@Query var groceryListItems: [GroceryListItem]
@State private var newItemName: String = ""
var body: some View {
List {
Section {
TextField("New Item", text: $newItemName)
Button("Add new item", action: insertNewItem)
}
Section {
ForEach(groceryListItems) { groceryListItem in
GroceryListItemView(groceryListItem: groceryListItem)
}
.onDelete(perform: deleteItem)
}
}
}
func insertNewItem() {
let newItem = GroceryListItem(name: newItemName)
context.insert(newItem)
newItemName = ""
}
func deleteItem(at indexSet: IndexSet) {
for index in indexSet {
context.delete(groceryListItems[index])
}
}
}
#Preview {
ContentView()
.modelContainer(for: GroceryListItem.self, inMemory: true)
}

GroceryListItemView.swift

import SwiftUI
struct GroceryListItemView: View {
@Bindable var groceryListItem: GroceryListItem
var body: some View {
Stepper("\(groceryListItem.name) (\(groceryListItem.stepperValue)x)", value: $groceryListItem.stepperValue, in: 1...100)
}
}
#Preview {
// Note that @Previewable was introduced in the XCode 16 Beta
@Previewable @State var previewItem = GroceryListItem(name: "Milk")
List {
GroceryListItemView(groceryListItem: previewItem)
}
}

(I like to keep my view body clear of all logic so I extracted the @Binding and the list item into its own view.)

Screenshot

Thank you so much!

You helped me a lot. I really appreciate it.

Best wishes.

[Basics] Saving in SwiftData doesn't work
 
 
Q