Building Reactive Apps with Realm: Episode 4

In this five-episode series, we’re going to have a look at how Realm facilitates building reactive applications from the ground up. In the fourth episode, we’ll use Inboxly’s sample project again to learn about elasticity, and how Realm can help keep our memory consumption optimized.

You can find the other episodes here:


 

Introduction (0:00)

Hi, and welcome back to Building Reactive Apps with Realm. As we’ve seen in previous episodes, Realm facilitates writing high-quality reactive apps regardless of whether you are using a reactive code framework or not.

Previously, we spoke about how Realm allows you to build more responsive and resilient apps. This time, we’re going to have a look at elasticity.

Elasticity (0:28)

If we consider the Inboxly project that we’ve been poking around with in this video series, you might want to show tens or even hundreds of messages in your feed table during development and testing. But in real life, when users use your app for a long time, the feed table view might end up having to display 10,000 or 20,000 or more messages that are stored on your disk in your Realm.

Imagine for a moment that you’re storing your data in other types of storage. You’re using structs to load data from disk into memory and power your table view. That means that, effectively, you will read all the tens of thousands of messages from disk in one go, copy that data into structs and store them in an array to show four or five table rows on screen at a time.

This doesn’t look very efficient, does it? Realm empowers you in your app by having a zero copy policy. Meaning that no data is loaded from the disk if you aren’t actively using it right now. Therefore, Realm’s results type might contain hundreds, 1,000 or 20,000 objects, but it will read exactly the same amount of data from the disk into memory to display the five table rows that the user sees on the screen.

Let’s have a quick look at a demo to see what all of this means for you.

Demo (2:05)

Let’s quickly have a look at the inner workings of Inboxly, the project that we’ve been poking around with for a while now in this series. We’ve already looked at this fetch method which runs repeatedly and calls the simulated API object, and then gives back a number of JSON objects that are being merged into Realm:

@objc private func fetch(timer: NSTimer) {
    dispatch_async(dispatch_get_global_queue(QOS_CLASS_BACKGROUND, 0), {[weak self] in

        self?.api.getMessages {jsonObjects in

        let realm = try! Realm(configuration: RealmConfig.Main.configuration)
        let me = realm.objects(User).first!

        try! realm.write {
            for object in jsonObjects {

                let url = object["image"] as! String
                let photo: Photo = realm.objectForPrimaryKey(Photo.self, key: url) ?? Photo(value: ["url": url])

                let message = Message(value: object)
                message.photos.append(photo)
                me.messages.insert(message, atIndex: 0)
            }
        }
        }
    })
}

We have played with this a few times, but we have never seen how getMessages work.

getMessages creates an array of objects that simulates the response from the server:

private func randomMessages() -> [[String: AnyObject]] {

    var result = [[String: AnyObject]]()
    for _ in 0...(arc4random_uniform(3)) {
        let name = users[Int(arc4random_uniform(UInt32(users.count)))]

        result.append([
            "id": NSUUID().UUIDString,
            "message": phrases[Int(arc4random_uniform(UInt32(phrases.count)))],
            "name": name,
            "timestamp": NSDate().timeIntervalSinceReferenceDate,
            "image": imageUrlForName(name)
        ])
    }

    return result
}

}

It creates a few messages and returns them back to the data controller. Those are anywhere between one and three.

Let’s have a look at Instruments and profile this application to have a look at how much memory it consumes while it shows a few hundred messages. We’re going to open the Allocations profile to make sure that we look at the total memory consumption of the app. It should gravitate around the same amount of memory, about 25, 26MiB.

Instead of featuring a few hundred, I’m going to import every time, not one, two or three objects. I’m going to import 10,001, 10,002 or 10,003 objects every single time. To do that, we just change the following line, in our randomMessages method:

...    
for _ in 0...(arc4random_uniform(3) + 1000) {
...

Allocations / Conclusion (4:53)

Now, let’s profile again with Instruments -> Allocations. Once we do that, you should see huge leaps in the allocated memory to ~72MiB, and then it falls back to ~29MiB. These peaks are where I’m creating all the structs and showing them in an array that contains my JSON objects. As soon as I write them through Realm, then the memory is freed up, and it goes down to ~29MiB.

All the memory peaks are where I’m importing the JSON into an array of structs, and passing it to my data controller to write to Realm. All the plateaus are where the app is using Realm to display the table view feed. The plateaus are around 29 megabytes, so there is minimal change in the consumed memory between showing a couple of hundred messages and showing the table view of the messages feed when there are tens of thousands of messages stored in Realm.

That is the beauty of it, because if you were reading structs from your data storage, now the memory peak would not go down because all of your structs would stay in memory. It wouldn’t go down; it would build up until we have so many messages stored on disk that it will not fit in memory.

What Realm does is use classes and added properties, which allows you to keep in memory only the objects that you’re working with and displaying at the time on the screen.

Resources


Marin Todorov

Marin Todorov

Marin Todorov is an independent iOS consultant and publisher. He’s co-author on the book "RxSwift: Reactive programming with Swift" the author of “iOS Animations by Tutorials”. He's part of Realm and raywenderlich.com. Besides crafting code, Marin also enjoys blogging, writing books, teaching, and speaking. He sometimes open sources his code. He walked the way to Santiago.