8️⃣Closures

Fall 2024 | Peter Bidoshi

Now that we have learned a bit about functions and how they work, we will discuss something more general, called Closures.

Closures are self-contained blocks of functionality that can be passed around and utilized within your code. The most simple example of closure is a function, but there exist others that are very important to understand before exploring future topics.

Imagine we have a function, which is a type of closure:

func getData() -> String {
    return "Fetched Data"
}

One way we could use the return of this function is as follows:

// Same code from above
func getData() -> String {
    return "Fetched Data"
}

// Save the string returned from the function in a variable
var data = getData()
// Print the variable
print(data)

Pretty simple right? However, we could also achieve this differently by using a Closure Expression. Closure expressions allow us to pass a piece of code as an argument to a function. To incorporate this, we need to change the method signature of our "getData" function.

// This is the syntax to indicate that this 
// function takes in another function!
// "handler" is just the name of the variable,
// and we can call it whatever we want
func getData(handler: (String) -> Void) {
    handler("Fetched Data")
}

What is going on here? Let's first talk about the method signature. You will notice we added an argument to the function that is called "handler". This argument has a type of "(String) -> Void". This represents a function that needs to take in a String and return nothing (void). The "getData" function then passes the String "Fetched Data" into that function. Now, let's change the other part of our code.

// Same function from above
func getData(handler: (String) -> Void) {
    handler("Fetched Data")
}

func printData(data: String) -> Void {
    print(data)
}

getData(handler: printData)

Here, we created a function called "printData" that takes a string and returns nothing (void). Notice that this signature (string -> void) is the same signature as our "handler" argument. We then pass the "printData" function as an argument to our "getData" function. with this, our "getData" function will pass the String "Fetched Data" to the "printData" function that was passed to it. This will achieve the same result as the original code.

However, this can be simplified! Instead of writing out a whole other function, let's provide the code right into the function! This is what a Closure Expression is all about.

// Same function from above
func getData(handler: (String) -> Void) {
    handler("Fetched Data")
}

getData(handler: { data in
    print(data)
})

Nice! "data in" is a tricky syntax that you will have to remember. The "data" represents a String that will be returned to the handler after the getData function is run. Swift is smart, so it can automatically infer the type of "data" as a String due to the getData method signature.

Last updated