Optimizing App Launch & detecting performance issues using Instruments

Alan Steiman
May 29, 2020 · 4 min read

A few years back I joined the top 1 online classifieds company in the world. One of the first tasks I was given, was to reduce the app launch below the 2 seconds mark. The users were leaving comments on the App Store because the app was “slow”.

The app, as you can imagine, had a mix of Swift and legacy Objective-C code, 4000+ files distributed in 8 repositories (main app and 7 internal libraries). In addition to this, around 30+ third-party libraries, everything linked using Cocoapods.

Photo by Veri Ivanova on Unsplash

pre-main vs. post-main

There are many articles (1, 2, 3) already talking about what to do with the pre-main, usually is related to convert the dynamic libraries into static (libs will be linked at compilation time instead of runtime), or at least reduce the amount of dynamic libs.

But when I did the profiling, although there was some room for improvement in the pre-main section, the slowest part was the post-main (starting when didFinishLaunchingWithOptions is called)

I don’t need to mention that several third-party libraries were initialized, internal services were set up, network calls were being made… it wasn’t an easy task.

Spoiler alert!

Can you spot the issue?

Using Instruments

Using the Time Profiler, the heaviest stack revealed that had something to do with storing stuff in Realm.

Asking my colleagues, I discovered we were using Realm to persist the complete category tree (5000+ rows, because car make and models were included). You can find comparison between Realm and CoreData here, here and here.

On the first install, and every time there was an update on the tree (more often than you would think), the full tree was downloaded from the server and stored using the code shared above. In case of failure to download, a JSON containing the latest tree embedded in the app was used as a fallback.

Write Transaction

But also the documentation states:

Since write transactions incur non-negligible overhead, you should architect your code to minimize the number of write transactions.

By looking at the first code shared, it’s obvious now that there is a write transaction for every category. Just imagine the overhead of having 5000+ database transactions queued while the user is watching clueless the loading screen.

Refactoring the code resulted in:

This simple change of moving the second realm.write outside the forEach, improved the efficiency by 90%

But wait, there’s more!

In order to avoid inconsistency on the database, both delete and write operations have to be grouped within the same transaction, and let Realm handle the rollback mechanism in case of error.

The code was refactored one more time as follows:

Realm was used to store Categories only, therefore using the deleteAll() command didn’t present any immediate risk. However would require further refactoring to ensure deleting only the desired objects and not flushing the entire database by mistake, like so:

Conclusion

We can always rely on tools like Instruments to analyze the app from different angles, time profiling, memory allocations, network usage, etc.

However I believe logic errors like the one described above should be caught during local testing, or during code review, and it’s important to understand how it managed to get merged into the main branch, in order to avoid similar scenarios.

Flawless iOS

🍏 Community around iOS development, mobile design, and…

Medium is an open platform where 170 million readers come to find insightful and dynamic thinking. Here, expert and undiscovered voices alike dive into the heart of any topic and bring new ideas to the surface. Learn more

Follow the writers, publications, and topics that matter to you, and you’ll see them on your homepage and in your inbox. Explore

If you have a story to tell, knowledge to share, or a perspective to offer — welcome home. It’s easy and free to post your thinking on any topic. Write on Medium

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store