How Hard Could It Be to Replace 22,000 Lines of Code?
When Apple introduced Swift, it promised a modern, strongly-typed, easy-to-read, safer and more secure language for iOS development. Emboldened by the promise of interoperability, we were excited to adopt Swift. So… how hard could it be to replace 22,000 lines of code written in Objective-C? We were about to find out.
Most developers have heard the rule of thumb that at some point, the programming language becomes less important than the algorithm. That is, it’s best to limit religious debates about which language to use and focus instead on execution. I wholeheartedly agree, but sometimes you need to step back and evaluate whether the language you’re using is the right fit for the task. I doubt anyone relishes the idea of writing a mobile app in Assembly!
My team develops our iOS app for the xMatters product, and when Apple introduced Swift back in June 2014, most of us were excited to adopt it. That November, a couple of months after Swift was officially supported for iOS development, we created our first Swift class.
How hard could it be to replace 22,000 lines of code?
Adopting Swift was more than just chasing a shiny new object: it had been more than a year since we first released the xMatters native iOS app. From late 2012 to February 2013 when we released v2.0 of the app, the team coded in Objective-C. However, the team never really loved that verbose-message-sending, weak-typed language.
In contrast, Swift promised a modern, strongly-typed, easy-to-read, safer and more secure language. Emboldened by the promise of interoperability, we marched forward with Swift. I mean, how hard could it be to replace 22,000 lines of Objective-C?
Pretty hard, it turns out
Fast forward about three years to November 2017, and the xMatters iOS app was less than 17% Swift: about 8,000 lines of code in the new language and – due to added functionality – 41,000 lines of Objective-C, excluding headers.
Interoperability was indeed possible, but the code change resulted in increased difficulty when debugging new class compilation issues – and the code took a turn for the worse in readability. Writing and reading blocks between the languages was a pain and it sometimes felt like we were getting the worst of both worlds.
At about this time, Apple started writing its iOS development documentation using Swift examples, and many popular third-party libraries were also written in Swift. Around the same time, we onboarded a new member to the iOS team who was very technical and skilled in iOS development but was unfamiliar with our code base. We took this as a sign, and accelerated transition to Swift (so to speak).
Welcome, and good luck!
We gave our new team member the task of migrating as much of our code base to Swift as possible during his first few months on the team. This might sound like hazing, but the idea was that he could learn our code base while reducing the burden of maintaining two languages. After some research, he proposed to migrate it in a very short timeframe in a huge focused effort.
He made his first commit toward a 100% Swift code base in January 2018, and a month later we had totally converted the code. Many things had to align for this to happen so quickly: the product owner’s trust in the team’s ability to handle quality of software in such a large migration; the audacity and technical capability of our new member to carry out a 100% migration in only three months; and, the team’s willingness to contribute their extra hours to testing and fixing migration issues.
Let the machine do (some of) the work
The initial cut of the migration was done with an Objective-C-to-Swift converter tool called Swiftify running in offline mode. The project would not have been possible without this tool. The few times we had to reach out to Swiftify for help, its CEO was quick to reply with helpful and insightful answers.
We started – naïvely (!) – by running the conversion tool on the entire code base… which generated so many compilation errors that we had to break down the task into bite-size pieces. Lesson learned, our newbie doggedly divided and conquered the code base with 13 individual migrations, where each commit retained the ability for us to compile the project.
After those individual heroics came the team effort. Even though we could compile the app in the new Swift code base, we needed to ensure we didn’t break any functionality. Our initial round of functional testing generated 128 individual subtasks for the team to fix. Regular development had to continue, but the team took time away from their main tasks and worked together to solve the migration issues.
Here be gotchas
While the migration tool did migrate Objective-C code into Swift, in some cases the conversion wasn’t accurate. For those of you thinking of going down the same path, here are a few things to watch for:
- Optionals don’t translate well, as the conversion tool cannot determine our business logic. To deal with this, the team retraced the logic and fixed each instance of unwrapping.
- Converted code that tested empty Strings and Arrays was incorrect. To get around this issue, the team made use of
guard
andif let
- There was converted code that tested equality with
NSNull()
when it should have been tested fornil
- Clean up untyped conversion to
[AnyHashable: Any!]
for dictionaries - Clean up untyped elements for Arrays; for example,
[Any]() versus [String]()
- Fix conversions of
Calendar.component
toCalendar.dateComponents
Fortune favors the Swift
By March 2018, our investment in the 100% Swift migration started to pay off. With the new code base, our bug fixing became more efficient. Eventually we got to our first ‘zero’ bug count since we started coding the native iOS app. It was finally time for our first post-migration release of the app.
For us, the key was to stop trying to do a ‘piecemeal’ conversion when we had time, and instead to really concentrate on getting it done in a focused and efficient way. In the year since migration, we haven’t for a moment missed Objective-C’s umbrella file or interop blocks and their impact on our development timelines!