With the WWDC 2015 came many improvements with Swift — not just making writing code much more beautiful, but also better error handling, and new optimisations that will make your app run even faster.
Back to Basics
So Swift 2.0 is here with a lot of new features. If you already completed the Swift programming ebook that Apple released last year, then reading it again (or at least the updated chapters) is not a very bad idea.
Enums
Enums in Swift are actually algebraic data types.
Going back to the fundamentals of Swift, some of the updates that is included was the `enum`. Fundamentals are about refining the core behaviour of the language and how it works together. Enums can now be printed to the console and show the value instead of just (enum value).
enum Cars {
case Volvo, Ferrari, AstonMartin, Audi
}
let c = Cars.AstonMartin
print(c)
Before Swift 2.0, this code will most likely printout a vague result on the console that looks something like:
(enum value)
After Swift 2.0, this code will now display the value on the console.
Cars.AstonMartin
Enum Defaults
As of Xcode7b3, enums with string raw type without an explicit raw value will use the enum
‘s name by default.
enum Cars : String {
case Volvo, Ferrari, AstonMartin, Audi
}
is equivalent to:
enum Cars : String {
case Volvo = 'Volvo'
case Ferrari = 'Ferrari'
case AstonMartin = 'AstonMartin'
case Audi = 'Audi'
}
Associated Values
Another new feature for the Enums is that it can now support multiple types which makes it even more powerful.
enum Car<T1, T2> {
case Volkswagen(T1)
case Audi(T2)
}
Before 2.0, the code above will give a very depressing error that looks like:
Playground execution failed:
swift2-0.swift:15:6: error: unimplemented ir generation feature non-fixed multi-payload enum layout
enum Car<T1, T2> {
^
Now in Swift 2.0, it just works.
Recursion
Recursive algebraic data types are a powerful feature by many languages but is now also available in Swift 2.0’s enums.
So what is a recursive data type?
Recursive data type is a data type for values that may contain other values of the same type. An important application of recursion in computer science is in defining dynamic data structures such as Arrays. Recursive data structures can dynamically grow to a theoretically infinite size in response to runtime requirements.
On another language, like Java for example, recursive data types can be similar to:
class List<E> {
E value;
List<E> next;
}
On Swift’s enum
, you can write something like:
enum Car<T> {
case Brand(T)
case Truck(Car, Car)
}
Prior to Swift 2.0, you will get an error similar to:
Recursive value type 'Cars<T>' is not allowed
Now, all you have to do is mark the case
as indirect
and you’re ready to go.
enum Car<T> {
case Brand(T)
indirect case Truck(Car, Car)
}
do-while
Statement
The do-while
loop has been renamed to repeat-while
loop. The reason for this change is clarity, having do
and do-while
loop at the same time may be ambiguous, hence the change of do-while
to repeat-while
.
The general form of a repeat-while
loop:
repeat {
i += 1
} while i < 10
Option Sets
In Swift, option sets are represented by structures conforming to the OptionSetType
protocol, with static variable for each option value.
Back in Swift 1, option sets are written like Objective-C does:
// assigning an option set
viewAnimationOptions = .Repeat | .CurveEaseIn | .TransitionCurlUp
// freeing it up
viewAnimationOptions = nil
// and checking
if viewAnimationOptions & .TransitionCurlUp != nil {
}
With Swift 2.0, option sets are now formed with square brackets:
// assigning an option set
viewAnimationOptions = [.Repeat, .CurveEaseIn, .TransitionCurlUp]
// freeing it up
viewAnimationOptions = []
// and checking
if viewAnimationOptions.contains(.TransitionCurlUp) {
}
Defining an Option Set
Defining an option set is very easy, all you have to do is define a struct
type and have it conform to the new type OptionSetType
.
struct CarAccessories : OptionSetType {
let rawValue: Int
static let Mats = CarAccessories(rawValue: 1)
static let Seat = CarAccessories(rawValue: 2)
static let Wheel = CarAccessories(rawValue: 3)
...
static let Shades = CarAccessories(rawValue: 11)
}
With just this simple definition you will get all the syntax for option sets, and it doesn’t even require you to do anything more. It’s all thanks to the Default Implementation and Protocols provided by option set type.
car.accessories = []
car.accessories = [.Mats]
car.accessories = [.Seat, .Wheel]
if car.accessories.contains(.Shades) {
}
Pattern Matching
Pattern matching is a staple of functional languages. For those who don’t know, Swift was built primarily as object-oriented language, like Objective-C. However, there are many features of functional style programming that the designers of Swift decided to include, pattern matching in particular.
guard
Statement
The way to look at the guard
keyword is that it does a check and then exits if the condition evaluates to false. Using guard
helps, yet again, reduce the possibility of having a ‘Pyramid of Doom’ in your source code.
func checkOut(car: AnyObject) -> Either<Cars, String> {
guard let name = car["name"] as? String else {
return .Second("missing name")
}
}
switch-case
Statement
A switch
statement considers a value and compares it against several possible matching patterns. The switch statement will execute the block of code based on the very first pattern that it matches successfully.
switch car() {
case .Accessories(let accessory) where kph == 0 && handBrake == enabled:
addAccessory(accessory)
default:
break
}
It is similar to if-elseif-else
statements that in some languages, (e.g. PHP) you can use strings for a switch’s case and for other people, switch
may be their favourite feature.
Inline if
With Swift 2.0, this feature just became more powerful that it can now be used with inline if statements.
if case .Accessories(let accessory) = .car() where kph == 0 && handBrake == enabled {
addAccessory(accessory)
}
With the new syntax above, the power of pattern matching has been added to the other control statements in the language. The example above has been written with the new (and shiny) if-case
statement that checks, matches, and bind variable names in just one line.
But wait, there’s more…
Inline for
The if
statement is not the only control statement that has been updated, for-in
loop also now supports the case inline. Have you gone to a for-in
loop without even checking a variable? Most of the time, you do check.
The common code you will first see inside a for
loop is an if
statement to check if the variable is not empty.
for car in myCars {
if car != "" {
removeAccessories(car)
}
}
Before Swift 2.0, a new language construct has been introduced, a simple Boolean filter right inline of the for-in
statement.
for car in myCars where car != "" {
removeAccessories(car)
}
With Swift 2.0, you can now use the power of pattern matching right in your for
loop to give you powerful conditions.
for case .Seat(let car) in CarAccessories {
removeAccessories(car)
}
Protocol Extensions
When Swift was released, you can use extensions to add new functionalities to an existing class, structure, or enumeration. This is a bit similar to Objective-C’s Class Category. It is a very powerful feature that I can take an arbitrary type such as an Array
and add my own methods.
extension Array {
func countCars(match: Element -> Bool) -> Int {
var n = 0
for object in self where match(object) {
n++
}
return n
}
}
As you can see, the code above does not work with Array
alone, but can also work for any arbitrary collection. With Swift 1 though, making the code above generic over an arbitrary collection, you will need to write a code similar to the code below:
func countCars<T: CollectionType>(collection: T, match: T.Generator.Element -> Bool) -> Int {
var n = 0
for object in self where match(object) {
n++
}
return n
}
That code reeks, a lot of extra syntax, a lot of brackets, and a lot of extra code just to make the code generic — it’s not even optimal anymore that it doesn’t feel like it’s part of the type. Since it’s not part of the type, in Array
for example, it won’t show up in any lists of the functionalities — so bye bye code completion.
With Swift 2.0, extension is now allowed for protocol types. With protocol extensions, you can now add functions or properties to all class that conform to a certain protocol. This is a very useful feature when you want to extend a protocol’s functionality.
So, instead of extending an Array
with a generic method, we can now extend the Array
‘s protocol CollectionType
to automatically add the method everywhere that implements the CollectionType
protocol — not just from the standard library but also the types you own that conforms to the CollectionType
protocol.
extension CollectionType {
func countCars(match: Element -> Bool) -> Int {
var n = 0
for object in self where match(object) {
n++
}
return n
}
}
Availability Checking
Many developer knows the struggle when building an app that supports multiple OSes. One regular problem that developers encounter is the API availability — UIStackView
for example. Now, if I want to adopt this in my own app, I would write something like this:
var stackView = UIStackView(arrangedSubView: views)
if you try to use UIStackView
prior to iOS 9, your app will crash. Back in Objective-C, developers would write code similar to:
if NSClassFromString(@"UIStackView") != nil
It will take a class from the specified string, UIStackView
in our example, and check if it’s not nil
. However, this method only works with classes, if we try to access a modern API from an old OS, then we will get an error.
if ([self getWhite:&white alpha:&alpha]) {
return [UIColor colorWithWhite:white alpha:alpha];
}
-[UIColor getWhite:alpha:]: unrecognized selector sent to instance 0x000000000
One example is UIKit
‘s UIColor
, it is available in iOS 2.0 and later but the method for retrieving color information getWhite(_:alpha:)
is only available for iOS 5.0 and later. There are other codes that developers have used before but the fairly common one is using respondsToSelector
.
if colour.respondsToSelector("getWhite") {
let convertible = colour.getWhite(0.83, alpha: 1.0)
}
If you’ve been an Objective-C developer for some time you probably heard of the method respondsToSelector:
and similar hacks like that that we have been using in order to check the availability of specific APIs.
With Swift 2.0, availability checking will be simple and straightforward, a new condition is added and is called #available
(pound available, not hashtag, let’s make that clear).
if #available(iOS 5, \*) {
let convertible = colour.getWhite(0.83, alpha: 1.0)
}
if #available(iOS 9, \*) {
var stackView = UIStackView(arrangedSubView: views)
}
With #available
, you list out the OS versions (comma separated) you want to make sure you test for and the star (or asterisk) at the end to make sure new OSes are included.
The
guard
statement can be used for availability checking
The star is required and indicates that the #available
condition is executed on the minimum deployment target for any platform that aren’t included in the list of platforms.
Error Handling
Error handling is just another update for Swift 2.0 that may result to a more safe, robust, and beautiful code syntax. Out of the list of new features and improvements, one that will probably impact your Swift 1.x code the most is error handling.
While writing a program, things will go wrong. When a function fails, an old method has been deprecated and a new list of APIs added, or a missing bang operator, it’s always good to catch the error, understand why it fails, and try to handle the errors properly.
try-catch
Statement
This is a common feature of other languages but something that is notably missing from Swift. With Swift 2.0, error handling has been updated. Instead of NSError object and double pointers, we’re moving to a new system that looks similar to exception handling.
func accelerate(inout error: NSError?) -> Bool {
if car.hasGasAndReturnError(&error) {
return false
}
removeBrake()
return true
}
The code above is not really something we want, a lot of boilerplate, the real function of the code is hidden, and a lot of code, that in reality, should only be 2 lines long.
With Swift 2.0, error are now represented by values of types conforming to the ErrorType
protocol. In this case, we can create an enumeration that adopts ErrorType
to model the error conditions.
enum CarErrors: ErrorType {
case NoFuel
case FlatTyre
case OilLeak
case RadiatorLeak
case LowBattery
case SqueakyBelts
}
To use the error, a method should be declared with a throws
keyword if the method can throw one or several errors.
func startCar(options: CarOptions) throws {
guard hasFuel else {
throw CarErrors.NoFuel
}
guard noFlatTyres else {
throw CarErrors.FlatTyre
}
guard noLeaks(options.oil) else {
throw CarErrors.OilLeak
}
guard noLeaks(options.radiator) else {
throw CarErrors.RadiatorLeak
}
guard hasBattery else {
throw CarErrors.LowBattery
}
guard options.beltsQuality == 100 else {
throw CarErrors.SqueakyBelts
}
}
do
Statement
The do
statement is used to introduce a new scope of error handling and can optionally contain one or more catch
clauses. The catch
clauses of the new do
statement contains patterns that will match against defined error conditions.
Variables and constants declared in the scope of a do
statement can only be accessed within that scope.
do {
let result = try car.startCar()
} catch CarErrors.NoFuel {
} catch CarErrors.FlatTyre {
} catch CarErrors.OilLeak {
} catch CarErrors.RadiatorLeak {
} catch CarErrors.LowBattery {
} catch CarErrors.SqueakyBelts {
} catch {
// something went wrong
}
The do
statement and switch
statement has some similarities in a sense that the compiler attempts to infer whether the catch
clauses are exhaustive, and you can use pattern matching to capture the thrown error.
To ensure that an error is going to get handled properly, use a catch
clause with a pattern that matches all errors. If a catch
clause does not specify a pattern, the catch
clause matches and binds any error to a local constant named error
.
defer
Statement
Now that try-catch
is available is Swift 2.0, I still feel that there’s something left out. Some languages have a concept of try-finally
which lets you tell the compiler to execute this block of code no matter what happens. Swift 2.0 introduces its own take on this requirement and is called the defer
statement.
func goToWork() {
defer {
closeCarDoors()
}
do {
let result = try car.startCar()
} catch CarErrors.NoFuel {
} catch CarErrors.FlatTyre {
} catch CarErrors.OilLeak {
} catch CarErrors.RadiatorLeak {
} catch CarErrors.LowBattery {
} catch CarErrors.SqueakyBelts {
} catch {
// something went wrong
}
// closeCarDoors will be called here, at the end of the scope.
}
A defer
statement is used for executing a code block just before transferring program control outside its scope. This lets you do any necessary cleanup that should be performed regardless of whether an error occurred. An example is to always close the doors of the car regardless if start car
is executed successfully or not.
General Updates
A few more updates with Swift 2.0:
- Mutability Warnings – As you may know, Swift offers 2 kinds of properties, constants (
let
) and variables (var
) and Swift prefer that you declare properties as constants rather than variables. If you accidentally made something a variable by accident, or if you thought you might need to change it later, but never actually do, Xcode will throw a mutability warning that will let you know that a variable can be declared as a constant. - Updated syntax –
println
has been removed only after a year andprint
has been updated to have a default second boolean argument set totrue
on whether to print a newline or not. There are also changes to protocol such asPrintable
is now called CustomStringConvertible - Xcode Migrator – With syntax updates, a feature was added to Xcode called Migrator. The Swift migrator will help bring things up to the latest standards and syntax changes.
- OPEN SOURCE – Yes, you read that right. Swift 2.0 is going to be open sourced by the end of 2015. Likely this will lead to Swift being used for more than just iOS development. This means that Swift
- Rich Comments – The syntax is a variant of Markdown that has been brought to Xcode playground’s comment documentation. This means you can build rich, beautiful documentation comments that shows up in your API.
Closing Remarks
As I mentioned in this article, it’s not bad to revisit the Swift programming ebook by Apple as it is constantly being updated for new releases. I would also suggest visiting Apple’s developer portal and download the Xcode 7 beta.
Leave a Reply