Swift Language Highlights: An Objective-C Developer’s Perspective

Check out some of the highlights of the new Swift language from an Objective-C developer’s perspective! By Matt Galloway.

Leave a rating/review
Save for later
Share

Contents

Hide contents

Swift Language Highlights: An Objective-C Developer’s Perspective

20 mins

Update 8/5/14: Updated for Xcode6-Beta5.

If you were like me this Monday, you were sitting back enjoying the keynote, excited to start trying out all the new lovely APIs. And then your ears pricked up as you listened to words about a new language: Swift! It suddenly hit you that this is not an extension to Objective-C, but a completely brand new language. Maybe you were excited? Maybe you were happy? Maybe you didn’t know what to think.

Swift has surely changed the way we’re going to write iOS and Mac applications in the future. In this post, I outline some of the highlights of the Swift language, contrasting them to their counterparts in Objective-C.

Note this post is not designed to be a Swift get started guide. Apple have released a fantastic book about this, and I strongly suggest you read it. Instead, this is a discussion of some particularly cool areas to notice and play around with!

Types

The first huge thing that Swift provides is type inference. In a language that uses type inference, the programmer doesn’t need to annotate variables with type information. The compiler infers it from what value is being set to the variable. For example, the compiler can automatically set this variable to a String:

// automatically inferred
var name1 = "Matt"
// explicit typing (optional in this case)
var name2:String = "Matt"

Along with type inference, Swift brings type safety. In Swift, the compiler (in all but a few special cases) knows the full type of an object. This allows it to make some decisions about how to compile code, because it has more information at hand.

This is in stark contrast to Objective-C which is extremely dynamic in nature. In Objective-C, no type is truly known at compile time. This is in part because you can add methods to existing classes, add entirely new classes and even change the type of an instance, all at runtime.

Let’s take a look at that in some more detail. Consider the following Objective-C:

Person *matt = [[Person alloc] initWithName:@"Matt Galloway"];
[matt sayHello];

When the compiler sees the call to sayHello, it can check to see if there’s a method declared in the headers it can see on the type Person called sayHello. It can error if there isn’t one, but that’s about all it can do. This is often enough to catch the first line of bugs that you might introduce. It will catch things like typos. But because of the dynamic nature, the compiler doesn’t know if the sayHello is going to change at runtime or even necessarily even exist. It could be an optional method on a protocol, for example. (Remember all those respondsToSelector: checks?).

Because of this lack of strong typing, there is very little the compiler can do to make optimisations when calling methods in Objective-C. The method that handles dynamic dispatch is called objc_msgSend. I’m sure you’ve seen this in many a backtrace! In this function, the implementation of the selector is looked up and then jumped to. You cannot argue this doesn’t add overhead and complexity.

Now look at the same code in Swift:

var matt = Person(name:"Matt Galloway")
matt.sayHello()

In Swift, the compiler knows much more about the types in play in any method call. It knows exactly where sayHello() is defined. Because of this, it can optimise certain call sites by jumping directly to the implementation rather than having to go through dynamic dispatch. In other cases, it can use vtable style dispatch, which is far less overhead than dynamic dispatch in Objective-C. This is the kind of dispatch that C++ uses for virtual functions.

The compiler is much more helpful in Swift. It will help stop subtle type related bugs from entering your codebase. It will also make your code run faster by enabling smart optimisations.

Generics

Another huge feature of Swift is generics. If you’re familiar with C++, then you can think of these as being like templates. Because Swift is strict about types, you must declare a function to take parameters of certain types. But sometimes you have some functionality that is the same for multiple different types.

An example of this would be the often useful structure of a pair. You want a pair of values to be stored together. You could implement this in Swift for integers like so:

struct IntPair {
    let a: Int!
    let b: Int!
    
    init(a: Int, b: Int) {
        self.a = a
        self.b = b
    }
    
    func equal() -> Bool {
        return a == b
    }
}

let intPair = IntPair(a: 5, b: 10)
intPair.a // 5
intPair.b // 10
intPair.equal() // false

Sort of useful. But now you want this to work for floating point numbers as well. You could define a FloatPair class, but that would look awfully similar. This is where generics come in. Instead of declaring a whole new class, you could simply do this:

struct Pair<T: Equatable> {
    let a: T!
    let b: T!
    
    init(a: T, b: T) {
        self.a = a
        self.b = b
    }
    
    func equal() -> Bool {
        return a == b
    }
}

let pair = Pair(a: 5, b: 10)
pair.a // 5
pair.b // 10
pair.equal() // false

let floatPair = Pair(a: 3.14159, b: 2.0)
floatPair.a // 3.14159
floatPair.b // 2.0
floatPair.equal() // false

Pretty useful! It might seem unclear why you’d want this sort of feature at this time, but trust me: the opportunities are endless. You’ll soon start to see where you can apply these in your own code.

Containers

You’ve come to know and love NSArray, NSDictionary and their mutable counterparts. Well, now you are going to have to learn about their Swift equivalents. Fortunately, they’re pretty similar. Here is how you declare arrays and dictionaries:

let array = [1, 2, 3, 4]
let dictionary = ["dog": 1, "elephant": 2]

This should be fairly familiar to you. There’s one slight catch though. In Objective-C, arrays and dictionaries can contain any type you jolly well wish. But in Swift, arrays and dictionaries are typed. And they are typed through the use of our friend from above, generics!

The two variables above can be rewritten with their types expressed (although remember you don’t actually have to do this!) like so:

let array: Array<Int> = [1, 2, 3, 4]
let dictionary: Dictionary<String, Int> = ["dog": 1, "elephant": 2]

Notice how generics are used to define what can be stored in the container. There is also a short form for these, which is slightly more readable, but essentially boils down to the same thing:

let array: [Int] = [1, 2, 3, 4]
let dictionary: [String: Int] = ["dog": 1, "elephant": 2]

Notice now that you cannot add anything to the array that isn’t of type Int. This may sound like a bad thing, but it’s incredibly useful. No longer does your API have to document what is being stored in the array it returns from a certain method or stored in a property. You can give that information right up to the compiler so that it can be smarter about error checking and optimisation described earlier.

Contributors

Over 300 content creators. Join our team.