From Objective-C to Swift: Introduction

At the WWDC 2014, Apple unveiled Swift as a complete replacement for Objective-C, the recommended programming language for developing for the Mac OS X and iOS platforms since Apple bought NextStep in 1997.

While you can still use Objective-C for all development, knowing Apple, it makes sense to start using Swift as soon as possible.

The swift is a very fast bird with a recorded max speed of 171 km/h (106 mph)
The swift is a very fast bird with a recorded max speed of 171 km/h (106 mph)

Apple has provided excellent documentation for the new language on The main Swift page, but reading through all that may take some time. The point of these two articles is to enable you to jump in and start using Swift right away, while still knowing about some of the more interesting features. Where applicable, we will point you to Apple’s documentation for further insights.

Swift Example

let π = 3.14159 // NOTE: Unicode identifiers. 'let' defines a constant

func circleArea(radius r: Double) -> Double { // NOTE: Named argument: 'r' internally, 'radius' for clients
    return π*r*r
}

let area = circleArea(radius: 1) // NOTE: 'radius:' is required
println("Circle area is \(area)") // NOTE: "String interpolation": You can include expressions in Strings with \(...)

class Circle {
    var r: Double // NOTE: An instance property. 'var' defines a variable
    init(radius: Double) {
        r = radius
    }
    init(diameter d: Double) {
        r = d/2
    }
    func circumference() -> Double {
        return 2*π*r
    }
}

let circle = Circle(radius: 1)
println("Circle circumference is \(circle.circumference())")

let circumferences = [1, 2].map { r in Circle(radius: r).circumference() }
println("Circumferences: \(circumferences)")

Result:

Circle area is 3.14159
Circle circumference is 6.28318
Circumferences: [6.28, 12.56]

Swift Migration

The above may look strange to Objective-C programmers, but Swift can look familiar too, like this code for a custom UIButton:

import UIKit

class RoundButton: UIButton {

    override func drawRect(rect: CGRect) { // NOTE: Explicitly state your intention to override methods
        UIColor.whiteColor().setFill()

        let width = CGRectGetWidth(self.bounds)
        let height = CGRectGetHeight(self.bounds)
        let capRadius = height / 2

        var p = CGPointZero

        let path = UIBezierPath()
        p = CGPointMake(capRadius, 0)
        path.moveToPoint(p)
        p = CGPointMake(width-capRadius, 0)
        path.addLineToPoint(p)
        path.addArcWithCenter(CGPointMake(p.x, p.y+capRadius), radius:capRadius, startAngle:-M_PI_2, endAngle:M_PI_2, clockwise:true)
        p = CGPointMake(capRadius, 2*capRadius)
        path.addLineToPoint(p)
        path.addArcWithCenter(CGPointMake(p.x, p.y-capRadius), radius:capRadius, startAngle:M_PI_2, endAngle:-M_PI_2, clockwise:true)
        path.fill()
    }

}

The same drawRect(), utilizing more Swift features:

override func drawRect(rect: CGRect) {
    UIColor.whiteColor().setFill()

    let width = self.bounds.width
    let height = self.bounds.height
    let capRadius = height / 2

    var p = CGPoint.zeroPoint

    let path = UIBezierPath()
    p = CGPoint(x: capRadius, y: 0)
    path.moveToPoint(p)
    p = CGPoint(x: width-capRadius, y: 0)
    path.addLineToPoint(p)
    path.addArcWithCenter(CGPoint(x: p.x, y: p.y+capRadius), radius:capRadius, startAngle:-M_PI_2, endAngle:M_PI_2, clockwise:true)
    p = CGPoint(x: capRadius, y: 2*capRadius)
    path.addLineToPoint(p)
    path.addArcWithCenter(CGPoint(x: p.x, y: p.y-capRadius), radius:capRadius, startAngle:M_PI_2, endAngle:-M_PI_2, clockwise:true)
    path.fill()
}

Going a bit overboard:

let π = M_PI

// Add a tuple to a CGPoint
func +(p: CGPoint, t: (Double, Double)) -> CGPoint { // NOTE: Override the '+' operator
    return CGPoint(x: p.x+t.0, y: p.y+t.1)
}

// Assign a tuple to a CGPoint (NOTE: you cannot overload '=')
infix operator << { associativity right precedence 90 } // Introduce a new operator, having the same associativity and precedence as '='

func <<(inout p: CGPoint, t: (Double, Double)) {
    p = CGPoint(x: t.0, y: t.1)
}

override func drawRect(rect: CGRect) {
    UIColor.whiteColor().setFill()

    let (width, height) = (self.bounds.width, self.bounds.height) // NOTE: Initialize two constants simultaneously
    let capRadius = height / 2

    var p = CGPoint.zeroPoint

    let path = UIBezierPath()
    p << (capRadius, 0)
    path.moveToPoint(p)
    p << (width-capRadius, 0)
    path.addLineToPoint(p)
    path.addArcWithCenter(p+(0,capRadius), radius:capRadius, startAngle:-π/2, endAngle:π/2, clockwise:true)
    p << (capRadius, 2*capRadius)
    path.addLineToPoint(p)
    path.addArcWithCenter(p+(0,-capRadius), radius:capRadius, startAngle:π/2, endAngle:-π/2, clockwise:true)
    path.fill()
}

For a list of associativity and precedence for the built in infix operators, see Binary Expressions.

 

Moving to Swift

So, I’m not going to waste too much of your time showing off random Swift code. The deal is to get you started writing your own fancy Swift code in a short while.

The Playground

Xcode 6 includes a file type called a Playground, where you can play with Swift as if it was a scripting language. You can create as many playgrounds in a project as you like.

A couple of tips:

  • Writing e.g. map([1,2,3]) {$0*2} will probably not give you what you want. The result column just states “(4 times”), as something on the line was evaluated four times. Just assign it to something, and evaluate that:
    let myList = map([1,2,3]) {$0*2}
    myList
  • Do not call -[NSObject addObserver]  in the Playground. It will crash.
  • To get a nice graph in the Timeline, hover over the result column (with text like “4 times”) and click the small circle.

Using Swift from Objective-C

Besides playgrounds, you may of course add real Swift source files to projects in Xcode 6. If you create a useful class in Swift that you would like to use from Objective-C, you should add the @objc attribute in front of the class declaration (if the parent wasn’t already Objective-C compatible, as e.g. imported UIKit classes), like so:

@objc class Circle {
    var r: Double
    init(radius: Double) {
        r = radius
    }
    init(diameter d: Double) {
        r = d/2
    }
    func circumference() -> Double {
        return 2*π*r
    }
}

All applicable Swift stuff can then be used from your Objective-C code by including the project Swift header, named after your project:

#import "MyProject-Swift.h"

class ObjCCirle : Circle {
}

And that’s all there is to it!

For more info, see Swift and Objective-C in the Same Project.

Using Objective-C from Swift

A bit more fun, and probably something you will use a lot more often, is accessing Objective-C (and C) stuff from your Swift source code.

A lot of existing Objective-C APIs are readily available. You just have to import it, like e.g.

import UIKit

For project specific APIs, you need the Bridging Header, which Xcode 6 will offer to create for you when you first add either a Swift file to an Objective-C project, of an Objective-C file to a Swift project. The header file is named after your project, like “MyProject-Bridging-Header.h” and should include all the #imports (and possibly declarations) that you want to be accessible from Swift.

For more information please refer to Using Swift with Cocoa and Objective-C.

Optionals

When working with Objective-C APIs, you will often have to interact with Optionals. These are needed as Swift is happily devoid of pointers, so denoting that a variable may point to nothing is achieved using a type that may either be nil or an actual value.

To work with Optionals, you use ?, !, and the “if let” statement:

var possiblyNil: Circle?
possiblyNil = Circle(radius: 5)
possiblyNil!.circumference() // Throws if nil
possiblyNil?.circumference() // A new optional that may be either nil or a Double
if let notNil = possiblyNil {
    notNil.circumference() // notNil is not an optional
}

let certainlyNil: Circle?
certainlyNil?.circumference() // nil
if let notNil = certainlyNil {
    notNil.circumference() // Never executed
}

var shouldntBeNil: Circle! // Shouldn't ever be nil
shouldntBeNil = Circle(radius: 5)
shouldntBeNil.circumference() // Treat as normal variable

When designing your own Swift APIs, you should of course only use Optionals when necessary.

For more information, please see Optional Chaining and The Optional Type.

Next up…

The next article will show you the main features of Swift, enabling you to read and understand Swift code like a pro.

Continue reading Swift Goodies

Leave a Reply

Close Menu