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.
Sign up/Sign in
With a free Kodeco account you can download source code, track your progress, bookmark, personalise your learner profile and more!
Create accountAlready a member of Kodeco? Sign in
Sign up/Sign in
With a free Kodeco account you can download source code, track your progress, bookmark, personalise your learner profile and more!
Create accountAlready a member of Kodeco? Sign in
Contents
Swift Language Highlights: An Objective-C Developer’s Perspective
20 mins
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!