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?
Defining Your Realm Object Model
Open and look at Ingredient.swift, inside the Models group.
This class defines an ingredient in the domain of PotionsMaster. Notice it’s a plain Swift class with Published
properties and an id
to identify each object.
This object is used inside the list of ingredients and inside the form to create or edit an ingredient. However, if you try to modify any ingredient or create a new one, you see the list doesn’t change. Even though you can bind the properties of this class to the form, you’re not yet persisting any changes.
You’ll modify this object to store it in Realm now.
Still in Ingredient.swift, begin by adding the import for RealmSwift
:
import RealmSwift
and then replace the declaration of the class with the following:
class Ingredient: Object, ObjectKeyIdentifiable {
Object
is a type alias to RealmSwiftObject
. This is the class Realm uses to store data inside a realm. By subclassing Ingredient
to it, you’re able to store this class in the database.
Like Identifiable
, ObjectKeyIdentifiable
is a protocol Realm uses to identify Realm objects.
Next, replace the following lines:
@Published var title = ""
@Published var notes = ""
@Published var quantity = 1
@Published var bought = false
With the following code:
@Persisted var title = ""
@Persisted var notes = ""
@Persisted var quantity = 1
@Persisted var bought = false
@Persisted
is a property wrapper like Core Data’s @NSManaged
. It defines properties as managed by the Realm framework, allowing it to store their value.
Realm Property Types
Although you’re still using regular Swift classes to store objects in it, Realm has a limited set of platform-independent property types because of its cross-platform nature.
You can define properties with the following types:
- Bool
- Int, Int8, Int16, Int32, Int64
- Double
- Float
- String
- Date
- Data
- Decimal128
Every property of Ingredient
is declared with a default required value. But you can declare optional properties, too. To define an optional property, all you have to do is make the property optional, like any other Swift property.
Relationships
Realm also supports relationships. You can declare nested objects to create many-to-one relationships. And you can use List
to create many-to-many relationships. When declaring a List
, Realm saves those nested objects together with your model.
Before you move on, find this code inside Ingredient.swift:
let id = UUID()
And change it to the following:
@Persisted(primaryKey: true) var id: ObjectId
This changes the ID from UUID
to ObjectId
and uses the Persisted(primaryKey:)
to set this value as the primary key of this object. The framework uses this property to enforce uniqueness of objects and to fetch objects from Realm.
Now that you’ve defined your Realm object, it’s time to write code to fetch and add objects to your database.
Fetching Realm Objects
Fetching objects from Realm is simple. You can instantiate a realm and fetch objects from it. However, the Realm team created handy property wrappers, which are like Core Data’s FetchRequest
, to use Realm with SwiftUI.
You’ll use them to fetch objects from Realm and observe changes to the database to update the view whenever you add, edit or delete an object from Realm.
Inside Views, open IngredientListView.swift. Add the RealmSwift
import:
import RealmSwift
and find the following two State properties:
@State var ingredients: [Ingredient] = []
@State var boughtIngredients: [Ingredient] = []
Replace them with the following:
// 1
@ObservedResults(
// 2
Ingredient.self,
// 3
where: { $0.bought == false }
) var ingredients
// 4
@ObservedResults(
Ingredient.self,
where: { $0.bought == true }
) var boughtIngredients
Here’s a breakdown of the code:
- You define a property,
ingredients
, and mark it with@ObservedResults
. - The first parameter of
@ObservedResults
is the type of the object you want to fetch from Realm: in this case,Ingredient
. - You also use a
where
closure argument to filter for only the ingredients that haven’t been bought. - Here, you define another property,
boughtIngredients
, and also mark it with@ObservedResults
. However, this time, you filter the ingredients that have been bought.
However, it’s often better to use the where
form of object filtering, because that tells the Swift compiler to check that the bought
property of
filter
argument calling style that takes an NSPredicate
to filter your objects like Core Data does. For example:
@ObservedResults( Ingredient.self, filter: NSPredicate(format: "bought == true") ) var boughtIngredients
However, it’s often better to use the where
form of object filtering, because that tells the Swift compiler to check that the bought
property of
@ObservedResults( Ingredient.self, filter: NSPredicate(format: "bought == true") ) var boughtIngredients
Understanding @ObservedResults
@ObservedResults
is a property wrapper you can use to fetch and observe objects from a realm. This property fetches objects and returns a Results
type, which is a type from Realm that represents a collection of objects retrieved from queries.
By replacing both arrays in your view and adding those two properties, you’re telling Realm to fetch ingredients from the database and display them in the view. And you can even filter the results, just like you’re doing with the bought ingredients.
This is a powerful property wrapper. Whenever you add or remove objects that are ingredients in the database, Realm updates those properties and SwiftUI updates its views.
Before you move on, open ContentView.swift and replace the code inside NavigationView
with the following:
IngredientListView()
This instantiates an IngredientListView without passing any values to the properties.
Build and run the project.
You’re not seeing the mocked data anymore and the list is gone. That’s because the database is empty and there’s nothing to list. It’s time to populate the database with objects.