Understand Closures

You’ll find closures throughout the API’s, but you may not know how to use them properly. Let’s take a basic tour of closures. 

Download the exercise file, and you’ll find a project with a playground. In the playground you’ll find a function  to compute a pizza volume.

func roundPizzaVolume(height:Double, diameter:Double) -> Double{
    let pi = Double.pi
    let z = diameter / 2.0
    let a = height
    let v = pi * z * z * a //My favorite pun.
    return v
}
roundPizzaVolume(height: 2, diameter: 10) 

That’s for a round pan pizza, for a rectangle it won’t work. 

But if I make a function that has a formula for the area, then I can do any area. In the anyPizzaVolume, I’ll put one at the end of the argument list. 

anyPizzaVolume(height:Double, length:Double, width:Double, area:(Double,Double)->Double)-> Double{

That declares a closure, which is not much more than a function that you pass as a parameter.

In my code I’ll multiply height by area, then return the volume.  

func anyPizzaVolume(height:Double, length:Double, width:Double, area:(Double,Double)->Double)-> Double{
    let volume = height * area(length,width)
    return volume
}

I’ll call anyPizzaVolume.In the autocomplete for anyPizzaVolume, you’ll see the closure.

anyPizzaVolume(height: , length: , width: , area:  Double>)

  Add a height of 2, a length of 10 and a width of 10. Hit tab when you get to area, and you’ll get a stubbed closure. I usually use this, but I’m going to do it manually so you get the idea. You stick the closure as the last parameter, so you can put the closure outside the function. Otherwise you need to put it inside, and that can get confusing to read. 

anyPizzaVolume(height: 2, length: 10, width: 10) {
    
}

You start a closure identifying the declared variables, then add the keyword in 

anyPizzaVolume(height: 2, length: 10, width: 10) {
    (l,w) in
}

Now you write code based on those two values. 

anyPizzaVolume(height: 2, length: 10, width: 10) {
    (l,w) in
    let r = l / 2.0
    return r * r * Double.pi
}

Run this and you get the answer you got before.  I can copy this, paste it, and change the code to a rectangle area. 

anyPizzaVolume(height: 2, length: 10, width: 10) {
    (l,w) in
    return l * w
}

Run again, and you get a different answer. There are better ways of doing this method, but I wanted to illustrate the basic use of closures. Where they get used is Asynchronous processing. That’s when you don’t control when the code will execute like completion handlers, timers,  and actions. For example, in the roundPizzaVolume I could add a completion handler that handles errors. 

completionHandler:(Bool,Double)-> Void

For this closure, I’m not returning anything to the function. Closures require a return type, so I useVoid to indicate there is noting returned. I’ll add the completionHandler`function to to the code

completionHandler(v>0,v)

The bool indicates if the area is a meaningful answer, and I pass the volume to the closure. Now I can change the roundPizzaVolume code to use the closure by adding this: 

roundPizzaVolume(height: -2, diameter: 10){
    (success,volume) in
    if !success{
        print("Invalid Volume \(volume)")
    }
}

Change the height to -2 and Run. Many closures, especially file handling, will do this and give you a success bool which you can then handle after it does it function.

Take a look at the ViewController.  You’ll find examples of  API’s using closures.   Action handlers are often closures. For example I have an alert in this code. UIAlertActions use closures to respond to the selection of the action.

let actionOne = UIAlertAction(title: “First Action”, style: .default) { (action) in
self.alertStatus.text = “First Action”
}

 Notice that as asynchronous blocks, you need self to use properties and methods of the class.  Also you can make closures optional, so if you do not want to use them, they can be set to nil, as in this action.

 let cancelAction = UIAlertAction(title: "Cancel", style: .cancel, handler: nil)

There’s a lot you can do with closures. As you can see here, you will find them in code frequently.  

The Whole Code

You’ll find the completed code on Github here. Below you’l find the playground and view controller for this project.

import UIKit
//
//  An exercise file for iOS Development Tips Weekly
//  by Steven Lipton (C)2018, All rights reserved
//  For videos go to http://bit.ly/TipsLinkedInLearning
//  For code go to http://bit.ly/AppPieGithub
//


func roundPizzaVolume(height:Double, diameter:Double, completionHandler:(Bool,Double)->Void) -> Double{
    let pi = Double.pi
    let z = diameter / 2.0
    let a = height
    let v = pi * z * z * a //My favorite pun.
    completionHandler(v>0,v)
    return v
}
roundPizzaVolume(height: -2, diameter: 10){
    (success,volume) in
    if !success{
        print("Invalid Volume \(volume)")
    }
}

func anyPizzaVolume(height:Double, length:Double, width:Double, area:(Double,Double)->Double)-> Double{
    let volume = height * area(length,width)
    return volume
}

anyPizzaVolume(height: 2, length: 10, width: 10) {
    (l,w) in
    let r = l / 2.0
    return r * r * Double.pi
}

anyPizzaVolume(height: 2, length: 10, width: 10) {
    (l,w) in
    return l * w
}

//
//  ViewController.swift
//  ClosureExercise
//
//  An exercise file for iOS Development Tips Weekly
//  by Steven Lipton (C)2018, All rights reserved
//  For videos go to http://bit.ly/TipsLinkedInLearning
//  For code go to http://bit.ly/AppPieGithub
//
import UIKit

class ViewController: UIViewController {

    @IBOutlet weak var presentStatus: UILabel!
    @IBOutlet weak var alertStatus: UILabel!
    @IBOutlet weak var alertButton: UIButton!
    @IBAction func alertButton(_ sender: UIButton) {
        let alert = UIAlertController(title: "Demo Alert", message: "Some Closure examples", preferredStyle: .alert)
        //Actions for methods like alerts often use closures to do the action
        let actionOne = UIAlertAction(title: "First Action", style: .default) { (action) in
             //This is on a seperate thread so use self to access the elclosing class' methods and properties.
            self.alertStatus.text = "First Action"
            //this is on a seperate thread so use self to access the elclosing class' methods and properties.
        }
        let actionTwo = UIAlertAction(title: "Second Action", style: .default) { (action) in
            //This is on a separate thread so use self to access the enclosing class' methods and properties.
            self.alertStatus.text = "Second Action"
        }
        // Closures can be optional, and then you can set the value to nil if you don't plan to use it. Make sure you handle the nil in the method though.
        let cancelAction = UIAlertAction(title: "Cancel", style: .cancel, handler: nil)
        
        
        alert.addAction(actionOne)
        alert.addAction(actionTwo)
        alert.addAction(cancelAction)
        
        // The present function has a closure we usually leave nil, but if you have clean up​ after you launch the method, this is where you do it. Here I print a status message.
        present(alert, animated: true) {
            self.presentStatus.text = "Presented Alert"
        }
    }
    
    override func viewDidLoad() {
        super.viewDidLoad()
        alertButton.layer.cornerRadius = 20
        // Do any additional setup after loading the view, typically from a nib.
    }


}

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 )

Google photo

You are commenting using your Google 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 )

Connecting to %s

This site uses Akismet to reduce spam. Learn how your comment data is processed.