Swift: Using Local Closures

Closures are often used as function arguments. But sometimes there are situations where local closures can be very handy.


Hint: This post has been updated to Swift 3, iOS 10 and Xcode 8
Imagine you have a view controller that has two GUI modes:

enum GUIMode {
      case Mode1
      case Mode2
}

For each GUI mode you want to set some properties for three labels:

var guiMode: GUIMode = .Mode1 {
    didSet {
        switch guiMode {
        case .Mode1:
            label1.text = "1"
            label1.textColor = UIColor.red
            label1.font = UIFont(name: "HelveticaNeue", size: 10)
        
            label2.text = "2"
            label2.textColor = UIColor.blue
            label2.font = UIFont(name: "HelveticaNeue", size: 12)
        
            label3.text = "3"
            label3.textColor = UIColor.yellow
            label3.font = UIFont(name: "HelveticaNeue", size: 11)
        
        case .Mode2:
            label1.text = "4"
            label1.textColor = UIColor.yellow
            label1.font = UIFont(name: "HelveticaNeue", size: 11)
        
            label2.text = "5"
            label2.textColor = UIColor.blue
            label2.font = UIFont(name: "HelveticaNeue", size: 9)
        
            label3.text = "6"
            label3.textColor = UIColor.brown
            label3.font = UIFont(name: "HelveticaNeue", size: 10)
        }
    }
}

This is a lot of code. You could create a function that sets the properties for a label, but it is very unlikely that you will use that function ever again. So in this case it is a nice solution to define a local closure within the function:

var guiMode: GUIMode = .Mode1 {
    didSet {
        let styleLabel: (_ label:UILabel,_ text:String,_ color:UIColor,_ size:CGFloat) -> () = { (label,text,color,size) in
            
            label.text = text
            label.textColor = color
            label.font = UIFont(name: "HelveticaNeue", size:size)
        }
    
        switch guiMode {
        case .Mode1:
            styleLabel(label1, "1", UIColor.red, 10)
            styleLabel(label2, "2", UIColor.blue, 12)
            styleLabel(label3, "3", UIColor.yellow, 11)
        case .Mode2:
            styleLabel(label1, "4", UIColor.yellow, 11)
            styleLabel(label2, "5", UIColor.black, 9)
            styleLabel(label3, "6", UIColor.brown, 10)
        }
    }
}

It is short and it is nice.

References

Image: @ Sergey Nivens / shutterstock.com

3 comments

  1. I don’t see any reason to use a closure instead of a function here; the closure is immutable and named.

    However, what is likely is that you should be running a mutable closure as your setter. This is why I am pining for mutability of property observers.

    1. Of course you could use a local function as well. As with many things in software programming, it is very much a matter of personal preferences.

    2. I like this as an option. This particular example I’d consider doing a private extension on UILabel also if I was going to change the styles a lot. I feel it a can be a little more readable to have something like:

      private extension UILabel {
      func setStyleWithText(text:String,color:UIColor,size:CGFloat) {
      self.text = text
      self.textColor = color
      self.font = UIFont(name: “HelveticaNeue”, size:size)
      }
      }

      var guiMode: GUIMode = .Mode1 {
      didSet {
      switch guiMode {
      case .Mode1:
      label1.setStyleWithText(“1”, color: UIColor.redColor(), size:10)
      label2.setStyleWithText(“2”, color: UIColor.blueColor(), size:12)
      label3.setStyleWithText(“3”, color: UIColor.yellowColor(), size:11)
      case .Mode2:
      label1.setStyleWithText(“4”, color: UIColor.yellowColor(), size:11)
      label2.setStyleWithText(“5”, color: UIColor.blackColor(), size:9)
      label3.setStyleWithText(“6”, color: UIColor.brownColor(), size:10)
      }
      }
      }

Comments are closed.