[WWDC2023] SwiftData Basic Use Case in 6 Steps
Tags: ios, swift, xcode, swiftdata
Published: 2023-06-14 07:14:25


![SwiftData](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/oidbyajdalubpnv6gil0.png)


It has happened! The long-awaited successor to `CoreData` has been released—`SwiftData`! Although `SwiftData` retains some of `CoreData`'s characteristics (uses the same storage architecture), it does enable us to persist data using simple, declarative code. It seems like it may be a game changer for Swift developers, so I immediately had to check it out. 

As I delved into basic use cases for `SwiftData`, like To-Do apps, I noticed that the use of `SwiftData` comes down to around 6 steps. In this article, I will guide you through the process of building a simple To-Do App using `SwiftData`, shedding light on these basic steps. The article will not cover the app's UI implementation, as the emphasis is on `SwiftData`.

Here are the six steps we'll cover:

1. `import SwiftData`.
2. Create your model: Define a class for your data.
3. Set your model to the `modelContainer`: This lets SwiftData know what data it will be working with.
4. Declare `@Environment(\.modelContext)` in your view: This establishes the context in which your data will exist and be manipulated.
5. Prefix `@Query` to an array variable: A wrapper to access your stored data.
6. Interact with your context for CRUD operations.

Let's get started by diving into each of these steps in more detail.

## 1. Import `SwiftData`

The first step is to `import SwiftData`. Yes, I know it is obvious, but we all forget sometimes. 

```swift
import SwiftData
```

## 2. Create your Model

In past, when using `CoreData`, you would open a `.xcdatamodel` file and implement an Entity by adding it with "Add Entity". In `SwiftData` we simply model data with ordinary Swift types using `@Model`. Here, I placed the `@Model` wrapper above my data class `ToDoItem`. This model will represent the individual tasks in our To-Do list. Each task will have a title, content, and the date it was added.

```swift
@Model
class ToDoItem {
    let title: String
    let content: String
    let dateAdded: Date
    
    init(title: String, content: String, dateAdded: Date = .init()) {
        self.title = title
        self.content = content
        self.dateAdded = dateAdded
    }
}
```

## 3. Set `.modelContainer()` in `App`

In `SwiftData`, the `.modelContainer()` is used to establish the storage area or "container" for your data model within your application. Think of it as designating a specific space within your app to store all the data instances of the `ToDoItem` model. 

The container is set within the main app structure (in this case, `SwiftDataToDoExampleApp` because this ensures the data is accessible throughout your entire application.

```swift
@main
struct SwiftDataToDoExampleApp: App {
    var body: some Scene {
        WindowGroup {
            ContentView()
        }
        .modelContainer(for: ToDoItem.self)
    }
}
```

In this code snippet,  `.modelContainer(for: ToDoItem.self)` is added to `WindowGroup`. This tells the application to create a storage area specifically for `ToDoItem` instances. Now, wherever we are in the app, we can manipulate our to-do items.

## 4. Declare `@Environment(\.modelContext)` in your view

Next, declare @Environment(\.modelContext) within your View.  `@Environment(\.modelContext)` allows us to read from our container. It sounds a bit complex, but it's essentially a way to establish a connection between your View and the data stored in your modelContainer from step 3. In other words, It acts like a portal, that our View can use to interact with the modelContainer.

```swift
struct ContentView: View {
	@Environment(\.modelContext) var context
	// ...
}
```

## 5.  Prefix `@Query` to an array variable

In `SwiftUI` view, you can fetch data using the wrapper `@Query`. `SwiftData` and `SwiftUI` work in together, and when the underlying data changes, the view is automatically updated with the most recent data.

```swift
@Query var items: [ToDoItem]
```

Just like in `CoreData`, you can add `SortDescriptor` and `Predicate` to a fetch. In the case below, the `ToDoItem`'s will be displayed in the reverse order of when they were added.

```swift
@Query(FetchDescriptor(sortBy: [SortDescriptor(\.dateAdded, order: .reverse)]),animation: .snappy) private var items: [ToDoItem]
```

## 6. Interact with your context for CRUD operations

Now that we have completed the set up of our model, container, context, and the way to communicate with the view, it is time to interact with our context. The first interaction with the context will be inserting data(`ToDoItem`) into the context. After inserting a `ToDoItem` into the context, don't forget to save it. This is a two-step process, and the changes made to the context are finalized by saving. 

In the snippet below I have created two functions one to insert the item and one to then save it into the context.

```swift
func insert(_ item: ToDoItem) {
    context.insert(item)
    save()
}

func save() {
    do {
        try context.save()
    } catch {
        print(error.localizedDescription)
    }
}
``` 

The same goes for deletion. Delete from the context and then save to the context to make sure it has been updated.

```swift 
func delete(_ item: ToDoItem) {
    context.delete(item)
    save()
} 
``` 

And with the above 6 steps, we have implemented `SwiftData` into our To-do app. I will include all of the UI code below so that you are able to see how I put together the whole project. Feel free to copy and paste it into Xcode. 

Finally, remember that `SwiftData` is still in beta, and it can randomly crash, especially after saving. These issues will be updated as Xcode 15 comes out of beta, I imagine. 

## App Screenshot


![App Preview Gif](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/6r0k8zc5b6lveyqz0067.gif)



## The Code

ContentView.swift

```swift
import SwiftUI
import SwiftData

struct ContentView: View {
    @Environment(\.modelContext) var context
    @Query var items: [ToDoItem]
    
    @State private var showingSheet: Bool = false
    @State private var title: String = ""
    @State private var content: String = ""
    
    var body: some View {
        NavigationStack {
            List {
                ForEach(items)  { item in
                    VStack(alignment: .leading) {
                        HStack(alignment: .top) {
                            
                            Text(item.title)
                                .bold()
                            
                            Spacer()
                            
                            Text(item.dateAdded.formatted(date: .numeric, time: .shortened))
                                .font(.caption)
                                .foregroundStyle(.gray)
                            
                        }
                        
                        Text(item.content)
                            .font(.subheadline)
                    }
                    .swipeActions(content: {
                        Button(action: {
                            self.delete(item)
                        }, label: {
                            Label("Delete", systemImage: "xmark.bin.fill")
                        })
                        .tint(Color.red)
                    })
                }
            }
            .navigationTitle("To Do List")
            .toolbar {
                ToolbarItem(placement: .topBarTrailing) {
                    Button {
                        showingSheet.toggle()
                    } label: {
                        Image(systemName: "plus")
                    }
                    
                }
            }
            .sheet(isPresented: $showingSheet, content: {
                Form {
                    Section {
                        TextField("Add the title...", text: $title)
                    } header: {
                        Text("Title")
                    }
                    
                    Section {
                        TextEditor(text: $content)
                    } header: {
                        Text("Content / Notes")
                    }
                    
                    Section {
                        Button("Add Item") {
                            let item = ToDoItem(title: title, content: content)
                            insert(item)
                            
                            showingSheet = false
                            title = ""
                            content = ""
                        }
                        .disabled(title.isEmpty || content.isEmpty)
                    }
                }
            })
        }
    }
    
    func insert(_ item: ToDoItem) {
        context.insert(item)
        save()
    }
    
    func delete(_ item: ToDoItem) {
        context.delete(item)
        save()
    }
    
    func save() {
        do {
            try context.save()
        } catch {
            print(error.localizedDescription)
        }
    }
}
```

ToDoItem.swift

```swift
import SwiftUI
import SwiftData

@Model
class ToDoItem {
    let title: String
    let content: String
    let dateAdded: Date
    
    init(title: String, content: String, dateAdded: Date = .init()) {
        self.title = title
        self.content = content
        self.dateAdded = dateAdded
    }
}
```

SwiftDataToDoExampleApp.swift

```swift
import SwiftUI

@main
struct SwiftDataToDoExampleApp: App {
    var body: some Scene {
        WindowGroup {
            ContentView()
        }
        .modelContainer(for: ToDoItem.self)
    }
}
```

Thanks for checking out the post.
All the best!

Dean Thompson

Follow me!
[LinkedIn](https://www.linkedin.com/in/deanwthompson/)
[Twitter](https://twitter.com/DeanWThompson)
[Instagram](https://www.instagram.com/thompson.dean)

## References 

[Apple's Press Release](https://developer.apple.com/xcode/swiftdata/)

Great example using SwiftData from [KavSoft](https://www.youtube.com/watch?v=4nRvm7EsB8s)

Great article by Alexander Logan. Also goes into depth on using SwiftData with relationships: [Alexander Logan's Blog](https://alexanderlogan.co.uk/blog/wwdc23/03-swift-data?hello=true)