Autoclosures And @autoclosure In Swift 3

What Is an Autoclosure

The first parameter of the fatalError(_:file:line:) function is an autoclosure as indicated by the @autoclosure keyword. But what is an autoclosure?

An autoclosure allows you to pass an expression to a function as an argument.

This definition should get us started. With this in mind, it makes sense that we can pass a string to the fatalError(_:file:line:) function. The string we pass to the function is an expression. The autoclosure wraps it in a closure. But why is that? What is the advantage of wrapping the expression in a closure?

By wrapping the expression in a closure, the function can decide if and when the closure is invoked. The moment the closure is invoked, the expression is evaluated.

As you can see, if used correctly, autoclosures are a powerful concept.

Anatomy of an Autoclosure

There are a few things you need to know about autoclosures. First, an autoclosure doesn’t take any parameters. Second, autoclosures usually return a value, but that isn’t always true. Later in this tutorial, we take a look at an example of an autoclosure that returns Void.

In Swift 3, closures are non-escaping by default. If the autoclosure of a function needs to be escaping, you need to prefix the @autoclosure attribute with the @escaping attribute.

When to Use Autoclosures

The benefit of autoclosures is that you don’t need to wrap the expression in curly braces. That is what you would need to do if you were to pass in a closure instead of an autoclosure.

In the below example, you can see that the fatalError(_:file:line:) function can accept an expression as its first argument thanks to autoclosures.

fatalError(response.error + " " + response.error)

Examples

Before I show you two examples of autoclosures, I need to emphasize that you won’t find yourself using autoclosures very often. This is a quote from The Swift Programming Language.

It’s common to call functions that take autoclosures, but it’s not common to implement that kind of function.

Despite the above quote, it is important that you understand the concept underlying autoclosures even though you won’t be implementing functions that use them very often.

Unbox

The first example I would like to show you is taken from one of my favorite libraries, Unbox, a lightweight JSON decoder created by John Sundell.

The following method is used to extract the value for a particular key or key path. What is important is that the method takes an autoclosure as its third parameter. The autoclosure returns the fallback value if no value for a particular key or key path is found.

func resolveRequiredValueForKey<R>(key: String, isKeyPath: Bool, fallbackValue: @autoclosure () -> R, transform: (T) -> R?) -> R {
    if let value = self.resolveOptionalValueForKey(key: key, isKeyPath: isKeyPath, transform: transform) {
        return value
    }
    
    self.unboxer.failForInvalidValue(invalidValue: self.unboxer.dictionary[key], forKey: key)
    
    return fallbackValue()
}

The implementation isn’t too complicated. What is of interest to us is the last line of the method.

return fallbackValue()

If no value was found, the autoclosure is invoked and the fallback value, which could be the result of an expression, is returned.

View Animations

The second example is a clever concept created by none other than Erica Sadun. In a blog post, Erica writes that autoclosures can help make the UIView API for animations easier and more elegant.

She wants to go from this:

UIView.animate(withDuration: 2.5) { 
    self.view.backgroundColor = .orange
}

To this:

UIView.animate(withDuration: 2.5, self.view.backgroundColor = .orange)

Notice that we don’t need to wrap the expression in a closure. How does this work?

To make this possible, we create an extension for UIView and implement a class method that accepts an autoclosure.

extension UIView {

    class func animate(withDuration duration: TimeInterval, _ animations: @escaping @autoclosure () -> Void) {
        UIView.animate(withDuration: duration, animations: animations)
    }

}

Remember that closure parameters, including autoclosures, are non-escaping by default. This means we need to prefix the @autoclosure attribute with the @escaping attribute.

What do you think? Doesn’t this look better?

UIView.animate(withDuration: 2.5, self.view.backgroundColor = .orange)

Should You Use Autoclosures

Apple emphasizes that autoclosures should not be overused. Not only is it hard to find valid use cases, autoclosures also make it harder to understand the program flow. Don’t overcomplicate your code with autoclosures if there is an easier alternative.

Advertisements
Autoclosures And @autoclosure In Swift 3

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s