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

Matt Galloway Matt Galloway

Swift

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.

Mutability

One interesting thing about collections in Swift is their mutability. There are no “mutable” counterparts to Array and Dictionary. Instead, you use the standard let and var. For those who haven’t read the book yet, or delved into Swift at all (and I suggest you do, ASAP!), let is used to declare a variable as constant, and var is used to declare a variable as, well, variable! let is like using const in C/C++/Objective-C.

The way this relates to collections is that collections declared using let cannot change size. That is, they cannot be appended to, or removed from. If you try to, then you get an error like so:

let array = [1, 2, 3]
array.append(4)
// error: immutable value of type 'Array<Int>' only has mutating members named 'append'

The same applies to dictionaries. This fact allows the compiler to reason about such collections and make optimisations as appropriate. If the size cannot change, then the backing store that holds the values never needs to be reallocated to accommodate new values, for example. For this reason it is good practice to always use let for collections that won’t change.

Strings

Strings in Objective-C are notoriously annoying to deal with. Even simple tasks such as concatenating lots of different values becomes tedious. Take the following example:

Person *person = ...;
 
NSMutableString *description = [[NSMutableString alloc] init];
[description appendFormat:@"%@ is %i years old.", person.name, person.age];
if (person.employer) {
  [description appendFormat:@" They work for %@.", person.employer];
} else {
  [description appendString:@" They are unemployed."];
}

This is quite tedious and contains a lot of characters that are nothing to do with the data being manipulated. The same in Swift would look like this:

var description = ""
description += "\(person.name) is \(person.age) years old."
if person.employer {
    description += " They work for \(person.employer)."
} else {
    description += " They are unemployed."
}

Much clearer! Notice the cleaner way of creating a string from a format, and you can now concatenate strings simply by using +=. No more mutable string and immutable string.

Another fantastic addition to Swift is comparison of strings. You’ll be aware that in Objective-C it is not correct to compare strings for equality using ==. Instead you should use the isEqualToString: method. This is because the former is performing pointer equality. Swift removes this level of indirection and instead leaves you being able to directly use == to compare strings. It also means that strings can be used in switch statements. More on that in the next section though.

The final piece of good news is that Swift supports the full Unicode character set natively. You can use any Unicode code-point in your strings, and even function and variable names! You can now have a function called 💩 (pile of poo!) if you want!

Another nugget of good news is there is now a builtin way to calculate the true length of a string. When it comes to the full Unicode range, string length is non-trivial to compute. You can’t just say it’s the number of bytes used to store the string in UTF8, because some characters take more than 1 byte. In Objective-C, NSString does the calculation by counting the number of UTF16, 2-byte pairs are used to store the string. But that’s not technically correct since some Unicode code-points take up two, 2-byte pairs.

Fortunately, Swift has a handy function to calculate the true number of code-points in a string. It uses the top level function called countElements(). You would use it like so:

var poos = "\u{1f4a9}\u{1f4a9}"
countElements(poos) // 2

It doesn’t quite work for all cases though still. It just counts the number of Unicode code-points. It doesn’t take into account special code-points that alter other characters. For example, you can put an umlaut on the previous character. In that case, countElements() would return 2 for the pair, even though it looks like just 1 character. Like so:

var eUmlaut = "e\u{0308}"
countElements(eUmlaut) // 2

All that said, I think you’ll agree that strings are pretty awesome in Swift!

Switch statements

The final thing I want to call out in this brief introduction to Swift is the switch statement. It has been drastically improved in Swift over its Objective-C counterpart. This is an interesting one, because it’s something that couldn’t have been added on to Objective-C without breaking the fundamental truth that Objective-C is a strict superset of C.

The first exciting feature is switching on strings. This is something that you may have wanted to do before, but couldn’t. To “switch” on strings in Objective-C you had to use lots of if-statements with isEqualToString: like so:

if ([person.name isEqualToString:@"Matt Galloway"]) {
  NSLog(@"Author of an interesting Swift article");
} else if ([person.name isEqualToString:@"Ray Wenderlich"]) {
  NSLog(@"Has a great website");
} else if ([person.name isEqualToString:@"Tim Cook"]) {
  NSLog(@"CEO of Apple Inc.");
} else {
  NSLog(@"Someone else);
}

This is not particularly readable. It’s also a lot of typing. The same in Swift looks like this:

switch person.name {
  case "Matt Galloway":
    println("Author of an interesting Swift article")
  case "Ray Wenderlich":
    println("Has a great website")
  case "Tim Cook":
    println("CEO of Apple Inc.")
  default:
    println("Someone else")
}

Aside from the switching on a string, notice something interesting here. There are no breaks in sight. That’s because cases in switches no longer fall through to the next one. No more accidentally falling through bugs!

Now the next switch statement may very well blow your mind, so be prepared!

switch i {
case 0, 1, 2:
    println("Small")
case 3...7:
    println("Medium")
case 8..<10:
    println("Large")
case _ where i % 2 == 0:
    println("Even")
case _ where i % 2 == 1:
    println("Odd")
default:
    break
}

First up, there is now a break. This is because switches need to be exhaustive, i.e. they need to handle all cases now. In this case, we want the default to do nothing, so a break is added to declare the intentions that nothing should happen.

The next interesting thing is the ... and ..< that you see in there. These are new operators and are used to define ranges. The former, defines a range up to and including the right hand number. The latter defines a range up to and excluding the right hand number. These are incredibly useful.

The last thing is the ability to define a case as a calculation of the input. In this case, if the value doesn't match anything from zero to ten, it prints "Even" if it's even and "Odd" if it's odd. Magic!

Where To Go From Here?

This has hopefully given you a taste of the Swift language and what wonderful gems there are in there. But there's far more! I encourage you to go and read the Apple book, and other Apple documentation that will help you learn this new language. You're going to have to do it sooner or later!

Also, if you'd like to learn more about Swift, check out our three brand new Swift books - covering everything you need to know about Swift, iOS 8, and more!

We'd love to hear what you think of the Swift language so far, or if there are any cool highlights you're excited about. Please chime in with your thoughts below!

Matt Galloway
Matt Galloway

Matt is the founder of SwipeStack, a mobile development company based in London, UK which create apps for clients and also a few of their own. One client's app was featured by Apple in the best apps of 2011, the App Rewind in the newsstand category.

BeerMap is one of their own creations, helping beer drinkers find a great pub near them! You can find him on Twitter.

User Comments

38 Comments

[ 1 , 2 , 3 ]
  • "in Swift, arrays and dictionaries are typed". Yes! but you can still have a non-homogenous array:
    let array: Array = [1, "Cat", 3.124353, 4]

    It may not be the smartest thing to do but I feel it might come in handy sometimes.
    ggamecrazy
  • Matt - I am usually a big fan, but this is not your best article. The updates helped, but your examples are weak in all areas except generics. Your discussion of code-points vs. bytes vs glyphs was confusing and unhelpful. Also, after explaining the difference between .. and ... you proceeded to claim the case statement would "match anything from zero to ten" - it should be nine. I guess this will be a NEW source of errors in Swift.
    MMM
  • Trading "appendFormat:" for the syntactic sugar of "+=" seems like weak tea, especially when on the flip side we now have the supreme ugliness of C++-style template syntax to contend with every time we want to declare a mutable array or dictionary. Ick. Can't wait to start declaring arrays of dictionaries of arrays. And what about parsing deeply-nested JSON responses? Without resorting to using Any or AnyObject, it's unclear just how difficult it would be to obtain a mutable copy of that. One shudders to think.

    Sadly, at first glance (still reading the book) it doesn't appear that Swift supports truly public/private scoping -- that's the only gripe I have with Objective-C at present.

    It remains to be seen whether this is a brilliant move by Apple to open iOS/OS X development to a wider audience of developers...or if they've just shot themselves in the foot by forcing the existing base of Objective-C developers to learn new tricks just to keep up. Let's face it: Obj-C may have first-class support today, but Apple will undoubtedly allow it to atrophy over time.
    JG401
  • My biggest issue so far is organizing the internals of a class. The two main things that are currently missing, but will apparently be coming at some point are:
    1. Something like #pragma mark -- supposed to be replaced with // MARK: - but is not currently showing up in the initial XCode 6 beta
    2. Some way to have private functions in a class

    Without header files and without the ability to have private functions for a class it is difficult to make it clear what the public interface of the class is supposed to be. Comments or protocols may work for some situations, but not really solving the problem. Any suggestions? Or I'll just have some patience. :)
    benplant
  • Great tutorial as always!

    One point of concern on switch statements. It seems that "..." could be
    easily mistaken for "..". I wonder how many times this will get missed in
    testing.

    switch i {
    case 0...5:
    println("The number is 0 through 5, including 5.")
    case 6..10:
    println("The number is 6 to 10, not including 10.")
    default:
    break
    }
    applegal4life
  • The syntax for half closed ranges has already changed to

    ..<

    while a closed range remains

    ...

    The upshot is that Apple is hard at work on the radars. The patch notes for Xcode 6 Beta 3 are full of really neat changes, I am imaging a lot of them being based on feedback. The main thing to keep in mind is that this is really ambitious, and I'm sure there are a lot of engineers at Apple that are busy submitting feedback too.

    One thing I hope they nip straight away is overly terse or "cute" code. I see examples all over the intent of people doing the strangest things that require an entire blog post to explain. This is NOT good design, clever though they may be.

    A good example of something a lot more simple but equally baffling is the popular Singleton pattern at the moment.

    Code: Select all

    let MyClassSharedInstance = MyClass()

    class MyClass () {
         class var sharedIntance:MyClass {
             return MyClassSharedInstance
         }
    }


    It is indeed a lot less typing, but it also leaves us wondering why it works. I think the tradeoff could be that you pay for the convenience of less typing and terse code with having to understand a lot more about compiler/language internals to understand why a three liner does what it does.

    I'm also going to say that I find convenience initializers when subclassing very annoying.

    This now produces an error on super.init, because imageNamed: and texture: are marked "convenience".

    Code: Select all

    class Player:SKSpriteNode {   
        init(texture: SKTexture!) {
            super.init(texture: texture) // Error, "Must call a designated initializer of the superclass "SKSpriteNode"
           
            self.playerSetup()
        }
       
        init(imageNamed name: String!) {
            super.init(imageNamed: name) // Error, "Must call a designated initializer of the superclass "SKSpriteNode"
           
            self.playerSetup()
        }
       
        init(texture: SKTexture!, color: UIColor!, size: CGSize) {
            super.init(texture: texture, color: color, size: size) // This one is fine, unsure why it is the only init we can call super on.
           
            self.playerSetup()
        }
    }


    /rant :] lol

    All that aside, it is a fun language to play with and I'm seeing some really great improvements as each Beta rolls out. Good times.
    jgnovak
  • I think, Swift has really good patterns and ideas.
    But when I read it, it just feels horrible to me!

    One could think full unicode support would be good. But I think it won't take long time until we can't read 30% of all code on the internet, when we can't read Chinese or Cyrillic characters fluently.

    What I really love about Swift, referencing to this article, is that we can use strings and comparisons in switch cases. This is great!

    But the whole typelessness is really confusing and irritating to me and I feel really bad about it.
    I like the fact that arrays in almost all languages can cary any types of objects. So this is appear really imprisoning.

    Swift differs so extremely from Objective-C that we kind of have to learn everything from the scratch. I rarely see languages that are so confusing and hard to read. I can just say that I hate it's syntax and keep on using Objective-C as long as possible.

    Sad fact about Apple and our future:
    In short time we won't be able to program in our good old known and comfortable Objective-C and when this happens I really have to think of if I'll keep on developing for iOS because Objective-C was one of the strongest reasons I started to do so.
    julian-weinert
  • julian-weinert wrote:I think, Swift has really good patterns and ideas.
    But when I read it, it just feels horrible to me!

    One could think full unicode support would be good. But I think it won't take long time until we can't read 30% of all code on the internet, when we can't read Chinese or Cyrillic characters fluently.

    What I really love about Swift, referencing to this article, is that we can use strings and comparisons in switch cases. This is great!

    But the whole typelessness is really confusing and irritating to me and I feel really bad about it.
    I like the fact that arrays in almost all languages can cary any types of objects. So this is appear really imprisoning.

    Swift differs so extremely from Objective-C that we kind of have to learn everything from the scratch. I rarely see languages that are so confusing and hard to read. I can just say that I hate it's syntax and keep on using Objective-C as long as possible.

    Sad fact about Apple and our future:
    In short time we won't be able to program in our good old known and comfortable Objective-C and when this happens I really have to think of if I'll keep on developing for iOS because Objective-C was one of the strongest reasons I started to do so.


    My impression is that you will be able to write in Objective-C for at least a couple more releases of iOS/OSX. Swift is very immature right now, the changelog each beta has brought significant changes to the language. Things are breaking each beta release because it is all still in flux. For iOS8 I would definitely NOT consider Swift to be the default language, it simply isn't ready yet.

    I too love Objective-C. Swift will be good in time, I do feel they should have tried to write a major application in it internally before releasing it to the public. Get it all sorted. That said, there is plenty of time to continue working in Objective-C and learn Swift as the language matures and develop your skills in each. At least this is my current approach.
    Cheers.
    jgnovak
[ 1 , 2 , 3 ]

Other Items of Interest

Ray's Monthly Newsletter

Sign up to receive a monthly newsletter with my favorite dev links, and receive a free epic-length tutorial as a bonus!

Advertise with Us!

Hang Out With Us!

Every month, we have a free live Tech Talk - come hang out with us!


Coming up in September: iOS 8 App Extensions!

Sign Up - September

Our Books

Our Team

Tutorial Team

  • Jake Gundersen
  • Tony Dahbura

... 50 total!

Update Team

... 15 total!

Editorial Team

... 23 total!

Code Team

  • Orta Therox

... 3 total!

Translation Team

  • Myeong Hoon
  • Jesus Guerra
  • Team Tyran

... 33 total!

Subject Matter Experts

  • Richard Casey

... 4 total!