post-photo

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 a project to Swift 3

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:

let x = NSFoo<NSNumber>(value: NSNumber(integer: 0))
let y: AnyObject = x
let z = y as! NSFoo<NSString> // Succeeds
// Error: Can't inherit Objective-C generic class with unbound parameter T
class SwiftFoo1<T>: NSFoo<T> { }
// OK: Can inherit Objective-C generic class with specific parameters
class SwiftFoo2<T>: NSFoo<NSString> { }

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

let blue = UIColor.blueColor()
let blue = UIColor.blue
attributedString.appendAttributedString(anotherString)
attributedString.append(anotherString)

New, more beautiful ways to import C functions:

let ctx = UIGraphicsGetCurrentContext()
 
let rectangle = CGRect(x: 0, y: 0, width: 512, height: 512)
CGContextSetFillColorWithColor(ctx, UIColor.redColor().CGColor)
CGContextSetStrokeColorWithColor(ctx, UIColor.blackColor().CGColor)
CGContextSetLineWidth(ctx, 10)
CGContextAddRect(ctx, rectangle)
CGContextDrawPath(ctx, .FillStroke)
  
UIGraphicsEndImageContext()
if let ctx = UIGraphicsGetCurrentContext() {
    let rectangle = CGRect(x: 0, y: 0, width: 512, height: 512)
    ctx.setFillColor(UIColor.red().cgColor)
    ctx.setStrokeColor(UIColor.black().cgColor)
    ctx.setLineWidth(10)
    ctx.addRect(rectangle)
    ctx.drawPath(using: .fillStroke)
 
    UIGraphicsEndImageContext()
}

Consistent first argument labels:

func foo(bar: Int) => func foo(_ bar: Int)

"RW".writeToFile("filename", atomically: true, encoding: NSUTF8StringEncoding)
"RW".write(toFile: "filename", atomically: true, encoding: String.Encoding.utf8)
SKAction.rotateByAngle(CGFloat(M_PI_2), duration: 10)
SKAction.rotate(byAngle: .pi / 2, duration: 10)
UIFont.preferredFontForTextStyle(UIFontTextStyleSubheadline)
UIFont.preferredFont(forTextStyle: UIFontTextStyleSubheadline)
override func numberOfSectionsInTableView(tableView: UITableView) -> Int
override func numberOfSections(in tableView: UITableView) -> Int

New format of enumeration cases:

UIInterfaceOrientationMask.Landscape
UIInterfaceOrientationMask.landscape
NSTextAlignment.Right
NSTextAlignment.right

Introduction of Self:

CustomStruct.staticMethod()

struct CustomStruct {
  static func staticMethod() { ... }
  
  func instanceMethod() {
    Self.staticMethod() // in the body of the type
  }
}
  
let customStruct = CustomStruct()
customStruct.Self.staticMethod() // on an instance of the type

For loops and ranges:

// Swift 2.x
for var i = 0 ; i < 10 ; i++ {
    print(i)
}
  
// Swift 3
for var i in 0..<10{
    print(i)
}
// Only in Swift 2.x
var z = Range(start: 1, end: 5)
 
// Swift 2.x and Swift 3
// from 1 to 9 (included)
for var i in 1...9{
    ...
}
 
// from 1 to 10 (not included)
for var i in 1..<10{
    ...
}

The Swift Package Manager:

Collection indexing changes:

myIndex.successor()  =>  myCollection.index(after: myIndex)
myIndex.predecessor()  =>  myCollection.index(before: myIndex)
myIndex.advance(by: …) => myCollection.index(myIndex, offsetBy: …)

Apple will give and take away:

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:

Bugs I encountered:

Weird changes, conversion issues:

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:
use_frameworks!
 
target 'RSS_Reader' do
 
platform :ios, '10.0'
 
pod 'Alamofire', '~> 4.0'
pod 'HanekeSwift', :git => 'https://github.com/beta-uy/HanekeSwift.git', :branch => 'feature/swift-3'
 
end

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.

  1. 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.
  2. 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.
  3. Next, clean and build your project again, there should be errors only in your hand-written code.
  4. Convert the rest of your project: the targets and tests (Edit -> Convert -> To Current Swift Syntax…)
  5. If you still have errors Clean and Clean Build Folder again, convert your targets, just in case the converter overlooked something.
  6. The converter will probably not change functions of pods/3rd party frameworks, so you will have to do some legwork, and update them manually.
  7. The remaining errors need some researching, or thinking, for example sharedInstance:
//Swift 2.3:
class var sharedInstance: AppConf {     
    struct Static {
        static var instance: AppConf?
        static var token: dispatch_once_t = 0
    }
         
    dispatch_once(&Static.token) {
        Static.instance = AppConf()
    }
 
    return Static.instance!
}
 
//Converted Swift 3 (throws error)
fileprivate static var __once: () = {
    Static.instance = AppConf()
}()
     
class var sharedInstance: AppConf {
    struct Static {
        static var instance: AppConf?
        static var token: Int = 0
    } 
    _ = AppConf.__once
    return Static.instance!
}
 
//Fixed Swift 3:
class var sharedInstance: AppConf {
    struct Static {
        static var instance: AppConf?
        static var token: Int = 0
    }
    Static.instance = AppConf()
    return Static.instance!
}

but they can be fixed quite easily.

Some tips:

text

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.

member photo

He is a junior iOS developer at Wanari and has been with us since January. His coding style and favorite language are the same: Swift.

Latest post by Róbert Klacso

A Beginner’s Guide to XML Parsing in Swift