IBOutlet Local Reasoning: What, why, and how

Did you watch Protocol and Value Oriented Programming in UIKit Apps from WWDC 2016 yet? I did and it’s one of my favorite sessions yet. It was essentially a part 2 from the WWDC 2015 session, Protocol-Oriented Programming in Swift. It even included a cameo of Crusty. I really liked the session because it gives some plain talk explanation towards improving your Swift code through basic functional programming techniques, and it was specifically attractive to me because of this year’s session’s focus on UI code. UIKit code is filled with object-oriented techniques and patterns like MVC and subclassing that it’s hard for a chiseled object-oriented veteran to learn new tricks like functional programming.

State is Bad

Relying on instance variables in your objects leads to buggy code. Furthermore, it leads to code that’s not just buggy, but also simply hard to debug. You should strongly consider any case where you introduce an instance variable that will be used for state management. Consider the following view controller:

class ViewController: UIViewController {

  var total = 0

  override func viewDidAppear(_ animated: Bool) {
    super.viewDidAppear(animated)
    if (total > 0) {
      print("total bigger than 0")
    }
  }

  func incrementTotal() {
    total = total + 1
  }
}

This code has an outrageous example of relying on an instance variable to determine state of the object. total is an Int. The major problem I have with code like this is the conditional if-statement in viewDidAppear(_:). This is a simple example, so it may not seem problematic to you. Imagine if the view controller had significantly more code, where total was referenced all over the code. Imagine there were numerous places where total was getting assigned to. Imagine there were numerous places where total was being used for conditional checks. Imagine that even external classes were reading and modifying total. Imagine all the places you would have to check and be aware of to ensure that total behaved correctly such that the view controller properly functioned. It quickly grows out of control. Code like this has been the source of more bugs than I can count in my UIKit programming. It’s why functional programming is so attractive to me, despite how hard it is for my head to wrap around some of the concepts.

Local Reasoning

One of the absolute most attractive things about functional programming in my mind is the lack of “side effects” of your code. Essentially, you strive to write code that does not have side effects, where there is a single “source of truth.” This is one of the cores of value oriented programming. When working towards this, you begin to write code that is much easier to debug and much less buggy to begin with. It’s much easier to understand code just by looking at it. One of the biggest anti-patterns of object oriented code is managing state of an object through a instance variable.

In the WWDC 2016 session Protocol and Value Oriented Programming in UIKit Apps the concept of “Local Reasoning” is introduced. It’s a semi-official computer science term that describes code which contains little to no side effects. Local reasoning is that “when you look at the code right in front of you, you don’t have to look at the rest of the code or project to know how it behaves.” This is really attractive because when you look at a piece of code, you can quickly have confidence that you understand what it does because you don’t need to look in many places. Instance variables that control object state are the enemy of local reasoning. For example, looking at the code above, any reference to total contains the caveat that there could be any number of things modifying total throughout program execution. You can never rely on the value of total to actually make a decision about the state of the program.

IBOutlet Local Reasoning

Of course you can’t actually remote all instance variables from your code. You’ll inevitably find certain scenarios where some state must be stored. One common such example are IBOutlets. IBOutlets are special snowflakes in the software design world. Pretty much any rule about software design may not apply when using IBOutlets. When using a Interface Builder file or a Storyboard, you’ll need to subclass UIView or UIViewController and link user interface elements from Interface Builder to the code through IBOutlets. The thing is, IBOutlets take the form of instance variables in code.

class ViewController: UIViewController {
  @IBOutlet weak var toggleSwitch: UISwitch!
}

So how can you apply IBOutlet local reasoning to improve your code? My coworker Mike Stanziano had a really useful idea on how to do this: use a property observer on the IBOutlet.

Let’s say that you want to create a UISwitch in your storyboard. The state of this switch will correspond to a boolean value in a persistence layer, the example will use UserDefaults. Initializing the value usually would happen in viewDidLoad() like:

override func viewDidLoad() {
  super.viewDidLoad()
  let keep = UserDefaults.standard().bool(forKey: "ToggleValue") ?? false
  toggleSwitch!.setOn(keep, animated: false)
}

Maybe you could use the Extract Method refactoring to move those two lines to their own method, but my point is that this code immediately violates local reasoning and starts you off on the wrong foot in using your IBOutlet. I’d consider this code part of initialization of the UISwitch, so it doesn’t make sense to put it in viewDidLoad() aside from the semi-artificial rule put in place by UIKit in that views should be setup in viewDidLoad().

Inspired by Protocol and Value Oriented Programming in UIKit Apps, my colleague Mike came up with something better:

@IBOutlet weak var toggleSwitch: UISwitch! {
  didSet {
    let keep = UserDefaults.standard().bool(forKey: "ToggleValue") ?? false
    toggleSwitch!.setOn(keep, animated: false)
  }
}

This code moves the initialization of the UISwitch‘s state out of viewDidLoad() to a place that I argue is much more appropriate, where the IBOutlet is defined. Since IBOutlets don’t get init’d in code, their definition is the closest thing we have towards an actual instantiation. Now, when you look at the definition of the IBOutlet, you immediately know how it’s initial state is configured. This is right in line with the local reasoning – “when you look at the code right in front of you, you don’t have to look at the rest of the code to know how it behaves.”

And of course, writing a unit test for this code is extremely straightforward.

func testSwitch_DisplaysStoredSwitchState_WhenToggled() {
  // Start the switch on
  UserDefaults.standard().set(true, forKey:"ToggleValue")
  UserDefaults.standard().synchronize()

  let toTest = ViewController()
  let testSwitch = UISwitch()
  toTest.toggleSwitch = testSwitch

  XCTAssertTrue(toTest.toggleSwitch.isOn)
}

No need to call viewDidLoad() from your test. Simply set the desired value on the UISwitch and verify the behavior.

Clean Code is Clear Code

I love the principle of local reasoning in your code. I’m all about writing clean code, I even named this blog after it. Give IBOutlet local reasoning a try and I think you’ll like it. Clean code is enabled by clear code, and local reasoning leads you to clearer code. Give it a try. Here’s a project on GitHub with this code all wired up using Swift 3 and Xcode 8.

Happy cleaning.

Implicitly Unwrapped Optionals and IBOutlets

Today I got burned by Xcode. It’s happened before and I’m sure it will happen again. I really do appreciate the help that you give me Xcode, but why are you such an enabler?

Implicitly Unwrapped Optionals as an IBOutlet goes BOOM The long story short: a tester noticed a crash in my app that ended up being due to the fact that Xcode’s Assistant Editor

helped me to create an implicitly unwrapped optional IBOutlet that ended up being nil in tester’s flow. When you try to access a nil implicitly unwrapped optional, your app goes BOOM (aka crash).

What went wrong My incorrect assumption was that the

IBOutlet would never be nil. Turns out that isn’t a safe assumption. In my case, it was a slightly complicated UITableView where one cell was attempting an operation on another cell that was off the screen. While the UITableView was able to find the destination cell in question, that cell’s IBOutlets weren’t alive at that moment, aka they were nil. So why I tried to access them directly, without concern for their ability to be nil, an error results. Where Xcode steered me wrong, was in how it automatically created the IBOutlet for me. Take a look at this: implicitly unwrapped optionals When the Assistant Editor creates the corresponding variable, it’s defined as an implicitly unwrapped optional. In my opinion this is bad, and the root cause of this error. I would never think to write an automated test to verify flows in the code around those being nil – well I will going forward. And in my opinion, totally enables me the developer to go on thinking that I never need to worry about IBOutlets being nil. Since Apple built Xcode to do this, it’s like the a complicit in my crime of not worrying about whether my IBOutlet was nil or not.

The Fix

It’s a pretty easy fix, but one that’s easy to overlook. First, take a look at this risky code:

class SampleCell: UITableViewCell {

  @IBOutlet weak var button: UIButton!

  func styleCellWith(color: UIColor) {
    button.setTitleColor(color, forState: .Normal)
  }
}

It’s well within the rules of the Swift compiler to write that code. And in fact, Xcode even points you in the direction of writing this code. The bad part is that since

button is an implicitly unwrapped optional, that means any reference to it with a ? mark means that if it’s nil, your app will crash. If you don’t believe me, try setting it to nil right before the title color is set, and try to run an app. There’s a couple safer ways to do this. Any of these work:

@IBOutlet weak var button: UIButton?

Defining

button in this way will force you to explicitly treat the optional everywhere you use it. While it’s more verbose, I kind of like this technique because I think this is the essence of where Swift excels – it forces you to be a little more defensive or explicit with handling “anything that could go wrong.”

Writing a test for it My big gripe with this is that Xcode makes it so easy to write the faulty code based on how the Assistant Editor guides you to create the

IBOutlet in code. If I’m following my normal Test Driven Development flow, I’d never think to write a test first to verify that an IBOutlet was not nil. But of course, now that someone actually found the bug in my code, I need to add a test to ensure that this specific case doesn’t actually happen again. Luckily, it’s pretty easy:

class SampleCellTests: XCTestCase {

  func testStyleCellWith_DoesNotCrash_WhenButtonIsNil() {
    let toTest = SampleCell()
    toTest.styleCellWith(UIColor.blueColor())
    // No need to do an assert here , as long as the test doesn't crash, it will be marked passed
  }

}

Just setup the object with the

IBOutlet, call the method under test. No need for an assertion, as long as the test doesn’t crash, we can consider that a pass. Here’s a pseudo screencast of my test driven development flow for fixing this bug and adding a test: implicitly unwrapped optionals

Beat Xcode to Submission! Well not really, in fact, you should totally try to work

with Xcode. It’s important to be aware of it’s shortcomings though, and I totally got burned today! Luckily I had an awesome tester discover that there are cases when you can’t totally trust an IBOutlet to exist, despite connecting it right from the storyboard. In case you want to try out the sample code, I posted it on Github. Check it out here. You can read more about implicitly unwrapped optionals in Apple’s documentation here.