Realm With SwiftUI Tutorial: Getting Started

Learn how to use Realm with SwiftUI as a data persistence solution by building a potion shopping list app. By Renan Benatti Dias.

5 (5) · 3 Reviews

Download materials
Save for later
Share
You are currently viewing page 3 of 4 of this article. Click here to view the first page.

Adding Objects to the Database

Open IngredientFormView.swift and import RealmSwift at the top of the file.

import RealmSwift

Next, find the following property:

@ObservedObject var ingredient: Ingredient

And replace it with the following:

@ObservedRealmObject var ingredient: Ingredient

ObservedRealmObject is a Realm property wrapper that behaves much like ObservedObject. You use it to observe changes to the state of Realm objects and update the view whenever the object changes. You’ll also use it to update the object in the database later.

Next, at the top of the view, add the following environment property:

@Environment(\.realm) var realm

This environment property stores the default realm you use to store and fetch objects from the database.

Finally, find the method save() at the bottom of the file and replace the contents of it with the following:

try? realm.write {
  realm.add(ingredient)
}
dismiss()

Here, you start a write transaction by calling write(withoutNotifying:_:) on the Realm object. Each operation you make on a realm must be inside this write transaction block, including additions, deletions and updates. Inside the transaction, you add the new instance of Ingredient to Realm. Realm now stores the object and tracks its changes, making it a managed object.

Build and run. Tap New Ingredient and type a title and note. Save the ingredient to see it in your list.

Ingredients list showing ginger

Success! You can now add and list objects in the app. When you open IngredientFormView, you also pass an empty ingredient object to it. When you add this ingredient in the database, Realm updates the property ingredients inside IngredientListView and SwiftUI updates your view. Feels like magic, right? Go ahead and create some more ingredients! :]

Now that you can successfully add an ingredient, it’s time to build the functionality to update existing ingredients.

Updating Objects

A key feature of PotionsMaster is the ability to toggle an ingredient to the BOUGHT list. Right now, if you tap the buy button, nothing happens. To fix this, you use Realm to update ingredients on disk.

Toggling Ingredients to BOUGHT

To move an ingredient to the BOUGHT list, you need to update the property value of bought to true on disk.

Open IngredientRow.swift and import RealmSwift.

import RealmSwift

Next, replace the following line:

let ingredient: Ingredient  

With the following:

@ObservedRealmObject var ingredient: Ingredient

Once again, you’re using ObservedRealmObject to observe changes to an ingredient in the database and update the view when its properties change.

Next, find and replace the contents of toggleBought() with the following:

$ingredient.bought.wrappedValue.toggle()

This code toggles the bought property of that ingredient to true if the ingredient hasn’t been bought yet, or to false if the ingredient has already been bought.

Notice the $ before the ingredient. Instead of calling write(withoutNotifying:_ :) to start a write transaction, you use $ in your Realm object to automatically start a write transaction and update the value of bought.

Build and run. Tap the circular icon on the right of the row to buy an ingredient.

Ingredient in the bought section

Awesome! You update ingredient, and Realm notifies the results inside IngredientListView to update the list. How cool is that? :]

You can also update objects by starting a write transaction and updating each property of the object inside that transaction.

You’ll do this next to update the rest of the properties of an ingredient.

Updating Other Properties

Now that you know how to update an object, you can use Realm to update other properties just as easily. Go back to IngredientFormView.swift and replace the computed property isUpdating with the following code:

var isUpdating: Bool {
  ingredient.realm != nil
}

This updates isUpdating to check if the ingredient in the form has been persisted in the database. When you tap New Ingredient, you pass to IngredientFormView a new instance of Ingredient. This is an unmanaged object. That means the database doesn’t know about it yet and any changes won’t persist until you add it to Realm.

When you open IngredientFormView with a new instance of Ingredient, this instance doesn’t have a realm yet, meaning it has yet to be saved in Realm. However, when you open IngredientFormView by tapping a row and passing its ingredient to the form, this ingredient has already been stored in the database and, thus, you’re updating it.

Build and run. Tap an ingredient in the list to open the form.

Ingredient form updating

Update its properties and tap Done.

List with ingredient in the bought section

Fantastic! You didn’t have to change a single line of code in the body of the view to update the ingredient. Realm objects have bindings to SwiftUI controls and, much like toggling the bought property, they already start a realm transaction to write the changes in the database whenever you change those values.

Now, all that’s left is deleting ingredients!

Deleting Objects

Tapping the buy button moves an ingredient to the BOUGHT section. But once it’s there, you can’t get rid of it. You can tap the blue icon again to move it back, but you can’t remove it from the list.

You’ll use the List‘s swipe gesture to remove ingredients from the list.

Open IngredientListView.swift and find the following code in the second Section:

ForEach(boughtIngredients) { ingredient in
  IngredientRow(ingredient: ingredient)
}

Next, add the following just under the ForEach view:

.onDelete(perform: $boughtIngredients.remove)

onDelete(perform:) is a view modifier for adding a delete action to the row of a list. It passes an IndexSet of the items to remove from the list.

You use Realm’s Results type to remove(atOffsets:) binding for removing those objects from Realm. This creates a write transaction and removes the swiped ingredient from Realm. Realm takes care of updating boughtIngredients, and SwiftUI updates the list.

Build and run the app. Buy ingredients and swipe left to delete them from the database.

List with bought ingredient being swiped left to delete it

Adding a New Property to a Realm Object

During development, it’s common for data models to grow and evolve. Property types might change, and you might need to add or remove properties. With Realm, changing your objects is as easy as changing any other Swift class.

In this section, you’ll add a new property to identify your ingredients by color.

Inside the Models group, create a new swift file and name it ColorOptions.swift. Add the following code to the new file:

// 1
import SwiftUI
import RealmSwift

// 2
enum ColorOptions: String, CaseIterable, PersistableEnum {

  // 3
  case green
  case lightBlue
  case lightRed

  // 4
  var color: Color {
    Color(rawValue)
  }

  // 5
  var title: String {
    switch self {
    case .green:
      return "Green"
    case .lightBlue:
      return "Light Blue"
    case .lightRed:
      return "Light Red"
    }
  }
}

Here’s a breakdown of this enum:

  1. First, you import SwiftUI and RealmSwift
  2. Then, you define an enum named ColorOptions and extend it to String. You also conform it to CaseIterable, to list all cases of the enum and to PersistableEnum. This is a protocol Realm uses to store enum values in the database.
  3. Next, you define three cases, green, lightBlue and lightRed. Those are the color options users will choose for each ingredient.
  4. Here, you add a computed property to instantiate a Color from the project assets.
  5. Finally, you define another computed property for the title of each case.

Now, open Ingredient.swift and add a new property under bought:

@Persisted var colorOption = ColorOptions.green

You add this property to store a color option with the ingredient in the database.

That’s it! The models are ready to store a color. Next, you’ll update IngredientFormView.swift to save this new property in your database.