Swift 3 migration for IoS app

When Apple released the newest version of its IDE Xcode 8 in June 2016, they released a beta update of Swift 3 along with it—the newest, open-source iteration of the Swift mobile programming language.

About Swift 3-

Swift 3

If you are new to Swift, just have a look to Swift vs Objective C. Apple developed Swift. It is a newer, streamlined language to better align with the platform’s evolving hardware and software. Swift allows rapid development by allowing developers to be more productive. This requires less code and also has better readability and is prone to fewer errors than its predecessors. Swift represents the direction that iOS has been headed for a while. If you haven’t migrated over yet, the Swift 3 iteration is a vital point and also a great opportunity to roll out the improvement.

Migrating to Swift 3-

How critical is to migrate existing code to Swift 3? Apple underlines that it is the right time to get on board—especially if you need to exploit all the new features in Xcode 8. This is the version that most closely aligns with the future of iOS development – migrating code from previous versions now will help you future-proof your app. Xcode 8 has a Swift Migration Assistant and a quick pre-migration checklist. Make a note that migrating to Swift 3.0 is a quite difficult. Expect that the migration tool will require some work to fix compiler errors after migration. It is not completely error-free but Apple has compiled a list of some of the known migration errors.

Apple suggests: “If you need to apply any workarounds, discard the changes that you accepted from the migration assistant earlier, apply the workarounds, and invoke the assistant manually to re-try the conversion from the start.”  

An example of something you can be proactive about changing is type names that slam with renamed Foundation types. We had a class called Operation which post-migration conflicted with NSOperation being renamed Operation. Note that you can run the migration assistant multiple times without discarding the prior-run’s changes. 

The clunkiness and also slowness of the migration assistant created a feedback loop that was too long. An example of undesirable migration assistant behavior is generated operator overloads to account for optional comparison changes in Swift 3. Here’s an example of a method that was added to the top of one of our class files:

 fileprivate func < <T : Comparable>(lhs: T?, rhs: T?) -> Bool {

  switch (lhs, rhs) {

  case let (l?, r?):

    return l < r

  case (nil, _?):

    return true

  default:

    return false

  }

}

One thing the migration assistant does very well is handle changing how we call system APIs in Swift 3 style. For example:

N SNSNotificationCenter.defaultCenter()    Became:           NotificationCenter.default       

Manual Migration-

In migration guide, Apple says “While the migrator will take care of many mechanical changes for you, it is likely that you will need to make more manual changes to be able to build the project after applying the migrator changes.” At this point in the process you have to do many more manual changes. In your note-taking exercise you have to try to identify regular patterns of issues, and also sometimes you have to make regular expressions to perform a project-wide search for code matching those patterns. This  will allow you to take large steps towards reducing compile errors as opposed to fixing individual issues and trying to recompile.

Feedback Loops-

Large mixed Swift/Objective-C projects compile particularly slowly. This long feedback cycles, and you have to make larger changes between compile attempts to offset the slow compile times when we’d usually prefer to have smaller iterations. This is basically the same challenge that kept us from needing to run the migration assistant multiple times.

private -> fileprivate-

The migration assistant changed private methods and variables to file private which is functionally similar. But what we really needed semantically, when you wrote those declarations is the equivalent of the Swift 3 private access modifier. 

Underscores in Method Signatures Everywhere-

Because Swift 3 style often prefers to not label the first method parameter, the migration assistant changed methods to suppress the first parameter label. This was an improvement at the call site over patterns:

doSomethingWithModel(model: someModel)

Which became less redundant:

    doSomethingWithModel(someModel) 

There were, however, instances where we did not prefer the label-free conversion where the Swift API Design Guidelines suggest a first parameter label. The above example might be better as:           

doSomething(with: someModel)                                                                                                                                                                 

Optional Handling-

Optional handling from Objective-C APIs to Swift was improved, so there are some cases where the migration assistant added code to explicitly unwrap instances that previously didn’t require the ! operator. This uncovered some places where we needed to perform proper optional handling, so we refactored accordingly. There were other cases where the migrator choose to force-unwrap expressions that could not assess to optional. In those cases, removed the ! operator. We wanted to add nullability tags to related Objective-C headers, and then if that wasn’t viable, we put in explicit optional handling on the Swift side.

Objective- C Categories-

Objective- C categories were were never again certainly available to Swift, and we regularly needed to cast back to an Objective-C type to access those category methods.

Trailing Closure Syntax-

While not a compile issue, emphatically prefer trailing closure syntax for methods that take one closure, and the migration assistant is not able to generate code that way. We changed code that migrated with closure parameters:

    DispatchQueue.main.async(execute: {

            // Do something })

To use trailing closure syntax:

     DispatchQueue.main.async {

           //Do something }

AnyObject -> Any

NSDictionarys from Objective-C used to import to Swift as [NSObject, AnyObject] and now are imported as [AnyHashable, Any]. This impact the most in our JSON parsing utility where we need to change to use Any instead of AnyObject.

Open Classes-

Some of the classes were marked with the open keyword which implies that they are subclassable outside of the module they are defined in. Most of the time these class changed to be final because no intention allowing those classes to be extended or be accessible outside the module.

@escaping-

closures passed as parameters are now non-escaping by default, so basically meaning that you can’t hold onto them after the method terminates, we had to add @escaping to closure parameters in lots of places.

Collection Indices-

Instead of calling  successor() on an Index, we needed to call index(after:) on the related collection due to a change in how collection indices work in Swift 3. Collection extension helper class includes this. The migrator left us with this: 

<#T##Collection corresponding to `i`##Collection#>.index(after: i)

Which we changed to:

self.index(after: i)

Conclusion-

This has been a story about dealing with the most frightening of Swift migrations we hope it helps you in your effort.

Are you looking to develop your business software with Swift 3? Solace is able to give you the best software solution through their experts. Contact us for any swift 3 development that will give you the success that your business deserves.