BLACK FRIDAY: Save 50% on all my Swift books and bundles! >>

Creating books with SwiftData

Paul Hudson    @twostraws   

Our first task in this project will be to design a SwitData model for our books, then creating a new view to add books to the database.

First, the model: create a new file called Book.swift, add an import for SwiftData, then give it this code:

@Model
class Book {
    var title: String
    var author: String
    var genre: String
    var review: String
    var rating: Int
}

That class needs an initializer to provide values for all its properties, but here's a tip: if you just start typing "in" inside the class, Xcode should autocomplete the whole initializer for you.

That class is enough to store the title of the book, the name of whoever wrote the book, a genre, a brief overview of what the user thought of the book, and also the user’s numerical rating for it.

Now that we have our data model, we can ask SwiftData to create a model container for it. This means opening BookwormApp.swift, adding import SwiftData to the top of the file, then adding this modifier to the WindowGroup:

.modelContainer(for: Book.self)

Our next step is to write a form that can create new entries. This will combine so many of the skills you’ve learned so far: Form, @State, @Environment, TextField, TextEditor, Picker, sheet(), and more, plus all your new SwiftData knowledge.

Start by creating a new SwiftUI view called “AddBookView”. In terms of properties, we need an environment property to get access to our model context:

@Environment(\.modelContext) var modelContext

As this form is going to store all the data required to make up a book, we need @State properties for each of the book’s values. So, add these properties next:

@State private var title = ""
@State private var author = ""
@State private var rating = 3
@State private var genre = "Fantasy"
@State private var review = ""

Finally, we need one more property to store all possible genre options, so we can make a picker using ForEach. Add this last property to AddBookView now:

let genres = ["Fantasy", "Horror", "Kids", "Mystery", "Poetry", "Romance", "Thriller"]

We can now take a first pass at the form itself – we’ll improve it soon, but this is enough for now. Replace the current body with this:

NavigationStack {
    Form {
        Section {
            TextField("Name of book", text: $title)
            TextField("Author's name", text: $author)

            Picker("Genre", selection: $genre) {
                ForEach(genres, id: \.self) {
                    Text($0)
                }
            }
        }

        Section("Write a review") {                
            TextEditor(text: $review)

            Picker("Rating", selection: $rating) {
                ForEach(0..<6) {
                    Text(String($0))
                }
            }                
        }

        Section {
            Button("Save") {
                // add the book
            }
        }
    }
    .navigationTitle("Add Book")
}

When it comes to filling in the button’s action, we’re going to create an instance of the Book class using all the values from our form, then insert the object into the model context.

Add this code in place of the // add the book comment:

let newBook = Book(title: title, author: author, genre: genre, review: review, rating: rating)
modelContext.insert(newBook)

That completes the form for now, but we still need a way to show and hide it when books are being added.

Showing AddBookView involves returning to ContentView.swift and following the usual steps for a sheet:

  1. Adding an @State property to track whether the sheet is showing.
  2. Add some sort of button – in the toolbar, in this case – to toggle that property.
  3. A sheet() modifier that shows AddBookView when the property becomes true.

Enough talk – let’s start writing some more code. Please start by adding an import for SwiftData to ContentView,swift, then add these properties to the ContentView struct:

@Environment(\.modelContext) var modelContext
@Query var books: [Book]

@State private var showingAddScreen = false

That gives us a model context we can use later on to delete books, a query reading all the books we have (so we can test everything worked), and a Boolean that tracks whether the add screen is showing or not.

For the ContentView body, we’re going to use a navigation stack so we can add a title plus a button in its top-right corner, but otherwise it will just hold some text showing how many items we have in the books array – just so we can be sure everything is working. Remember, this is where we need to add our sheet() modifier to show an AddBookView as needed.

Replace the existing body property of ContentView with this:

 NavigationStack {
    Text("Count: \(books.count)")
        .navigationTitle("Bookworm")
        .toolbar {
            ToolbarItem(placement: .topBarTrailing) {
                Button("Add Book", systemImage: "plus") {
                    showingAddScreen.toggle()
                }
            }
        }
        .sheet(isPresented: $showingAddScreen) {
            AddBookView()
        }
}

Tip: That explicitly specifies a trailing placement for the button so that we can add a second button later.

Bear with me – we’re almost done! We’ve now designed our SwiftData model, created a form to add data, then updated ContentView so that it can present the form when needed. The final step is to make the form dismiss itself when the user adds a book.

We’ve done this before, so hopefully you know the drill. We need to start by adding another environment property to AddBookView to be able to dismiss the current view:

@Environment(\.dismiss) var dismiss

Finally, add a call to dismiss() to the end of your Save button’s action closure.

You should be able to run the app now and add an example book just fine. When AddBookView slides away the count label should update itself to 1.

Save 50% in my WWDC sale.

SAVE 50% All our books and bundles are half price for Black Friday, so you can take your Swift knowledge further without spending big! Get the Swift Power Pack to build your iOS career faster, get the Swift Platform Pack to builds apps for macOS, watchOS, and beyond, or get the Swift Plus Pack to learn advanced design patterns, testing skills, and more.

Save 50% on all our books and bundles!

BUY OUR BOOKS
Buy Pro Swift Buy Pro SwiftUI Buy Swift Design Patterns Buy Testing Swift Buy Hacking with iOS Buy Swift Coding Challenges Buy Swift on Sundays Volume One Buy Server-Side Swift Buy Advanced iOS Volume One Buy Advanced iOS Volume Two Buy Advanced iOS Volume Three Buy Hacking with watchOS Buy Hacking with tvOS Buy Hacking with macOS Buy Dive Into SpriteKit Buy Swift in Sixty Seconds Buy Objective-C for Swift Developers Buy Beyond Code

Was this page useful? Let us know!

Average rating: 4.9/5

 
Unknown user

You are not logged in

Log in or create account
 

Link copied to your pasteboard.