14
Sep
2018

Explore Closures in Swift

Closures are self-contained blocks of functionality that can be passed around and used in your code. Closure expressions are a way to write inline closures in a brief, focused syntax. Closure expression gives some syntax optimizations in a shortened form without loss of any clarity or intent. Swift’s closures are similar to blocks in C and Objective-C. Closures are able to capture and store references to any constants and variables from the context in which they are defined.

The key advantage of swift’s closures is the memory management, so the developer doesn’t have to take care of it. In excess of, swift’s closures have a clean, clear and clutter free syntax with much-needed optimization as well.

Global functions, as well as nested functions, are also a part of closures. Closures take one of three forms:

  1. Global functions are also closures which have a name and do not capture any value.
  2. Nested functions are also closures which have a name and which is able to capture values from its enclosing function.
  3. Closure expressions are unnamed closures which are written in a simple syntax and is able to capture values from its surrounding context.

Closure Expression Syntax

Closure expression syntax has the following general form:

{ (parameter) ->; returntype in
statements
 
}

The parameters in closure expression syntax can be in-out parameters, but they don’t have a default value. For the inline closure expression, the parameters and return type are written inside the curly braces, not outside of them. The start of the closure’s body is introduced by the in the keyword. This keyword indicates that the definition of the closure’s parameters and return type has been completed and the body implementation of the closure is about to start.

The body of the closure is so compact, it can even be addressed on a single line. A combination of parentheses still covers the entire argument for the method. However, that argument is now an inline closure.

Swift’s Closures expression has been optimized well. These optimizations include:

  1. Inferring parameter types and return value types from context
  2. Implicit returns from single-expression closures
  3. Shorthand argument names
  4. Trailing closure syntax

Inferring Type From Context

Due to sorting closure is passed as an argument to a method, Swift can infer the types of its parameters and the type of the value it returns. The sorted(by:) method is being called on an array of strings, so its parameter must be a function of type (String, String) → Bool. Meaning that the (String, String) and Bool types do not need to be written as part of the closure expression’s definition. Because all these types can be inferred, the return arrow (→) and the parentheses around the names of the parameters can also be removed:

var arrayTest = [“abc”, “pqr”, “xyz”, “efg”]
 
let arrayNew = arrayTest.sorted(by:  { s1, s2 in return s1 > s2  } )

Implicit Returns from Single-Expression Closures

Single-expression closures can implicitly return the result of their single line expression by removing the return keyword from their declaration, as in the above previous example can be written as:

arrayNew = arrayTest.sorted(by:  { s1, s2 in s1 > s2  } )

Shorthand Argument Names

Swift automatically provides shorthand argument names to the inline closures, which can also be used to mention to the values of the closure’s arguments by the names $0, $1, $2, and so on.

arrayNew = arrayTest.sorted(by:  by: { $0 > $1 }  )

In the above example, $0 and $1 refer to the closure’s first and second String arguments respectively.

Trailing Closures

If you required to pass a closure expression to a function as the function’s final argument and the closure expression is long, it can be useful to write it as a trailing closure instead. A trailing closure is written after the function call’s brackets, even though it is still an argument to the function. When you utilize the trailing closure syntax, you actually no need to write the argument label for the closure as part of the function call.

func functionThatTakesAClosure(closure: () → Void) {
 
// function body can be written here
 
// Here’s function without using a trailing closure:
 
functionThatTakesAClosure(closure: {
 
// closure's body will be written here
 
// Here's function with a trailing closure instead:
 
functionThatTakesAClosure()
 
// trailing closure's body will be written here

So, the above previous example of sting-sorting function can be written outside of the sorted(by:) method’s parentheses as a trailing closure:

arrayNew = arrayTest.sorted()  { $0 → $1 }

If a closure expression is provided as the function or method’s only argument and you want to provide that expression as a trailing closure, then, you no need to write a pair of brackets() after the function or method’s name when you call the function:

arrayNew = arrayTest.sorted  { $0 → $1 }

So, the conclusion is that trailing closures are most useful when the closure is sufficiently long that it is not possible to write it inline on a single line.

Let’s get better idea with the example of functions and closure.

For example, the array has a function map that takes function or closure and applies to each element of the array which returns a new array containing the new functional result. Below example double the value of each element of the array.

var arrayTest = Array(1...10)
 
arrayTest = arrayTest.map{$0.*2}. //This will print [2,4,6,8,10,12,14,16,18,20].

Let’s explore the above example in a more expressive and dynamic way using closure. Below example is the function which returns closure.

Here you can implement your own custom logic or calculation in this closure and then use it through the function.

This way you can perform any operation on array data and get the expected result by just calling one line function.

var array = Array(1...10)
 
func implementDynamicLogic(x: Int)(Int) → Int {
    let returnImplementation = { y in
 
// you can implement your custom calculations or logic here.
 
// For example we have multiply each element with x and then add x into it.
 
            let z = x*y
 
            return x + z
     }
 
   return returnImplementation
 
}
 
 array = array.map(implementDynamicLogic(x: 2))
 
 print(array)  //This will print [4,6,8,10,12,14,16,18,20,22]

Two ways, iPhone app developers can declare functions in swift. Either with the func keyword as demonstrated above Or with to use a “closure expression”.

Here is the double Value function from above, written using the closure expression syntax:

let doubleValueClosure = {  (i: Int) → Int in return i*2 }
array.map(doubleValueClosure)

One more example here regarding closure and function.this is the more expressive and dynamic way to use closure.

Below example demonstrate that if need to replace “closure” text in all strings within an array with “function” then it can be easily done with closure rather than writing old function code that looping through the array.

var arrayString = ["abc closure", "pqr closure", "xyz closure"]
 
 
func replaceStringFunc()(String)String {
 
        return { name in
 
             if (name.contains(“closure”)) {
 
return name.replacingOccurences(of:”closure”, with:function)
} else {
 
return name
 
}
            }
 
    }
 
        arrayString = arrayString.map(replaceStringFunc())
 
      print(arrayString)
 
// This will response as  ["abc function", "pqr function", "xyz function"].

To define tree node models, closure is the best way to approach it. See below example.

This example demonstrates how to get values from closure. iPhone developers might have seen sending values through closure, but iPhone developers can also receive a value through closure.

// Suppose there are two child belongs to one parent node, what can be the better static way to init it.
 class Child {
 
     var firstname: String? = nil
 
var lastname: String? = nil
 
   init(firstname: String, lastname: String) {
 
        self.firstname = firstname
 
     self.lastname = lastname
 
    }
 
}
 
class Parent {
 
   var firstname: String? = nil
 
   var lastname: String? = nil
 
   var childList: [Child] = []
 
init(firstname: String, lastname: String, childs: ()[Child]) {
 
        self.firstname = firstname
 
      self.lastname = lastname
 
      self.childList = childs()
 
    }
    func decorateChilds() {
 
        print("---------------------")
 
        for child in self.childList {
 
           print("Child: \(child.firstname!) \(child.lastname!)")
 
        }
 
      print("---------------------")
 
   }
 
let parent = Parent.init(firstname: "first", lastname: "last") { ()[Child] in
 
   return [Child.init(firstname: "firstChild", lastname: "lastChild"),      Child.init(firstname: "firstChild1", lastname: "lastChild1")]
 
}
 
parent.decorateChilds()


Comments

Leave a Reply