Swift Closures — Everyday Gems Part 1 of 2
Closures are self-contained blocks of functionality that can be passed around and used in your code. Closures in Swift are similar to blocks in C and Objective-C and to lambdas in other programming languages.
In this post, I will cover creating closures and simplifying them to their most succinct form. In Part 2 (next week’s post), we will cover more advanced features of closures such as what it means to capture values.
Defining and Creating Closures
Closures can be defined in three ways:
- Global functions
- Nested Functions
- Closure expressions
This article will focus on Nested and Closure Expressions.
When we define a function inside the body of another function, we are really creating a nested function. Nested functions let us control access by hiding their existence from the outside world by default. However, the inner function(s) is controlled by its enclosing function, allowing access to any values passed to or contained in the body of the enclosing function. In addition, the enclosing function could return any nested function as a return value. Think of the Factory pattern from the Gang of Four guys on design patterns as an example of where a nested function might apply.
Nested functions are closures that have a name and can capture values from their enclosing function.
Let’s look at an example to make this concrete. Below, I created a function applySalesTaxForState that contains several nested functions to apply the appropriate sales for a provided state. The return value is a function that we can call to calculate the sales tax on an amount passed as a parameter. In my case, I want to calculate the sales tax on a $100 purchase in Pennsylvania.
Each of the “apply” methods that are enclosed by the applySalesTaxForState method are considered nested functions. Once I have assigned the return of our enclosing function, I call it by referencing the variable name taxForPA and passing it an amount.
Closure Expressions are unnamed (anonymous) code blocks written in a concise format. Closure expressions are great because you can to define an immediately useful function without giving it a name to perform a focused task that is really only useful to the receiver of the closure. When it feels like a waste to create a function that is only needed for one spot, you consider creating a closure expression instead. More experienced coders may accuse me of over simplifying the definition, but I’m also attempting to breakdown a complex concept in terms that newbie developers can grasp. Feel free to leave me comments on better ways of describing closure expressions in the response section. I believe that newcomers to Swift development could benefit from and appreciate additional definitions from other developer perspectives.
Closure expressions are a way to write inline closures in a brief, focused syntax. Closure expressions provide several syntax optimizations for writing closures in a shortened form without loss of clarity or intent.
Now that we have a definition, let’s move on to creating a closure expression. See below for the closure expression syntax:
It is important to note that the parameter declaration, return type and expression body are all defined inside of the curly braces with the expression body starting after the in keyword. Let’s put this to use and create a closure expression. Array filtering is a common need when working with lists of objects. Apple added a filter method to allow you to provide a closure with your criteria for filtering. Let’s create an array of people and a closure expression to return a subset of the array using the filter method on an array.
In our example above, we have created a closure expression that is passed to our filter method to find elements of our array that contain the string “Ryan”. Using a closure expression makes sense since we really don’t need to search for people named “Ryan” outside of filtering our people array.
Simplifying Closure Syntax
The code in the last section is good, but we can do better. Let’s look at a few ways to reduce the amount of code we have to write when creating a closure.
Inferring parameters and return types
One of the nice features of Swift is its ability to infer types. We can use this ability to simplify our closure syntax. The Swift compiler can infer types passed as parameters or returned from functions when using a closure expression. Therefore, you don’t have to implicitly set a parameter’s type or define return types. You certainly can add them if you feel it makes the code more readable, but they really are optional.
In the code sample above, I first demonstrate writing the closure expression using parameter and return types. The next statement shows a closure omitting the types as they are inferred by the Swift compiler. Finally, the last statement demonstrates creating a closure inline, allowing us to omit the return keyword since it is inferred by the compiler. Hopefully, you can begin to see how you can simplify a closure to make it more concise.
Using Shorthand syntax
Another nice feature Swift gives us is automatic parameter naming for inline closure expressions. What this means is that we can leave off the parameter list entirely and Swift will name parameters for us using the following pattern: $0, $1, $2, etc to represent the first, second, third argument in a parameter list. In fact, you can also drop the in keyword when using the shorthand syntax. Let’s revisit our example from above and show how we could apply it to our closure statement.
In our final version of our closure expression, we are able to omit the parameter and return type along with the in and return keywords. By refering to each name of the array as $0, we are using Swift’s automatic parameter naming to shorten the amount of code we have to write. That’s pretty concise syntax if you ask me!
We can also reduce the amount of syntax we have to write in cases where we are passing a closure expression as the final argument to a method.
A trailing closure is a closure expression that is written outside of (and after) the parentheses of the function call it supports
Trailing closures are really useful when you want to pass a long closure expression to a method as its last argument. If the closure expression is the only argument, you can also discard the () following the function’s name. Continuing with our previous example, we could rewrite our closure expression as follows:
Closures are used everywhere in Swift! You might need to work through a bunch of examples to feel comfortable using them. Trust me, you don’t want to skip over this feature. Fire up Xcode and play around with them soon. You can find a copy of the Playground that I used to create the samples here. My next post will extend our topic on closures and will even give you hints on helping the Swift compiler optimize your code when using them.
If you find this post helpful, recommend it for others to read. Please visit me at www.gittielabs.com and subscribe to my RSS feed so that you won’t miss a post. I’m also putting together a video course to teach Swift development and could use your input on topics that you feel would be helpful. Thanks for reading!