CRUD Operation Using RealmSwift database Part 1

 In this part, we will Cover:

  1. What is Realm
  2. RealmSwift
  3. Creating a Complex Realm Model
  4. Why we use @objc and dynamic in Property
  5. To-One and To-Many Relationship in Realm
  6. How to store Custom enum in Realm
  7. What is RealmOptional and How to Store Swift Optional Atomic Property
  8. How Computed Property works as Transient Property In realm
  9. How to do indexing for performance gain
  10. What is Linking Object in Realm
  11. Complex Predicate Queries
  12. CRUD Operation
  13. What is Live Results in Realm
  14. 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.

Figure 1

User Model

As shown in Figure 2 we created User model and adds a few properties:

  1. First we inherit User class from Object which makes it Realm 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 all Object class Realm are able to infer your schema. Every model you want to persist you have to inherit from Object class.
  2. firstName stores the user first name as String. Since RealmSwift 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 hood
  3. userId 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 make userId primary key by overriding primarykey static function. The default implementation of this method return nil. Note userId acts as a Objective C primitive Int type since it is a wrapper around Objective C Realm
  4. passport 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 of Passport object will store
  5. privateUserType is the wrapper property of UserType 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 as Int 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.
  6. 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 haveAs shown in Figure 2 isEmailSubscriptionEnable we declare as RealmOptional 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.
  7. 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
  8. isUserHasTodos is a computed swift property and will not store in realm database which return if user has some tasks to do or not.
  9. Finally we make userId and firstName as indexed properties. By overriding indexedProperties 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
Figure 2

Passport Model

As shown in Figure 3 we created Passport model with the few properties

  1. passportNumber stores the Passport information and expiryDate stores the expiry date of the passport we want these properties to store that’s why we use @objc with dynamic keyword to tell realm do your under the hood magic
  2. We created passport property on User model which means User object have their passport 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 created ofUser property in Passport that have a reference of all User objects that assign Passport object in its passport property. In Core Data its called it as inverse relationship. Its a dynamic collection telling you who links to the current object.
Figure 3

As shown in Figure 4 we created Todo Realm model with has a backlink to the all the users pointing to particular task .

Figure 4

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

Figure 5

As shown in Figure 6 we performed number of tasks

  1. We get the instance of Realm database
  2. Created Passport and three tasks as a todos
  3. Created User object and assign passport and todos to it. In addition to this initialized Realm optional property which is isEmailSubscriptionEnable and Usertype 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

Figure 6

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

  1. We added User to your Realm, and since it references passport and todos, these objects are also added to the Realm.
  2. 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
  3. 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
Figure 7

Check Object Physically Store

By running this command on debugger output you get the address of realm file where your data is stored

Figure 8

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.

Figure 9

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.

Figure 10

Fetch Object from Realm

The process of fetching all the User records from Realm has following tasks

  1. 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 call init() without parameter
  2. 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
  3. Printed object on console to validate data is there and Linking objects working fine
Figure 11

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.

Figure 12

As shown we filtered using some Predicate Here are the list of operators we used

  1. [==] filter → matches values equal to
  2. [==] [c] filter case insensitive → matches values equal to ignore case
  3. IN {1,2,3} filter → matches value from a list of values.
  4. [BEGINSWITH] filter → matches if the firstName value starts with a.
  5. [CONTAINS] filter → matches if the firstName value conatins with li.
  6. Predicate with Passport object we want to filter User that have passport number == ‘pass1’
  7. 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)

Figure 13

As shown in Figure 14 we sorted results with the firstName property on User model

Figure 14

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

Reading and writing can happen in different spots in the project, on different threads, from different processes, or, when using the Realm Platform, from anywhere in the world. Each class in the app can focus on the business logic and forget about the notion of outdated or cached data, since Realm objects are always up to date.

Figure 15

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

Figure 16

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.”

Figure 17

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

Figure 18
Figure 19

Last example is very interesting we performed following tasks to update user with the newUser

  1. We get the User with its primary key userId which is 1
  2. Created new User with the same primary key
  3. 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
Figure 20
Figure 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’

Figure 22
Figure 23

As shown in Figure 24 we empty Realm database using deleteAll() method in realm object. As shown in Figure realm.isEmpty returns true

Figure 24

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

https://academy.realm.io/posts/nspredicate-cheatsheet/

https://academy.realm.io/posts/nspredicate-cheatsheet/

Comments

Popular posts from this blog

NSPredicate Cheatsheet, Basic, compound, Aggregate, String, comparison operators

what is appDelegate?