Refactoring Complex iOS Apps

Pavle Pesic
codeburst
Published in
5 min readNov 19, 2020

--

Photo by Marek Levák on Unsplash

Refactoring an unfamiliar working system is both risky and complicated. Add to this the need to simultaneously implement new features and persuade management to allocate time for the job to the equation; the task may seem impossible! However, if you create a good plan, this will be far easier than it may seem.

Please note: I won’t write about refactoring examples. Instead, I will leave that for another post. The main goal of this article is to show you how to begin the process.

Our starting point

The app that my team inherited didn’t have production issues. With 1.1M users, it was 99.9% crash-free. However, the codebase was a real mess. There was no architecture, the app had over 600 warnings, dependencies were are all over the place, unneeded code was everywhere, some classes were too big to maintain, and there was very little documentation. Suffice it to say that there were many things to improve.

The first set of changes

To refactor a big app, you need a good strategy. Because we were unfamiliar with the project, we wanted to get to know the system better; find its weak spots, and perform small fixes.

Firstly, we decided what kind of architecture we wanted (MVVM) and introduced a coding style — these are conventions on how to structure and mark the code, create and inject dependencies, name classes, methods, and variables, and which software patterns to use. All new files in the app had to follow these, and every time we were changing the existing code (there are some exceptions, of course), it had to conform to the rules. The goal was to leave the code better than we found it.

The second thing we agreed to do was warning removal. If you want to refactor a project, warnings are your allies. They will give you appropriate guidelines about poorly written and error-prone code, and using a linter through all of this will enforce the coding style. We had over 600 warnings, so if we were to introduce a new one, there was a significant probability that it would go unnoticed. Another benefit of warning removal was that we got to know the project very well. Because warnings were all over the place, we learned a lot about the app and its weak points.

Please note: The project had reliable test coverage. We used those and added new ones if needed to ensure that each refactoring goes well. We didn’t have any special plans for tests, so we haven’t included them in the big picture.

Weak points

After locating all of the weak points within our app, we’ve prioritized them based on multiple metrics: refactor time, impact on robustness, and how much it would benefit further development. High priority problems were those regarding architecture, essential features that were hard to maintain, and things that waste our time. If we want to create a modular, easy-to-upgrade system, then we have to tackle those issues first.

High priority:

  • Creating a new dependency container: the current one was full of static variables, and anyone could access it anytime. Another problem was that not all dependencies were there; many of those missing ones were singletons. A good dependency container is a must-have if you want modularity. Creating it is a good investment because adding and reusing features will become much more straightforward to do in the future.
  • Rewriting big classes: some classes had over 2000 lines, or the logic was too complicated to maintain. It’s far more comfortable and faster to rewrite those classes instead of refactoring. Big classes are ticking time bombs. And those big classes were some of the essential features.
  • Fixing logs in the app: Logs were unreadable as they were so cluttered and bloated. There was no way to filter them, so critical errors in runtime would go unnoticed during development. Fixing those will significantly improve the debugging of the app.
  • Fixing build time: build time was about five minutes long after cleaning the project. Fixing it would save us much time and would keep our focus.

Lower priority:

  • Fixing debug symbols: When the app hits a breakpoint, it had a delay of about 30 seconds before showing us the logs and a stack-trace.
  • Removing dead code: We used the script to find hanging legacy code and unused features. We iterated over it a few times, which made our codebase significantly smaller.
  • Too many third party dependencies: There were many pods we didn’t need in the app. Removing those improved build time and reduced the complexity of the app.
  • Introducing coordinators: Coordinators have many benefits. You can read more about them here.

Persuading management to allocate time

We thought this would be the hardest thing to do. However, everything went surprisingly smoothly.

The team created a two-page document where we described all weak points and things we’ve already changed. We explained the current and long-term harmful effects and the time needed to implement changes for each of the weak points. An important note here is the document’s length. Because it was short and simple, we knew that the management would read it.

Management was surprised about some things, especially the build time issue. They understood that refactoring was an investment, not a waste of time. On the same day, they gave us the green light to create Jira tasks for refactorings (both the lower and higher priority) and permission to create new ones for similar improvements if we found it necessary. Now at least one of the iOS developers is working on one of those problems each week.

Conclusion

When you want to refactor big projects, you need to have a strategy; the last thing you want is to break a system in production — so don’t rush things. Every app is a different story, take the time to get to know the project and its weak spots; only then you can start creating the strategy. We needed three months to develop the plan, document it, and present it to the management. The document needs to be short with clear reasoning as to why refactoring is required, how much time it will consume, and how much time it will save in the long run.

The last thing is not to accept no for an answer. You are going to work on the project. If you don’t take care of it, the app will become a behemoth sooner than you think. It will be tough to maintain and cause many frustrations and time waste. We spend at least 40 hours a week working, so we should make that time more enjoyable. Refactoring is the solution!

--

--