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.

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.

Storing the New Property in Realm

Now that you’re ready to store the new color, it’s time to update IngredientFormView.swift to let users select the ingredient color. You’ll use a Picker view to allow users to select the color from the available options.

Open IngredientFormView.swift and add the following property:

let colorOptions = ColorOptions.allCases

This adds a property to store all cases of ColorOption. You’ll use this to list all color options in the picker view.

Next, find the comment // To Do Add Color Picker and replace it with the code below:

Picker("Color", selection: $ingredient.colorOption) {
  ForEach(colorOptions, id: \.self) { option in
    Text(option.title)
  }
}

This adds a Picker view to the form, binding the property colorOption form to the ingredient, with all the cases from ColorOption.

Build and run. And you get …

Xcode console Realm migration crash

The app crashes as soon as you open it! Realm throws a migration error: Migration is required due to the following errors. But why is this happening?