Swift: Syntax Cheat Codes

↑ ↑ ↓ ↓ ← → ← → B A

Whether it’s your first language or you’re coming from Objective-C, the Swift language is literally the bomb-diggity in terms of learning to write code, but it can be a bit intimidating if you’re unfamiliar with the syntax enhancements that it’s brought to the table. This post is going to go through and detail some the common syntax you will encounter when reading about, and learning to write concise code with Swift.

Closures

() -> Void

Othewise known as “unnamed functions”, or “blocks” in C and Objective-C; a closure is pretty much a miniature function which is able to be passed around as a value, usually in function arguments but also as variables as well.

If you have previous experience with iOS development, you most likely would have interacted with these when using the UIView animation API:

class func animate(withDuration duration: NSTimeInterval, animations: @escaping () -> Void)

The animations: argument is where we would pass in our animation code, here’s what it looks like in code:

UIView.animate(withDuration: 10.0, animations: {
button.alpha = 0
})

the animationWithDuration: function would then use our closure and do some awesome behind-the-scenes magic to make our button slowly fade to an alpha of 0 (invisible).

Trailing closures

UIView.animate(withDuration: 10.0) { 
button.alpha = 0
}

Swift has this feature as a way to reduce unnecessary syntax. If we look at the code above, those of you with a keen eye would have noticed that it’s the same API I mentioned in the closure example above, except it’s syntax has been reduced.

Because the animate API function arguments end with a closure, it’s called a trailing closure, hence the name. A trailing closure allows us to omit the final argument name completely and move it out of the function argument parentheses as well, which in turn produces more elegant and concise code. Both code examples below are the same, except the latter uses trailing closure syntax:

func say(_ message: String, completion: @escaping () -> Void) {
print(message)
completion()
}
...
say("Hello", completion: {
// prints: "Hello"
// Do some other stuff
})
say("Hello") {
// prints: "Hello"
// Do some other stuff
}

Type Alias

typealias

Type aliases are a handy little tool that give us the ability to prevent us from repeating ourselves when we’re crunching code. Let’s say we have a function that takes a closure as it’s argument:

func dance(do: (Int, String, Double) -> (Int, String, Double)) { }

At first this is straight forward, but what if we to pass this closure around to other functions? We’d have to remember the signature of the closure and make sure it’s consistent between all of the places it’s passed and used otherwise the compiler would report an error because there’s a misaligned signature in the chain.

func dance(do: (Int, String, Double) -> (Int, String, Double)) { }
func sing(do: (Int, String, Double) -> (Int, String, Double)) { }
func act(do: (Int, String, Double) -> (Int, String, Double)) { }

The problem arises when we change something with the closure’s signature, like if we swap the order of the arguments or return types. Then we have to go through all of the places we used it and update them one by one. This is where we would create a typealias instead.

typealias TripleThreat = (Int, String, Double) -> (Int, String, Double)
...
func dance(dance: TripleThreat) { }
func act(act: TripleThreat) { }
func sing(sing: TripleThreat) { }

There we go, now we’ve removed all repeated code and if we want to change the closure, all we have to do is edit the typealias we created.

Famous Type Aliases

typealias Void = ()
typealias NSTimeInterval = Double

Shorthand argument names

$0, $1, $2...

If a closure has one or more arguments, Swift allows us to assign the arguments variable names:

func say(_ message: String, completion: (_ goodbye: String) -> Void) {
print(message)
completion("Goodbye")
}
...
say("Hi") { (goodbye: String) -> Void in
print(goodbye)
}
// prints: "Hi"
// prints: "Goodbye"

In this example, our trailing closure is defined to have one argument named goodbye: which is of type String, Xcode will automatically place it inside a tuple followed by an in to signify the return type and end of the arguments, and then we do our code on the next line. However, when we know our closures are small in scope this verbosity can be considered unnecessary. Let’s start disecting and reducing this code to it’s absolute minimum.

(goodbye: String) -> Void in

None of code is actually necessary because we can simply just use shorthand argument names.

say("Hi") { print($0) }
// prints: "Hi"
// prints: "Goodbye"

As you can see, we’ve omitted the goodbye: argument name, the Void return type declaration, and the in suffix that follows it because we don’t use the argument names. Each argument is named by the order of which they were declared in the closure and because it’s so simple, we can also just place it all within a single line.

If we had more than one argument inside the closure, the shorthand argument names would simply increment for each following argument.

(goodbye: String, name: String, age: Int) -> Void in
// $0: goodbye
// $1: name
// $2: age

Returning Self

-> Self

When Swift 2.0 was released it brought a bunch of functionality out of the box such as map and flatMap, but what’s even cooler is that those functions showed us that we can chain a set of functions with dot notation and execute them in a sequential order:

[1, 2, 3, nil, 5]
.flatMap { $0 } // remove nils
.filter { $0 < 3 } // filter numbers that are greater than 2
.map { $0 * 100 } // multiply each value by 100
// [100, 200]

How cool is that?! It’s so elegant, easy to read and understandable, we should totes be putting it in more places!

Let’s say we create an extension for String where we perform a bunch of operations on the string itself, instead of making the functions return Void, we return Self:

// extension UIView
func with(backgroundColor: UIColor) -> Self {
backgroundColor = color
return self
}
func with(cornerRadius: CGFloat) -> Self {
layer.cornerRadius = 3
return self
}
...
let view = UIView(frame: CGRect(x: 0, y: 0, width: 10, height: 10))
.with(backgroundColor: .black)
.with(cornerRadius: 3)
Update:
In the original post I wrote that returning Self was a Swift 3 feature. I have since been informed by Abdullah Abanm that returning Self only applies to reference types. My apologies for the confusion.

Summary

Whether you’re writing new lines or reading old ones, you will discover parts of your code where the things you’ve learnt written here will apply. Xcode isn’t always correct when it comes to autocomplete so you should always question the autocompleted code it produces, as well as the code you write yourself.


As always I’ve provided a playground on GitHub for you to check out as well as a Gist incase you aren’t in front of Xcode.

If you like what you’ve read today you can check our my other articles or want to get in touch, please send me a tweet or follow me on Twitter, it really makes my day. I also organise Playgrounds Conference in Melbourne, Australia and would to see you at the next event.