We aren’t done with closures quite yet. We learned last time about how to write and compress closure expressions in the post Closure Expressions in Swift. Now, how about we talk about making functions that take them as parameters or return them! I am still very new to closures, so I do not fully know the power that these hold, so my examples will only show the tip of the iceberg. When I understand more, I will definitely come back and share, but for the moment, I have to know how to get started with them, so let’s see how that goes.
Closure Type
So, when you want to take a parameter to a function, or return a value, you normally have to specify a type. So, how do you specify a the “type” of a closure when doing so? This may look somewhat similar to my post Functions in Swift: Parameters and Return Types, but as we said, functions are a type of closure.
Firstly, the simplest type of a closure is this:
()->()
It is a closure that takes no parameters, and returns nothing, so if we wanted to take this as a parameter we would use it like so:
func functionToRunAClosure(someClosure: () -> ()) -> String { someClosure() return "I ran the closure!" } functionToRunAClosure(someClosure: {print("I'm printing something!")})
Here we wrote a function that runs a closure given to it, and then we handed that function a closure expression that just printed something to the console. After running the closure, we just returned a string saying that we ran the closure. In this case I ignored the return, but I just wanted to show what the syntax for a function that takes a closure but has a more standard type of return.
If we wanted to return a closure that takes no arguments and returns nothing itself, we would do something like this:
func makeClosureToPrintExclamationString(someString: String) -> ()->() { let result = { print("\(someString)!!") } return result } let yellAboutAwesomeSwiftSite = makeClosureToPrintExclamationString(someString: "Coding Explorer is awesome") yellAboutAwesomeSwiftSite() //Output: "Coding Explorer is awesome!!"
The syntax does look a bit odd at the end with the ” -> ()->() “. It is just saying that it returns a closure that takes no arguments and returns nothing. Remember that in Swift, a function that returns nothing really returns an empty tuple? More precisely, it returns a closure that has an empty tuple’s-worth of arguments, and returns an empty tuple.
So, how do you make your closures actually take arguments or return values themselves? Just think of those tuples like you think of a normal function prototype in Swift. The leftmost parenthesis holds the types of your parameters, and the rightmost parenthesis holds the return type (or types if it returns a full-fledged tuple). If the return type is just a single type (not a tuple itself), you don’t have to surround it with parenthesis.
func makeClosureThatReturnsStringAboutAwesomeGroup() -> ()->String { let result = { () -> String in return "SwiftLDN is Awesome!" } return result }
In this case, the function returns a closure that returns a Swift String, and the closure is created by a closure expression that just returns a Swift String. This function is hard-coded to return the same Swift String each time. However, what if I wanted to make a function that takes a String, and returns a closure that talks about how awesome that String is? In that case, we would have to take a String and somehow keep a reference to it after the function that makes the closure goes out of scope. How is that done?
Capturing Values in Closures
In Swift, you do that by capturing values when you make your closure, of course! Okay, that isn’t really obvious, but that’s what I’m here to help with. So, let us make the awesomeMaker function, that returns a closure that talks about how awesome some String you pass into awesomeMaker is. It would look something like this:
func awesomeMaker(subject: String) -> ()->String { let result = { () -> String in return "\(subject) is Awesome!" } return result } let codingInSwiftStatement = awesomeMaker(subject: "CodingInSwift") print(codingInSwiftStatement()) //Output: "CodingInSwift is Awesome!"
Now, we can state that anything is awesome by just passing its name to awesomeMaker. Whenever we want to state it, we just insert the call to the closure we made about CodingInSwift, in this case.
If we use awesomeMaker again with another string, it has no effect on the first one we made. It will capture a new value that we send it, and each awesomeMaker-made closure says what it was supposed to, like this:
let soSoSwiftStatement = awesomeMaker(subject: "SoSoSwift") print(codingInSwiftStatement()) //Output: "CodingInSwift is Awesome!" print(soSoSwiftStatement()) //Output: "SoSoSwift is Awesome!"
But Closures are Actually Reference Types?
There is one somewhat confusing thing about this. While each closure created by awesomeMaker is independent, if we created another variable and assigned codingInSwiftStatement to it, they are actually pointing to the same closure. In this case, that doesn’t change much since nothing changes between calls to the closure, but what if we had a closure that would yell a String louder each time, like this:
func yellSomethingLouder(subject: String) -> ()->String { var exclamationPoints = 0 let result = { () -> String in exclamationPoints += 1 let workingString = subject + String(repeating: "!", count: exclamationPoints) return workingString } return result }
See what happens when we use this, and assign it to a new variable below:
let yellComputer = yellSomethingLouder(subject: "Computer") print(yellComputer()) //Output: "Computer!" print(yellComputer()) //Output: "Computer!!" print(yellComputer()) //Output: "Computer!!!" let yellAtOtherComputer = yellComputer print(yellAtOtherComputer()) //Output: "Computer!!!!"
When we just set yellAtOtherComputer to the value of yellComputer, it kept counting up on those exclamation points! Much like classes, in Swift, closures are actually reference types. We just told the compiler to set what yellAtOtherComputer to point to wherever yellComputer is pointing. If it was a value type, it would be copied and would be a separate instance, but instead they are pointing to the same instance.
I was a bit curious at what happened if I create a new closure with the exact same literal, let’s see what happens:
let yellAtThirdComputer = yellSomethingLouder(subject: "Computer") print(yellAtThirdComputer()) //Output: "Computer!" print(yellComputer()) //Output: "Computer!!!!!"
I actually wasn’t entirely sure what would happen before I tried this. This seemed most likely, but there was a chance it would optimize and just tie it to the same closure since they were made via the exact same means. This does make sense though. In our case, that optimization might seem like a good idea, but what if we wanted to append the date this closure was created for some reason? Then it wouldn’t make sense to tie them to the same closure. Since the compiler doesn’t know if I’m doing something simple (like the above), or like that date idea, it has to assume you are making a new closure, since you called yellSomethingLouder again to make a new closure.
In case you thought yellAtOtherComputer did in fact copy, and just keep counting from 4 exclamation points, you can see that when we ran yellComputer again, it had 5 exclamation points. If yellAtOtherComputer had copied, then yellComputer would not have been incremented yet, and would only have 4 exclamation points above.
Conclusion
This post used closure expressions very often to simplify writing closures for the examples. If you missed my previous post about them, you might want to go back to Closure Expressions in Swift, for a better explanation on how to use them and their syntax. I am just beginning to understand how useful closures can be, as evidenced by how silly these examples were. These examples try to show the syntax with as little fluff about other concepts as I could, but that did mean we had closures that just called print, when one would normally just call print themselves.
I will definitely come back as I learn more about closures in Swift and share more about what they can do, but I do think awesomeMaker and yellSomethingLouder are beginning to show what they are capable of. They help encapsulate functionality in a (potentially) terser way, so someone can call a closure made by awesomeMaker instead of calling a more general use function each time, supplying the same parameter each time. In other words, one can call iPhoneIsAwesome (made by awesomeMaker) instead of saySomethingIsAwesome(“iPhone”) each time. Then if you want to change the subject, you just change it on the awesomeMaker line, instead of on each function call to saySomethingIsAwesome.
I hope you found this article helpful. If you did, please don’t hesitate to share this post on Twitter or your social media of choice, every share helps. Of course, if you have any questions, don’t hesitate to contact me on the Contact Page, or on Twitter @CodingExplorer, and I’ll see what I can do. Thanks!