A Beginner’s Guide to SwiftData with a to-do app

Jakir Hossain
5 min readSep 20, 2023

There are several option to store data locally for data persistency. SwiftData is the latest option to use for storing data in the device. Here we can write Swift-like code to perform CRUD operations. We are going to create a simple to-do app to demonstrate how to use SwiftData. Let’s create a project first. When we create a project, we can choose Swift Data as Storage. That will generate some code for us. For now, select none. We will write our own codes.

Disclaimer: SwiftData introduced in iOS 17. If you need to target lower SDK, you better use Core Data.

Data Model:

To use SwiftData, we have to create data schema first. Create new file called Item.swift or any name you want. This will be our data model. We will use only a single value to store data. That’s why we used only a variable in this model.

import Foundation
import SwiftData

@Model
class Item: Identifiable{

var name: String

init(name: String){
self.name = name
}
}

Model Context :

Model Context provides interface for tracking changes, fetching data, updating changes etc. Code to get ModelContext:

@Environment(\.modelContext) private var modelContext  

Add this code to ContentView.swift file:


import SwiftUI
import SwiftData

struct ContentView: View {

@Environment(\.modelContext) private var modelContext

var body: some View {
VStack {
Text("Hello, world!")
}
.padding()
}
}

#Preview {
ContentView()
}

Don’t forgot to import SwiftData.

Model Container:

The model container is the bridge between the model context and the storage. It creates a database from a given schema. It manages the reading, writing, deleting and updating of data.

We only need this line of code to create model container:

.modelContainer(for: Item.self)

Add this to App_Name_App.swift file:

import SwiftUI

@main
struct App_Name_App: App {
var body: some Scene {
WindowGroup {
ContentView()
} .modelContainer(for: Item.self)
}
}

Now we are ready to do CRUD operation in SwiftData.

Add data:

To add data we need only single line of code:

modelContext.insert(…)

Create a button, and try to add some to-do item in the database.

Button("Add item") {
let item = Item(name: "Task to add")
modelContext.insert(item)
}

Here we hard coded “Task to add” string. This will be added each time we click the button. Though we can’t see any changes right now. To see added data, let’s create a list and fetch data:

List{
ForEach (items) { item in
Text(item.name)
}
}

Here is the full code of ContantView.swift code so far:

import SwiftUI
import SwiftData

struct ContentView: View {

@Environment(\.modelContext) private var modelContext
@Query private var items: [Item]


var body: some View {
VStack {
Button("Add item") {
let item = Item(name: "Task to add")
modelContext.insert(item)
}

List{
ForEach (items) { item in
Text(item.name)
}
}
}
.padding()
}
}

#Preview {
ContentView()
}

Now if we run the app, we will see that, data is added to the storage.

Here, we hard coded the data to store in the storage. Let’s create a TextField and try to dynamically store the data.

HStack {
TextField("Add an item", text: $input )
Button("Add") {
addItem()
}
}

We also created a function name AddItem() to organise our code. Here is the ContantView.swift code so far:

import SwiftUI
import SwiftData

struct ContentView: View {

@Environment(\.modelContext) private var modelContext
@Query private var items: [Item]
@State private var input: String = ""


var body: some View {
VStack {
HStack {
TextField("Add an item", text: $input )
Button("Add") {
addItem()
}
}

List{
ForEach (items) { item in
Text(item.name)
}
}
}
.padding()
}

func addItem(){
let item = Item(name: input)
modelContext.insert(item)
input = ""
}
}

#Preview {
ContentView()
}

Now we can add to-do items dynamically.

Delete:

Let’s add delete functionality. To delete an item, we need to call modelContext.delete(item). Crate a function name deleteItem() so that we can call it to delete item:

   func deleteItem(_ item: Item){
modelContext.delete(item)
}

In the List item, add onDelete method and here, call deleteItem() function. Here is the full code of ContantView.swift:

import SwiftUI
import SwiftData

struct ContentView: View {

@Environment(\.modelContext) private var modelContext
@Query private var items: [Item]
@State private var input: String = ""


var body: some View {
VStack {
HStack {
TextField("Add an item", text: $input )
Button("Add") {
addItem()
}
}

List{
ForEach (items) { item in
Text(item.name)
}.onDelete(perform: { indexSet in
for index in indexSet{
deleteItem(items[index])
}
})
}
}
.padding()
}

func addItem(){
let item = Item(name: input)
modelContext.insert(item)
input = ""

}


func deleteItem(_ item: Item){
modelContext.delete(item)
}
}

#Preview {
ContentView()
}

Delete an item also works now. Swipe left an item to delete that item.

Update:

Updating also easy in SwiftData. We just need to call modelContext.save() to update an item in SwiftData. Initially you can try to add a button and hard code update like we did for add item. Here is my final code with all CRUD operations:

import SwiftUI
import SwiftData

struct ContentView: View {

@Environment(\.modelContext) private var modelContext
@Query private var items: [Item]
@State private var input: String = ""

var body: some View {

NavigationSplitView {
VStack {
HStack {

TextField("Add an item", text: $input )
Button("Add") {
addItem()
}
}
HStack {
List {
ForEach(items) { item in
NavigationLink{
Text(item.name)
TextField("Update here..", text: $input ).onAppear(){
input = item.name
}
Button("Update") {
updateItem(item)
}

} label: {
Text(item.name)
}
}.onDelete(perform: { indexSet in
for index in indexSet{
deleteItem(items[index])
}
})
}
}
}.padding()
} detail: {
Text("Select an item")
}
}

func addItem(){
let item = Item(name: input)
modelContext.insert(item)
input = ""

}

func updateItem(_ item: Item){
item.name = input
try? modelContext.save()
input = ""
}

func deleteItem(_ item: Item){
modelContext.delete(item)
}
}

I have added NavigationSplitView for better presentation. In the details view, I have added a text field to update to-do items.

Hope this helps you to understand SwiftData clearly! You can download the project from GitHub.

--

--