Swift Basics: Closures

Closures are when you add functions, etc, blocks of code to a variable declaration. It can take in parameters and can have return values.
Here is a simple closure:
let c1 = { (name: String) -> Void in
print(name)
}
To execute it:
c1("ABC")
They are self contained blocks of code. They can be used throughout the application code.

We can indicate that a closure is a parameter in a function.
func test(handler: (String) -> Void)
{
//(String) -> Void means that it is a closure that takes in a String and does not have a return value
  handler("efg")
}

Then when we call the function:
test(handler: c1)

With a return value:
let c2 =
{
  (name: String) -> String in
  return "Well \(name)"
}

to call it:

var message = c2("Mary")


Let's say we have a function that accepts a closure as a parameter:
func test(handler:() -> Void)
{
  handler()
}

create a closure:

let c3 = {
()-> Void in
print("ABC")
}
Have the function call the closure:
test(handler: c3)
You can pass other closures into the handler as well.

An example of a shorthand closure
test(handler: {print("GHI")})

Another example:

func printMany(num: Int, handler(_: String)->Void)
{
  for _ in 0..<num
 { handler("DEF")}
}


If you were to call the function this way, it will print Well DEF 5 times.
test(num: 5){ print("Well \($0)")}
$0 just means the first parameter, without giving it a name.
$1 means the second parameter, without giving it a name.

let c4: (String, String) -> Void =
{
print("\($0) \($1)")
}

To call it:
c4("ABC", "DEF")

If it does not return a value, instead of void, we can do this:
let c5: ()->() = {}

An example of closures being used is that of Map in arrays. It applies the closure to every element of the array:

myArray.map { str in print(str)}
This will print every element in the array.
OR
myArray.map{print($0)}

We could have 2 separate closures and place them in the map.
let add1 = {
(number: Int) -> Int in
return number += 1
}

let minus1 = {
(number: Int) -> Int in
return number -= 1
}

var newArray = numbersList.map(add1)
var newArray2 = numbersList.map(minus1)

Let's say you have a closure called add1. It can be a parameter inside a function:

func toAdd(workingDone: (Int)->Int)
{
  newArray = myArray.map(workingDone)
}

To call:
toAdd(workingDone: add1)

Closures help you to run code asynchronously (which prevents UI from freezing if you are getting data from a webservice).

In order to reuse a closure's parameters and return types, instead of retyping, you can use a typealias:

typealias UseMeInstead = (String) -> Void

Defining the function with a handler:
func test(handler: UseMeInstead) { handler("ABC")}

Selecting a closure based on results:

typealias c1 = ((String)->Void)

func test( handlerA: c1, handlerB: c1)
{
 if a>b
{
 handlerA("ABC")
}
else
{
 handlerB("DEF")
}
}
//The results of what you enter into the closure c1 will depend on conditions.

The closure for both situations:

var sitA : ClassA.c1 = {print("HAHA: \($0)")}
var sitB : ClassA.c1 = {print("LALA: \($0)")}
When we call:
classA.test(handlerA: sitA, handlerB: sitB)
if a > b
We will get: HAHA: ABC

If you have a class, let's say ClassA. We assign a closure, c1 to a property of ClassA. 
Let's say, within the closure, we make use of an instance of the class - this will cause memory issues.
To solve this problem, when declaring the closure:

lazy var c1: (()->String)
{ [unowned self] in return self.nameOfProperty}

Comments

Popular posts from this blog

Setting up a playground

Go to another page