|Keeping an app up-to-date with the steady flow of new features released by mobile device companies is a daunting task. Read about the efforts of the mobile team at eHarmony to clean up and redesign our mobile apps.|
Many of you old-timers out there have been through this on desktop applications, server apps, and old mainframe code. To mobile developers, this is still a relatively new thing, as the mobile development world is so young. But with the breakneck speed of new features and deprecations from Apple, Google, etc., it’s rapidly becoming an issue for everyone, mobile developers included. This is a large topic, so we are going to focus on iOS for this go-around. This is an exceptionally interesting start, since Apple is notorious for changing the way things are done, with its almost annual new OS releases. For example, iOS 5 introduced a new memory management model, usurping the decades-old practice of retain count memory management. iOS 6 brought Auto Layout, which significantly changed the UI layout system. iOS 7 changed the way the UI looks, which resulted in a huge amount of effort for developers, scrambling to update their apps for the new look and feel, so they didn’t look instantly dated. The iOS 8 adaptive UI brings new ways to make your UI look good, regardless of device or screen size. This list doesn’t even address myriad smaller changes.
A Little History…
eHarmony’s code base began in 2010, early on in iOS history. So, back when it was initially designed, many of the cool technologies and tools we enjoy using daily did not exist. There were no storyboards, no easy-to-use animation tools, no custom view transitions, no Auto Layout … Yet the team needed to provide as much as eye-candy as possible, to meet mobile customer expectations. Using the few tools they had on hand, the team managed to replicate some fun animations with a lot of hard work, low-level coding, slight-of-hand, and hackery. While many of these techniques have held up well over the years, Apple’s changes in the OS have made some of those code trickeries more risky. In some cases, due to OS evolution, those hacks have created very fragile code fragments, and inevitable instabilities. In addition, functionality that did not exist at the time predicated the need for 3rd-party libraries (some of questionable quality). Since then, Apple has provided more built-in tools, allowing us to clear out the code and use built-in, Apple-blessed functionality instead. Finally, many, many, many hands have touched the code over the years. Some of the original intentions or caveats have been forgotten, and snippets of code may be used in ways that were never intended, increasing risk.
With iOS 8 arriving soon, and the need to add yet more functionality and eye-candy, it became clear the team needed to take a good look at our code, and start the process of cleaning things up.
So, how did we at eHarmony decide to tackle this?
A Pragmatic Approach…
Below, I am going to summarize some of the approaches we took. In future installments, we’ll look at some of these more closely, along with discussion of the new features we’ll be adding. For example, in our next article, we’ll talk about adding Touch ID support to the eHarmony and Compatible Partners mobile apps.
First, we took a look at our build system. We were using Xcode along with a combination of other tools such as Raven (a Maven derivative) and Jenkins, along with TestFlight. We used GitHub as our repository, with GitHub submodules for some 3rd-party libraries. It soon became unwieldy and error-prone, and it felt like every few builds had glitches that caused erroneous builds.
With Apple beefing up Xcode over the past few iterations, and its pending release of Xcode 6, it seemed like a good idea to maximize our usage of the tools Apple provided, leveraging whatever benefit we could from them. Also, since CocoaPods is now the de facto standard in 3rd-party module management, it made sense for us to look there as well. This allowed us to divorce ourselves from tools like Raven, and standardize more on well-known tools.
Next, we used tools Apple provides to scrub our code. Xcode offers an ARC migration tool, as well as an Objective-C modernization process. These tools are largely automated, and gave us an excellent first step looking at our aging code base. The modernization process proved very interesting. Not only did it clean up a lot of code by using the modern array/dictionary accessors, and new literals, it revealed some insight into non-obvious practices from earlier years that had been the cause of some of our issues. An example is how arrays were once described. A NSArray could be initialized with objects, and use a nil sentinel to indicate the end of the object list. An earlier-used trick was that an object that evaluated to nil could signal the end of the list, so there would be no need to check if the object was nil ahead of time. (NSArrays cannot store nils). The problem, of course, is that this is non-obvious, and a subsequent programmer may not realize this is what was going on. With modernization, a new syntax that does not require a nil sentinel is used. Random crashes quickly showed us where this programming practice was used, and gave us an opportunity to clean it up.
For our next step, we updated our project. Some of this happened automatically (Xcode offers some “update settings” steps on its own). Others, we needed to set up on our own. We went through the “warnings” settings and discovered that some programmer in the past had turned many of them off (probably finding them bothersome). As we know, warnings serve a purpose, highlighting potential problems, and should not be ignored. So, we went through the list of warnings (turning many of them back on), and addressed the issues they indicated. Similarly, we turned on the Analyzer by default for development builds, so that those issues would also be raised for attention. These simple steps allowed us to see many issues that had been lurking deep in our code, and gave us an opportunity to address them.
Clearing Out the Old
Afterwards, we went through the code base and removed old, dead code. Categories, convenience methods, etc. were created years ago, before similar functionality was usurped by the OS. Unfortunately, when these were no longer used, they were not removed. Just on this first pass, we were able to remove over 15% of the lines of code, simply because they were no longer being used. Obviously, code that isn’t there is code you don’t have to maintain.
Finally, we ran this code base through Instruments and started profiling our app. Again, there were areas where it quickly became apparent we needed to spend some time focusing on the code. Memory leaks, performance issues, all quickly became obvious, and old bugs and crashes were suddenly understood. I cannot stress enough the importance of using Instruments as part of your code-writing process. It highlights problem areas, and helps to locate issues that even an experienced QA group may have trouble locating.
A Long Way to Go…
There’s a long way to go, but just these few steps have already greatly improved our code and highlighted areas where we can make improvements. Technical debt can quickly become a cancer in your code base if it is ignored. While not every point raised in this article may apply to you, taking the time to run through the applicable ones will yield tremendous rewards.
This is still an ongoing process, and we will continue the work in the coming weeks. We need to introduce Auto Layout to our UI, then adopt Adaptive UI, as we look to create a universal app for the iPad and whatever new size of phone Apple releases. We’ll continue this series of articles as we move closer to iOS 8, elaborating on the steps described here and discussing future steps.