App Migration to Swift 3

by iOS Guild, Helsinki - 6 Jan 2017

We are a team of 10 iOS developers located in Zalando’s Helsinki hub, working on a Zalando iOS application called fleek: a fashion e-commerce app that connects mobile-savvy consumers with brands, retailers, and influencers. To ensure our app was ready for the latest release of Xcode 8/iOS 10, we started to plan our code migration to the new version of Swift. We wanted to share our experience and tips for other teams needing to migrate their code.

Plans and preparation

Our plan was to make sure all of our dependencies were ready for Swift 3 and upgrade one of our key dependencies, the internal Zalando Payment SDK, to Swift 3. After all dependencies were ready, we had to think of a way to migrate every single file without having merge conflicts and without repeating the same work. This could prove difficult as we are a big team.

The aim was to get this done as quickly as possible to avoid delays in feature delivery, so everyone on the team was needed. Since our project has more than 450 files, the problem we faced was how to divide up the work. After a quick brainstorming session, we asked our DevOps and command line guru to label all the source code files based on the last commit a developer had made. He came up with this:

find . -type f -name "*swift" -exec git log -n 1 --pretty=format:"%an ," {} \; -exec echo {} \; | tee ~/Desktop/result.csv

The results of this file served as a great starting point for us to take on the challenge.

Getting to work

We started by creating a swift-3 branch for the migration, with one person running the Swift 3 migration tool. Once this was completed, we updated our podfile with all Swift 3 dependencies and pushed to Git.

Using the document we previously created, we shared it and marked which files were under conversion or ready to go. When someone had completed all of their allocated files, they would talk to the rest of the team and decide where to help out. As more of the project compiled successfully, some of the already converted files had to be revisited; team members would simply mark the files being worked on again in the file list.

When all files were converted and the app would compile again, we used the same document to mark who was fixing crashes and in which files. Soon we were using more of our time for testing and less for fixing crashes or addressing missing functionality.

Here are a couple of the issues we came across:

  • AnyObject to Any. Many APIs changed from AnyObject to Any, which need to be revised by hand along with checking the documents.
  • Closures were being typecasted, for example:
    { (text: String) -> [String: AnyObject] in
    return [text: randomObject]
    }

    Was converted to:
    { (text: String) -> [String: AnyObject] in
    return [text: randomObject]
    } as! [String: Any]

    This generated app crashes.
  • Code inside closures was almost never converted
  • private was changed to fileprivate incorrectly
  • Tons of foundation types didn’t migrate at all, for example: NSUrl and NSMutableURLRequest
  • Enum cases were migrated only in one file
  • Working with String characters had to be reviewed. The API had changed a lot

Before you start manual migration to Swift 3, we recommend the following steps:

  • Do search-and-replace fixes. For example: UIControlState() to .normal, _ map: map to map: map
  • Activate the “continue build after errors” Xcode setting

While migrating, we recommend that you try to avoid fixing bugs and refactoring, as you can come back to this task later. You’ll also need to remember to migrate your CI system.

Ready to go

After almost 2 weeks of non-stop code conversion, our code finally started to compile and run. By the time we had finished, we had experienced a huge learning curve with Swift 3 and the new Apple APIs.

We’re happy to share our experiences with teams needing to migrate their own apps. Hopefully the above information will prove useful if your team is facing the same task.

Similar blog posts