6

I am trying to update data in SwiftData. The documentation, however, is literally just:

func update(expressions: [String : NSExpression], 
    model: any PersistentModel.Type,
    where predicate: NSPredicate? = nil) throws -> Bool

As a new developer, I have no clue what most of this means or how to use it. My code is below:

try? context.update(expressions ["teamScores":NSExpression(format: "\(teamScores)")], model: CounterModel.self)

It failed with exception of type NSException.

I haven't been able to try much, honestly. With such a new framework having literally been released 6 days ago as of writing this, there isn't much documentation or examples outside of setting up the model, persisting data, and querying the database.

4
  • 2
    You don't have to use anything to update your data, it is all done for you when you use the @Model. Just change your data in the UI as you would normally, and that's it, SwiftData will take care of it. Commented Jun 13, 2023 at 1:18
  • thank you! i think it speaks to the absolute need of a swift-native persistence library that I thought it was so much more complicated than this. thanks again! Commented Jun 13, 2023 at 1:38
  • Have you watched the WWDC videos related to SwiftData? If you haven't then you really should, they are probably the best source of information for now. Commented Jun 13, 2023 at 13:00
  • I have, actually. I must’ve missed this if it was covered. They’re really helpful Commented Jun 13, 2023 at 20:53

2 Answers 2

10

To update data in SwiftData, we don't need to touch the modelContext directly. We just need to update the instance and the data would be updated automatically.

Here's an example with simple add list project and keep list tracked when didTapped:

@Model final class AnyItem {
    var name: String
    var didTapped: Bool = false
    init(name: String) {
        self.name = name
    }
}

struct ContentView: View {
    @State var itemName: String = ""
    @Environment(\.modelContext) private var modelContext
    @Query var items: [AnyItem]
    
    var body: some View {
        NavigationStack {
            List {
                Section {
                    TextField("Item name", text: $itemName)
                    Button("Submit", action: addItem)
                }
                
                ForEach(items) { item in
                    Section {
                        Text(item.name)
                        Text("Did tapped: \(item.didTapped.description)")
                    }
                    .onTapGesture {
                        updateItem(item)
                    }
                }
            }
        }
    }
    
    private func addItem() {
        withAnimation {
            let newItem = AnyItem(name: itemName)
            modelContext.insert(newItem)
            itemName = ""
        }
    }
    
    private func updateItem(_ item: AnyItem) {
        withAnimation {
            item.didTapped = true
        }
    }
}

#Preview {
    ContentView()
        .modelContainer(for: AnyItem.self, inMemory: true)
}
Sign up to request clarification or add additional context in comments.

Comments

4

To update the data you can bind directly to an object via @Bindable and change the values you want. Changes should then be saved/persisted automatically.

Check out the Build an app with SwiftData session (at 4:11). The code basically boils down to:

@Model
final class Item {
   var text: String
}
struct EditorView: View {
   @Bindable var item: Item

   var body: some View {
      TextField("Item Text", text: $item.text)
   }
}

PS: In this example the Item is fetched via a @Query in the main view and passed on this EditorView.

5 Comments

But for modelContainer with isAutosaveEnabled: false parameter then we've to call the context.save() manually right?
yes, u need to call save() if AuoSave is false
How would you handle your code example if the Item wasn't fetched with @Query in the main view and passed to EditorView? Would you have an .onAppear{ if let myObject = modelContext.model(for: itemID) as? Item { _item = myObject }
@dbala You could use this method, but that would require that you know the exact persistent identifier! Not sure when this is the case, but you don’t have the object (you could pass) already at hand. I think best is to go with a @Query like Pengguna showed (or in this tutorial).
@alexkaessner I found a way to implement it my way. the reason I don't want to use @ Query is because I want to only pull a subset of the @ Model's properties using a custom fetchdescriptor in the Parent level View. and then I only pass the object.persistentModelID to the Child view, and the child view does a single fetch on that persistent model ID. I was forced to do this because my usecase with @ Query had some performance issues causes hangs under heavy load going foreground->background. Now it's all solved :D Thanks for the comment

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.