From Objective-C to Swift: Swift Goodies

Swift comes with a lot of really nice features that make it hard to go back to Objective-C. The main feature is safety, but that may be seen as just a bonus side effect. There are a lot of other reasons to use Swift.

Strong Typing with Type Inference

Swift has strong typing, meaning that Swift will not convert between types for you, unless you ask it to. So you cannot just assign e.g. an Int to a Double. You have to typecast it first (actually construct a Double from an Int):

let i: Int = 42
let d: Double = Double(i)

Strong typing is really, really beneficial for safety. But it could become a bit of a daunting task if it wasn’t for Type Inference adding a lot of type information for you, almost like writing in a scripting language.

let ary = ["Hello", "world"] // NOTE: 'ary' is of type [String] or Array<String>
for s in ary { // NOTE: 's' is of type String
    print(s + " ")
}

If you want to create an array containing several types (with no common ancestor), you should use an enum (which can hold values, see below). If you would like it to be able to hold any type at all, you can use the Any type. To make it hold any Objective-C type, use the AnyObject type.

Please note that Type Inference won’t add the types for you when declaring functions. You have to explicitly state the type of functions you declare.

Blocks

Blocks in Swift (called Closures) look a lot like blocks in Objective-C, with two main exceptions: Type Inference and avoiding the weakify dance.

With Type Inference you don’t have to include the full type each time you write a block:

sorted([2,1,3], { (a: Int, b: Int) -> Bool in
    return a < b
})

// Using Type Inference
// Using the Trailing Closures feature

sorted([2,1,3]) { a, b in
    return a < b
}

// Implicit 'return' for single-expression blocks

sorted([2,1,3]) { a,b in a<b }

// Shorthand Argument Names

sorted([2,1,3]) { $0 < $1 }

// Operators are functions, and functions are blocks too!

let sortedArray: [Int] = sorted([2,1,3], <)

To read more about blocks in Swift, please visit: Closures.

On top of that, the Objective-C weakify dance gets a lot simpler, by just including e.g.  [unowned self]  or [weak self]  at the start of the block:

class CallbackTest {
    var i = 5
    var callback: (Int -> ())? // NOTE: The optional callback takes an Int
    deinit { // NOTE: This is like -dealloc in Objective-C
        println("Deinit")
    }
}

var obj = CallbackTest()
obj.callback = { [unowned obj] a in // NOTE: Without [unowned obj], deinit() would never be invoked!
    obj.i = a
}

Please note that Optionals (like the callback above) was explained in the Introduction post.

To read all about ARC in Swift, please refer to the ARC chapter.

Enumerations Supercharged

Enumerations in Swift are a major step up from Objective-C.

Fixing Enums

Apple has been advocating explicit sizes for enumeration types for a while, this is a mess in Objective-C:

// Apple recommended enum definition in Objective-C
typedef NS_ENUM(NSInteger, UIViewAnimationCurve) {
    UIViewAnimationCurveEaseInOut,
    UIViewAnimationCurveEaseIn,
    UIViewAnimationCurveEaseOut,
    UIViewAnimationCurveLinear
};

// Previous Apple recommended enum definition in Objective-C. No link between enum values and the UIViewAnimationCurve type.
typedef enum {
    UIViewAnimationCurveEaseInOut,
    UIViewAnimationCurveEaseIn,
    UIViewAnimationCurveEaseOut,
    UIViewAnimationCurveLinear
};
typedef NSInteger UIViewAnimationCurve;

// Traditional enum definition in Objective-C. No explicit fixed size.
typedef enum {
    UIViewAnimationCurveEaseInOut,
    UIViewAnimationCurveEaseIn,
    UIViewAnimationCurveEaseOut,
    UIViewAnimationCurveLinear
} UIViewAnimationCurve;

This is fixed in Swift:

enum UIViewAnimationCurve: Int {
    case EaseInOut
    case EaseIn
    case EaseOut
    case Linear
}

Extending Enums

Enums in Swift goes further than just being a list of unique choices. You can add methods (and computed properties):

enum UIViewAnimationCurve: Int {
    case EaseInOut
    case EaseIn
    case EaseOut
    case Linear
    func typeName() -> String {
        return "UIViewAnimationCurve"
    }
}

Using type extensions, you can add methods to any enum you like:

extension UIViewAnimationCurve {
    func description() -> String {
        switch self {
        case EaseInOut:
            return "EaseInOut"
        case EaseIn:
            return "EaseIn"
        case EaseOut:
            return "EaseOut"
        case Linear:
            return "Linear"
        }
    }
}

Adding value to Enums

Enumerations in Swift goes even further by allowing each unique choice to have an associated value:

enum Shape {
    case Dot
    case Circle(radius: Double) // Require argument name!
    case Square(Double)
    case Rectangle(width: Double, height: Double) // Require argument names!
    func area() -> Double {
        switch self {
        case Dot:
            return 0
        case Circle(let r): // Assign the associated value to the constant 'r'
            return π*r*r
        case Square(let l):
            return l*l
        case Rectangle(let w, let h):
            return w*h
        }
    }
}
var shape = Shape.Dot
shape = .Square(2)
shape = .Rectangle(width: 3, height: 4) // Argument names required
shape.area()

You can consider this as a safe union type, if you want. Or just the way enumerations should be.

For Apples take on it, go read the Enumerations chapter.

Swift Switches

As you just witnessed, switch statements in Swift have been improved in several ways.

The implicit fall-through behavior has been changed to be explicit:

var (i, j) = (4, -1) // Assign (and create) two variables simultaneously
switch i {
case 1:
    j = 1
case 2, 3: // The case for both 2 and 3
    j = 2
case 4:
    j = 4
    fallthrough
case 5:
    j++
default:
    j = Int.max // The Swift version of INT_MAX
}

Switch statements can access the associated values of enumerations, as you saw earlier, but they can do more than that:

var tuple: (Int, Int) // Did I mention that Swift has tuples? :-)
var result: String

tuple = (1,3)

switch tuple {
case (let x, let y) where x > y:
    result = "Larger"
case (let x, let y) where x < y:
    result = "Smaller"
default:
    result = "Same"
}

It even works for strings:

var s: String = "Cocoa"
switch s {
case "Java":   s = "High caffeine"
case "Cocoa":  s = "High sugar"
case "Carbon": s = "Lots of bubbles"
default: ()
}

And if you think it makes your code more readable, you could overload the ~= operator to alter the behavior of switch statements!

func ~=(pattern: String, str: String) -> Bool {
    return str.hasPrefix(pattern)
}

var s = "Carbon"
switch s {
case "J":  s = "High caffeine"
case "C":  s = "No caffeine"
default: ()
}

You can read more about switch statements under Conditional Statements.

Classes and Structs

Like in C++, Swift classes and structs look the same at first:

class Apple {
    var color = "green" // Property declaration
    init() {} // Default initializer
    init(_ color: String) { // '_' means no argument name
        self.color = color
    }
    func description() -> String {
        return "apple of color \(color)"
    }
    func enripen() {
        color = "red"
    }
}

struct Orange {
    var color = "green"
    init() {}
    init(_ color: String) {
        self.color = color
    }
    func description() -> String {
        return "orange of color \(color)"
    }
    mutating func enripen() { // NOTE: 'mutating' is required
        color = "orange"
    }
}

var apple1 = Apple()
var apple2 = apple1 // NOTE: This references the same object!
apple1.enripen()
apple2.description() // Result: "apple of color red"

var orange1 = Orange()
var orange2 = orange1 // NOTE: This makes a copy!
orange1.enripen()
orange2.description() // Result: "orange of color green"

The main difference is that classes are reference types (along with blocks), while structs are value types (along with enumerations). So two variables can point to the same (class) object, while assigning a struct to another variable will make a (lazy) copy of the struct. The ‘mutating ‘ keyword tells callers that the enripen()  method cannot be invoked on constant structs. Mutating a constant reference to a class object is all fine.

Most built-in types are actually structs in Swift, and can be extended by user code. Even Int. You can see the declarations for built-in stuff by Cmd-clicking e.g. an Int in Swift source code (or in a Playground). The Array and Dictionary types are structs too, so assigning an Array variable to another will copy the array and all its elements (this is done lazily on demand, though).

let array1 = [1, 2, 3] // An immutable array
var array2 = array1 // A copy (on-demand)
var array3 = array2 // Another copy (on-demand)
array2[1] = 5 // Lazy copy element at index 1
array3 // [1, 2, 3]

You can read more about the Collection Types in Apple’s documentation.

 The Life of an Object

Another major difference between classes and structs is that classes can be subclassed. Both classes and structs can be extended, and they can implement protocols, but only classes can inherit from other classes.

class Pineapple: Apple {
    init(color: String) { // NOTE: Not overriding init(_: String), so Pineapple("green") isn't valid!
        super.init(color)
    }
    convenience override init() {
        self.init(color: "green")
    }
    convenience init(ripe: Bool) {
        self.init()
        if ripe {
            color = "yellow"
        } else {
            color = "green"
        }
    }
    deinit {
        println("Pineapple down")
    }
    override func description() -> String {
        return "pine" + super.description()
    }
    override func enripen() {
        color = "yellow"
    }
}

As you can see, inheritance adds a bit more fun Swift stuff to learn. For starters, you need to be explicit about your intention to override a method declared in a parent class. If you want to prevent children overriding something, you can add the attribute final in front of either a single declaration or the entire class. For more attributes, see Apple’s documentation.

Initialization

Swift objects are initialized in two steps: First the object has to be valid, then it can be tweaked.

class ChildShoe {
    var size: Double // Uninitialized properties are not allowed unless taken care of in init()
    init(foot_size: Double) {
        size = foot_size // First make the object valid
        addGrowthCompensation()
    }
    func addGrowthCompensation() {
        size++
    }
}

Making the object valid entails invoking one of the super class’s designated init() methods. Classes can have both designated initializers and convenience initializers (marked with the ‘convenience’ keyword). The convenience initializers call other initializers in the same class (ultimately a designated initializer), and designated initializers call the super class’s initializers.

If you add initializers for all the super class’s designated initializers, then your class will automatically inherit all the convenience initializers too. If you do not add any designated initializers at all, then your class will inherit all the initializers (designated and convenience) of the super class.

Please refer to Initialization for further reading.

Type casting

For casting between classes, especially down-casting,  you can use “is”, “as?”, and “as”:

let apple: Apple = Pineapple()

let exotic: Bool = apple is Pineapple

let pineappleOption: Pineapple? = apple as? Pineapple
let pineapple: Pineapple = apple as Pineapple // NOTE: Throws if not!

if let obj = apple as? Pineapple { // If executed, 'obj' is a Pineapple
    "sweet"
}

To read all about it, visit the Type Casting chapter.

Generics

Generics are a major plus for Swift. They look a bit like C++ templates, but are stronger typed and simpler (simpler to use, and less capable).

// Mark both Int and Double as being convertible to a Double using the '+' prefix
protocol DoubleConvertible {
    prefix func +(v: Self) -> Double
}
prefix func +(v: Int) -> Double { return Double(v) }
extension Double: DoubleConvertible {}
extension Int: DoubleConvertible {}
// NOTE: Repeat this for all Int*, UInt*, and the Float type

// The traits of a generalized point
protocol PointTraits {
    typealias T
    class var dimensions: Int { get }
    func getCoordinate(dimension: Int) -> T
}

// Generalized Pythagoras
struct Pythagoras<P1: PointTraits, P2: PointTraits where P1.T: DoubleConvertible, P2.T: DoubleConvertible> {
    static func apply(a: P1, b: P2, dimensions: Int) -> Double {
        if dimensions == 0 {
            return 0
        }
        let d: Double = +a.getCoordinate(dimensions-1) - +b.getCoordinate(dimensions-1) // NOTE: '+' to convert to Double
        return d * d + apply(a, b: b, dimensions: dimensions-1)
    }
    static func apply(a: P1, b: P2) -> Double {
        let dimensions = P1.dimensions
        assert(P2.dimensions == dimensions)
        return apply(a, b: b, dimensions: dimensions)
    }
}

import func Foundation.sqrt // NOTE: You can import a typealias­, struct­, class­, enum­, protocol­, var­, or func only

func distance<P1: PointTraits, P2: PointTraits where P1.T: DoubleConvertible, P2.T: DoubleConvertible>(a: P1, b: P2) -> Double {
    assert(P1.dimensions == P2.dimensions)
    return sqrt(Pythagoras.apply(a, b: b))
}

// A generalized 2D point
struct Point2D<Number>: PointTraits {
    static var dimensions: Int { return 2 }
    var x: Number, y: Number
    func getCoordinate(dimension: Int) -> Number { return dimension == 0 ? x : y } // NOTE: The typealias T is inferred
}
let a = Point2D(x: 1.0, y: 2.0)
let b = Point2D(x: 5, y: 5)
Pythagoras.apply(a, b: b) // NOTE: Methods require all argument names, except the first
distance(a, b) // NOTE: Functions do not require argument names

// UIColor
extension UIColor: PointTraits {
    class var dimensions: Int { return 4 }
    func getCoordinate(dimension: Int) -> Double {
        var red: CGFloat = 0, green: CGFloat = 0, blue: CGFloat = 0, alpha: CGFloat = 0
        getRed(&red, green: &green, blue: &blue, alpha: &alpha)
        switch dimension {
        case 0: return red
        case 1: return green
        case 2: return blue
        default: return alpha
        }
    }
}
distance(UIColor.redColor(), UIColor.orangeColor())

The above was inspired by the Design Rationale for the Boost.Geometry C++ library. Generics in Swift are not as capable, but makes for much more readable source code than the C++ version.

Generics in Swift are parameterized by types. Each parameter type can be required to implement a specific protocol or inherit from a specific base class. After declaring the parameter types, an optional “where” clause can add requirements to the types (or their internal types, like the typealias ‘T’ in the PointTraits protocol above). A “where” clause can also require two types to be equal.

Apple has a much more thorough chapter on Generics.

Adopting Swift

You are now ready to go read all sorts of Swift source code, and even write your own :-)

A couple of final notes, before I leave you to the new and largely undiscovered land of Swift:

  • Apple recommends that you use Int for all integer types, even where you previously used an unsigned type. “Use UInt only when you specifically need an unsigned integer type with the same size as the platform’s native word size.”
  • Apropos Ints, you can now add underscores in number literals for greater legibility, e.g.
    let mil = 1_000_000

    The compiler simply ignores the underscores.

  • The String class has some nice initializers. E.g.
    String(0x16, radix:2) // "10110"
    String(255, radix:16, uppercase: true) // "FF"
    let π = M_PI
    String(format:"%i, %.3lf", 7, π) // "7, 3.142"

    As of this writing, this hasn’t been documented yet, so it may not go into the final release of Xcode 6.

  • If you create a Swift module, you can Cmd-click the name of your module to see the auto-generated Swift header for your module.
  • Swift modules are actually namespaces, so prefixing everything like CF, NS, UI, etc. isn’t strictly necessary anymore when creating third party libraries.

Enjoy using Swift!

This Post Has 2 Comments

  1. Hello, first place, great post, but I have one question.

    There is posible migrate a existing Objective – C app released at the App Store to Swift?, this app have Core Data, so how can migrate the Objective – C Core Data model wich current version is number 4, to Swift without crash the app?

    Sorry for my bad english.

    Thank you very much.

  2. Well, I’m happy to say that I couldn’t resist and created iSwift (started out as a Swift-related blog) but it now includes a fully featured an Objective-C to Swift converter.

    http://iswift.org

    It currently works online, as an independent OSX app and I’m also thinking of releasing it as a plugin for the Peppermint editor.

    Could it be considered complete? As most of you would imagine, I believe it could never become a Drop-your-Xcode-project app that instantly converts anything 100% correctly, producing something that does compile.

    However, it most definitely does a great job and certainly saves you up to 95% of the time you’d need to manually convert it.

    So, here it is. I’m looking forward for your feedback! :)

Leave a Reply

Close Menu