Converting Your Project to Swift 3.0

Swift 3 is the first update since Apple announced Swift would be going open-source, and is packed with new features based on community direction.

When converting to Swift 3, you’ll notice that practically every file needs changes! That’s largely because, all the Cocoa API names have changed. Or to be more precise, the API is the same, but there’s one name appropriate for Objective-C and one name appropriate for Swift. Swift 3 is going to make Swift much more natural to write in the years ahead.

Converting to Swift 3

Converting a project to Swift 3 🙂 (Source: ibtimes)

Apple has included a Migration Assistant with Xcode 8 that can brilliantly make most of these changes in one fell swoop. Don’t be surprised though if you need to touch up a few areas yourself that the migrator doesn’t handle automatically.

You can convert to either Swift 2.3 or Swift 3 immediately. If you ever need to bring it back up, you can always navigate in Xcode to Edit > Convert > To Current Swift Syntax…. The compiler fortunately shares the same smarts as the Migration Assistant as well. If you accidentally use the old API on a method call, the compiler will offer a Fix-It option that will help you use the correct modern API.

The best news of all is that Swift 3 aims to be the last release with breaking source changes. So looking forward, you should be able to keep your Swift code from version to version.

The major changes I found interesting

Objective-C lightweight generic classes are now imported as generic types:

  • If an Objective-C generic class is used in a checked as?as!, or is cast, the generic parameters are not checked at runtime. The cast succeeds if the operand is an instance of the Objective-C class, regardless of parameters.

  • Swift subclasses can only inherit an Objective-C generic class if its generic parameters are fully specified.

  • Foundation container classes NS[Mutable]ArrayNS[Mutable]Set, and NS[Mutable]Dictionary are still imported as nongeneric classes for the time being.
  • The NS prefix has been removed on old foundation types, you can now use CalendarDate instead of NSCalendar and NSDate.

The API has gotten smarter about how Objective-C libraries are transformed into native Swift:

  • Swift 2.x above, with Swift 3 in the second line.

New, more beautiful ways to import C functions:

  • Swift 3 introduces attributes for C functions that allow library authors to specify new and beautiful ways their code should be imported into Swift. For example, all those functions that start with “CGContext” now get mapped to properties methods on a CGContext object, which makes for a much more idiomatic Swift.
  • To demonstrate this, here’s an example in Swift 2.2:

  • In Swift 3 the CGContext can be treated as an object that you can call methods on, rather than repeating CGContext again and again. So, we can rewrite that code like this:

Consistent first argument labels:

  • The first argument label in functions is now considered API by default. The migrator adds underscore labels to preserve the existing APIs: 

  • The first parameter in functions and methods now always has a label unless you request otherwise. Previously when you called a function or method you omitted the first parameter label (Swift 2 above, Swift 3 under):

New format of enumeration cases:

  • In another reversal from the way you’ve been used to coding Swift, lowerCamelCase now replaces enumeration cases. This makes them more consistent with other properties – or values (Swift 2 above, Swift 3 under):

Introduction of Self:

  • When you define a static property or method, you have always called them on the type directly:

  • If you are writing code in the context of a type, you still need to include the name of the type to call a static method on the type. To make this a bit cleaner, you can now call Self to get the containing type. The capital ‘S’ refers to the type of self, whereas the lowercase ‘s’ refers to the instance of self.

For loops and ranges:

  • C-Style for loops are going away, so it’s definitely time to get used to for-in loops:

  • Change in range:

The Swift Package Manager:

  • Open source Swift is actually a family of repositories including the language, the core libraries, and the package manager. Together, this suite makes up what we think of as Swift. The Swift Package Manager defines a simple directory structure for any Swift code that you share and import into projects.
  • Similar to package managers you may be used to such as Cocoapods or Carthage, Swift’s package manager will download dependencies, compile them, and link them together to create libraries and executables. Swift 3 is the first release to include the Swift Package Manager. There are 1,000 libraries that already support it and in the coming months, you’ll start to see more formatted for it.

Collection indexing changes:

  • The Collection indexing model has changed dramatically in Swift 3. The most visible change is that indexes no longer have successor()predecessor()advancedBy(_:)advancedBy(_:limit:), ordistanceTo(_:) methods. Instead, those operations are moved to the collection, which is now responsible for incrementing and decrementing its indices.

Apple will give and take away:

  • As well as adding features to Swift 3.0, Apple is taking features away. Here are some removed features for Swift 3.0.
    • Currying func declaration syntax
    • var in function parameter lists
    • ++ and — operators
    • C-style for loop
    • Implicit tuple splat in calls

Using Carthage/CocoaPods Projects

  • If you are using binary Swift modules from other projects that are not built along with your project in your Xcode workspace, you can choose from one of the following migration strategies:
    • Include the source code of the project in your Xcode workspace

    • Wait until the upstream open-source project updates to Swift 2.3 or Swift 3

Bugs I encountered:

  • Some types are now generic (e.g. NSCache -> Cache<Key,Value>NSMapTable ->MapTable<Key,Value>). 
    • After migrating to Swift 3 you may need to add appropriate generic parameters for them.
  • The migrator is not handling C-style for loops that count down, e.g for var i = right; i > left; i– {}

    • Manually migrate the loop to a for-in loop or while loop.

Weird changes, conversion issues:

  • NSData(contentsOfMappedFile: x) can be changed to Data(contentsOf: x, options: .mappedAlways)
    NSData(data: x) can be changed to x (I personally think, this is a typo)
  • The migrator may incorrectly lower-case certain enum values such as objc_ASSOCIATION_RETAIN.
    Workaround: Change them back to uppercase 
    OBJC_ASSOCIATION_RETAIN.

  • The migrator may incorrectly rename the NSApplicationDelegate’s applicationShouldTerminateAfterLastWindowClosed(_:) method to applicationShouldTerminate(afterLastWindowClosed:), which should cause a warning that “Instance method ‘…’ nearly matches optional requirement ‘…’ of protocol NSApplicationDelegate“.
    Workaround: Change the name back to 
    applicationShouldTerminateAfterLastWindowClosed.

  • While migrating to Swift 3, if you called self.init(…), the migrator may incorrectly change self.init to self.dynamicType.init, which may cause you to see the error “return from initializer without initializing all stored properties”.
    Workaround
    The fix is to remove .dynamicType.

In practice

Fortunately Apple did a good job with the converter, so it saves us lots of work and time. All you need to do is follow this 9-step-long list:

  1. First of all, make sure you have a working project in Swift 2.3. The next step should be making a copy of this project, just to be sure you can always start from a working version.
  2. Search for the Swift 3 version of the pods/3rd party frameworks you are using. In the Podfile you have to specify your target, the terminal will throw an error otherwise. Sometimes after this error it can switch your branch to master, keep an eye on this. I personally like to delete current pods and Podfile.lock, and clean install the new version of them. Example Podfile:

As you can see, some pods have a full Swift 3 version update, while some have only updated branches, watch their progress and update if necessary.

3. Now open your project/workspace in XCode8, and click on Convert. On the first run, convert only pods/3rd party frameworks to current Swift syntax.

4. Save the changes the converter made, unlock the items and build your project. If you have errors, fix them, there should be a few at most.

5. Next, clean and build your project again, there should be errors only in your hand-written code.

6. Convert the rest of your project: the targets and tests (Edit -> Convert -> To Current Swift Syntax…)

7. If you still have errors Clean and Clean Build Folder again, convert your targets, just in case the converter overlooked something.

8. The converter will probably not change functions of pods/3rd party frameworks, so you will have to do some legwork, and update them manually.

9. The remaining errors need some researching, or thinking, for example sharedInstance:

but they can be fixed quite easily.

 

Some tips:

  • Make sure, you change your Deployment Target in your target settings to 10.0.
  • Now you have to set Provisioning Profiles in the General settings, not in the Build Settings.
  • If using the Assistant Editor, make sure you have the right files chosen (I tried to create a new Event in a ViewController, didn’t see the function being created in the ViewController, then I realized that the code opened by the Assistant Editor was only a copy, outside my project.)
    To solve this, click on Add Assistant Editor, choose Manual, then find the desired file.

Converting your project to Swift 3.0

Conclusion:

Try the new features of Swift, they are pretty good and fun to code in, but need a little routine after 2.X. If you’ve tried the beta version and hated it, give the new version a try (Apple has improved it enormously). For further reading, I recommend the official migration guide and this useful article.
All in all, if you want to just try the conversion, choose a small project with some 3rd party frameworks or pods to find out what can go wrong. If you have to convert a more complex project, take the time and make it as good as possible, it will save lot of future work hours.

Róbert Klacso

Róbert Klacso

My favorite language and coding style are the same, swift.

Latest posts by Róbert Klacso (see all)
  • Not sure why you must set deployment target to 10.0? Swift 3 should run fine even on 8.0.

    • Klacso Róbert

      You are right, you can set it to lower versions, I just tried it with the Xcode Beta, and found it more reliable with this setting.