CRUD Operation Using RealmSwift Part 1
In this part, we will Cover:
- What is Realm
- RealmSwift
- Creating a Complex Realm Model
- Why we use @objc and dynamic in Property
- To-One and To-Many Relationship in Realm
- How to store Custom enum in Realm
- What is RealmOptional and How to Store Swift Optional Atomic Property
- How Computed Property works as Transient Property In realm
- How to do indexing for performance gain
- What is Linking Object in Realm
- Complex Predicate Queries
- CRUD Operation
- What is Live Results in Realm
- Things avoided when Modify Realm Objects
Realm
Realm is a cross-platform mobile object database. It’s very fast, performant and easy to use as compared to Core Data and Sqlite. It uses its storage mechanism to store object as JSON on disk as compared to Core Data that uses Sqlite as its backend. It is written in cross-platform C++ so it works exactly the same way on Android, iOS, macOS or any other platform.
RealmSwift
Most of Realm is open-source, but the secret sauce behind Realm’s platform is the Core DB engine written from scratch in C++. RealmSwift is a wrapper around the Objective-C Realm framework and Objective-C Realm framework is a wrapper around Realm Core DB engine.
Getting Started
In this tutorial we will be working on these models as shown in Figure 1
User → It’s a model contains user information in which userId
it’s primary. It also has a To-one relationship with passport model and To-Many relationships with Todo tasks. (User can have only passport and have many todo Tasks)
Passport → It’s a model contains passport information of a user and also has a reverse back link of User
object.
Todo → It’s a model contains tasks of a user and also has a reverse back link of User
object.
User Model
As shown in Figure 2 we created User model and adds a few properties:
- First we inherit
User
class fromObject
which makes itRealm Object
. Realm objects are basically a standard data model, much like any other standard data model you’ve defined in your apps. The only difference is they’re backed by Realm persistence and abilities. By looking into allObject
class Realm are able to infer your schema. Every model you want to persist you have to inherit from Object class. firstName
stores the user first name asString
. SinceRealmSwift
is a wrapper around the Objective-C Realm framework Types like String, Date and Data are sub-classes of NSObject in Objective-C, so you can consider them object types in Swift as well. In addition to this @objc means you want your Swift code (class, method, property, etc.) to be visible from Objective-C whereas dynamic means you want to use Objective-C dynamic dispatch. To make simple swift object stores in database Realm uses Objective-C dynamic dispatch feature to do work under the hooduserId
stores the User id which acts as a primary key. You can set one of your object’s properties as its primary key. Usually, property that uniquely identifies itself, that is a prime candidate for a primary key. It helps you a quick lookup or edit object in a database. As shown in Figure 2 we makeuserId
primary key by overridingprimarykey
static function. The default implementation of this method return nil. NoteuserId
acts as a Objective C primitive Int type since it is a wrapper around Objective C Realmpassport
property stores the user passport information which is another Realm Object. We created To-One relationship with the Passport Model. To-one relationship / object link , in which one realm object point to another realm object. When you create a relationship with another Realm object, its property must be of an Optional type. In passport variable pointer reference ofPassport
object will storeprivateUserType
is the wrapper property ofUserType
since UserType is swift enum we can’t store directly on Realm. All of its case values would have an implicitly assigned raw value matching the case’s name. You’ll use this raw value to persist the enum options asInt
in Realm. But client is accessing computed enum property and under the hood enum raw value/atomic value is storing in database which is not visible to client.- String, NSDate, and NSData properties can be declared as optional or non-optional using the standard Swift syntax.Optional numeric types are declared using RealmOptional. Since RealmSwift is wrapper around Objective C Realm and there is no optional Int in Objective C that’s why Realm created it’s type RealmOptional for this scenarios. All the Objective C primitive type require in Swift with Optional you have to use RealmOptional . Note: RealmOptional properties cannot be declared as dynamic and @objc keyword because generic properties cannot be represented in the Objective C runtime, which is used for dynamic dispatch of dynamic properties, and should always be declared with let. Since It’s Realm class it has all features that Realm persist object have. As shown in Figure 2
isEmailSubscriptionEnable
we declare asRealmOptional
Bool means it can be nil we used with let since its a reference type and we don’t want its address to change in future. - As shown in Figure 2 User can have many todos which is a collection of Realm Todo Objects . List as Realm class to hold collection of the Realm Object instances. We created To-Many relationship with the Todo model. To-many relationship , in which one realm object point to collection of realm object. If you use normal Swift array to store Realm collection of Object you will get exception. Note: Like RealmOptional List cannot be declared as dynamic and @objc keyword since its a Realm class with all the built in features. List is very similar to Array for built in methods and accessing objects using indexed subscripting. List as you see is typed and all objects should be of the same type
isUserHasTodos
is a computed swift property and will not store in realm database which return if user has some tasks to do or not.- Finally we make
userId
andfirstName
as indexed properties. By overridingindexedProperties
static method we provide array of properties in String form. We do indexed on properties to improve the access times when filtering or querying the database
Passport Model
As shown in Figure 3 we created Passport model with the few properties
passportNumber
stores the Passport information andexpiryDate
stores the expiry date of the passport we want these properties to store that’s why we use@objc
withdynamic
keyword to tell realm do your under the hood magic- We created
passport
property onUser
model which meansUser
object have theirpassport
object reference what if we want passport object also know which user has this passport / have a reference to the user associated with this passport. We created the backlinks using LinkingObjects which means we createdofUser
property in Passport that have a reference of all User objects that assign Passport object in itspassport
property. In Core Data its called it as inverse relationship. Its a dynamic collection telling you who links to the current object.
As shown in Figure 4 we created Todo
Realm model with has a backlink to the all the users pointing to particular task .
We done with the model / schema creation and now it's time to do actual CRUD operation
Add Object to Realm
We first clear some concept then we will be able to easily add object in Realm
As shown in Figure 5 we start by getting an instance of the default Realm
by initializing it without any arguments. The only way you can access database through realm instance. A Realm
instance (also referred to as “a Realm”) represents a Realm database.Realms can either be stored on disk (see init(path:)
) or in memory (see Configuration
) we will see in upcoming parts. Realm
instances are not thread safe and cannot be shared across threads or dispatch queues. You must construct a new instance for each thread in which a Realm will be accessed.
As shown in Figure 5 since it’s a fresh app and no object was stored in the database realm.isEmpty
returns true means database is empty as printed on the console
As shown in Figure 6 we performed number of tasks
- We get the instance of
Realm
database - Created
Passport
and three tasks as atodos
- Created
User
object and assign passport and todos to it. In addition to this initialized Realm optional property which isisEmailSubscriptionEnable
andUsertype
enum with gold membership
As shown in Figure 6 realm database still empty and inverse relationship/ Linking object is not working either because we didn’t added these objects to the realm. Realm objects (User,Passport,Todo)can be instantiated and used as unmanaged objects (i.e. not yet added to a Realm) just like regular Swift objects. To make theses objects managed by Realm you have to add them in Realm
As shown in Figure 7 we finally insert object into Realm database. We performed cascading insert into the DB.
Note: All changes to an object (addition, modification and deletion) must be done within a write transaction.Realm write operations are synchronous and blocking, not asynchronous. If thread A starts a write operation, then thread B starts a write operation on the same Realm before thread A is finished, thread A must finish and commit its transaction before thread B’s write operation takes place. Write operations always refresh automatically so No race condition is created. Write operation can throw error like running out of disk space
- We added User to your Realm, and since it references passport and todos, these objects are also added to the Realm.
- By adding unmanaged object to realm we made these objects Managed now as shown in the console our backlinks works. Now passport can access User object as well. Now our database is no more empty
- We finally insert object into the database since we are inserting object we have to add in the write transaction block and we add into the realm by using add method on Realm instance. Now If another separate object with the same primary key of User with
userId = 1
is attempted to be added as a separate object to the Realm, an exception will be triggered
Check Object Physically Store
By running this command on debugger output you get the address of realm file where your data is stored
Open
default.realm
in Realm Studio
Realm Studio is our premiere developer tool, built so you can easily manage the Realm Database and Realm Platform. With Realm Studio, you can open and edit local and synced Realms, and administer any Realm Object Server instance. Download it now for Mac, Windows, or Linux.
As shown in Figure 10 data actually stored. You may wonder Article
and Person
class also there because in some of my project class create models Person and Article. When application runs, Realm introspects all of the classes in your app and frameworks and finds those classes that subclass Realm’s Object class. It considers the list of these classes to be your data schema that will be persisted on disk or in-memory.
Fetch Object from Realm
The process of fetching all the User records from Realm has following tasks
- We get the instance of Realm Database and it can throws: An
NSError
if the Realm could not be initialized. Default realm is created when we callinit()
without parameter - Called objects method on Realm database which will return all objects of the given type stored in the Realm and it will return a Results with all the objects as shown in Figure 11
- Printed object on console to validate data is there and Linking objects working fine
We fetched User record from its primary key which is also a indexed property so we get the optional User object since object with this primary key might not exists. Primary key can be Int
or String
The recommendation is to use String.
It uniquely identify specific objects in a Realm database.Once a primary key value has been set on a specific object, it cannot ever be changed.
As shown we filtered using some Predicate Here are the list of operators we used
- [==] filter → matches values equal to
- [==] [c] filter case insensitive → matches values equal to ignore case
- IN {1,2,3} filter → matches value from a list of values.
- [BEGINSWITH] filter → matches if the firstName value starts with a.
- [CONTAINS] filter → matches if the firstName value conatins with li.
- Predicate with Passport object we want to filter User that have passport number == ‘pass1’
- Predicate with Todos object we want to filter User that have any tdodo contains details == ‘Need ot create RxSwift blog’
For advanced queries It is highly recommended to see https://academy.realm.io/posts/nspredicate-cheatsheet/ this NSPredicate cheatsheet
Note: What you refer to as “transient (computed) properties”, Realm refers to as “ignored properties”. These are properties that are for the most part ignored by Realm, so they won’t be stored in the db file, can be mutated outside write transactions, etc.However, this also means that they don’t benefit from many of the capabilities of non-ignored properties, such as queries. (querying for Realm objects can only be done with non-computed, Realm-persisted properties)
As shown in Figure 14 we sorted results with the firstName
property on User model
Live results
Last topic on fetching from Realm section
Realm result sets always return the latest up-to-date data. Data in Results is never outdated. This means you never have to reload results from disk or somehow manually refresh in- memory data.
As shown in Figure 15 we got the new user and we didn’t fetch using realm.objects
method instead newly added object are presented on users variable . If you come from Core data background you need to again fetched object from the stack
Modify Object On Realm
As shown in figure 16 we are modifying primary key and we get “Primary key can’t be changed after an object is inserted.” exception since we can’t update primary key . From Realm Docs primary key is added to a Realm, the primary key cannot be changed. Workaround → Remove and reinsert the object or see this question in stackoverflow
As shown in Figure 17 you can’t modify object fetched from realm outside of write transaction block if you try to modify outside of this you will get exception “‘Attempting to modify object outside of a write transaction — call beginWriteTransaction on an RLMRealm instance first.”
As shown in Figure 18 as we modify object in write
transaction block it will persist that object into the disk as well as shown in Figure 19 default.realm
file now has a updated firstName
value of User
Last example is very interesting we performed following tasks to update user with the newUser
- We get the User with its primary key
userId
which is 1 - Created new
User
with the same primary key - On a write transaction we call add batch update method with update = true it will update the User having primary key = 1 . If update = false it will throw an exception since two objects can’t have same primary key as shown in Figure 20 and 21
Delete Object From Realm
Any objects currently linking to the deleted ones will set their linking property to nil. If those objects are linked from any List properties, they’re removed from the lists in question.
As shown in Figure 22 and 23 we deleted User object having firstName == ‘ali new User’
As shown in Figure 24 we empty Realm database using deleteAll()
method in realm object. As shown in Figure realm.isEmpty
returns true
Sometimes you need to build some kind of hierarchy between Realm objects, just like you’re used to doing with Swift classes. Unfortunately, Realm does not currently support object inheritance out of the box . WorkAround: https://forum.realm.io/t/inheritance-with-realm-confusion/153
Useful Links
https://www.raywenderlich.com/9220-realm-tutorial-getting-started
https://www.appcoda.com/realm-database-swift/
https://realm.io/docs/swift/latest/#to-many-relationships
https://realm.io/docs/swift/latest/#optional-properties