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.
Sign up/Sign in
With a free Kodeco account you can download source code, track your progress, bookmark, personalise your learner profile and more!
Create accountAlready a member of Kodeco? Sign in
Sign up/Sign in
With a free Kodeco account you can download source code, track your progress, bookmark, personalise your learner profile and more!
Create accountAlready a member of Kodeco? Sign in
Contents
Realm With SwiftUI Tutorial: Getting Started
30 mins
- Getting Started
- Project Structure
- Working With Realm
- Understanding the Realm Database
- Setting up Realm
- Defining Your Realm Object Model
- Realm Property Types
- Relationships
- Fetching Realm Objects
- Understanding @ObservedResults
- Adding Objects to the Database
- Updating Objects
- Toggling Ingredients to BOUGHT
- Updating Other Properties
- Deleting Objects
- Adding a New Property to a Realm Object
- Storing the New Property in Realm
- Working With Migrations
- Creating a Migration
- Updating Ingredient Row
- Where to Go From Here?
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.
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.
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.
Update its properties and tap Done.
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.
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:
- First, you import
SwiftUI
andRealmSwift
- Then, you define an enum named
ColorOptions
and extend it to String. You also conform it toCaseIterable
, to list all cases of the enum and toPersistableEnum
. This is a protocol Realm uses to store enum values in the database. - Next, you define three cases,
green
,lightBlue
andlightRed
. Those are the color options users will choose for each ingredient. - Here, you add a computed property to instantiate a
Color
from the project assets. - 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.