WWDC Refactored

Recently, I’ve been discussing ways to architect iOS applications to make them easier to test. Yesterday, I stumbled upon this talk from WWDC ‘17. In this video, the presenter espouses a lot of the same ideas I’ve been advocating here. It’s a great video and I highly recommend it.

There, the presenter refactors a method that might be seen in any application. The original method would be hard or impossible to unit tests due to its dependency on the UIApplication singleton. They refactor it using several techniques, including mocking a dependency, in order to make the logic testable. The result is a much more testable unit of code. Here is the original method:

@IBAction func openTapped(_ sender: Any) {
  let mode: String
  switch segmentedControl.selectedSegmentIndex {
    case 0: mode = "view"
    case 1: mode = "edit"
    default: fatalError("Impossible case")
  }
  let url = URL(string: "myappscheme://open?id=\(document.identifier)&mode=\(mode)")!
  if UIApplication.shared.canOpenURL(url) {
    UIApplication.shared.open(url, options: [:], completionHandler: nil)
  } else {
    handleURLError()
  }
}

And here is the refactored version:

protocol URLOpening {
  func canOpenURL(_ url: URL) -> Bool
  func open(_ url: URL, options: [String: Any], completionHandler: ((Bool) -> Void)?)
}
extension UIApplication: URLOpening {
  // Nothing needed here!
}
class DocumentOpener {
  let urlOpener: URLOpening
  init(urlOpener: URLOpening = UIApplication.shared) {
    self.urlOpener = urlOpener
  }
  func open(_ document: Document, mode: OpenMode) {
    let modeString = mode.rawValue
    let url = URL(string: "myappscheme://open?id=\(document.identifier)&mode=\(modeString)")!
    if urlOpener.canOpenURL(url) {
      urlOpener.open(url, options: [:], completionHandler: nil)
    } else {
      handleURLError()
    }
  }
}

And this is how you would test it:

class MockURLOpener: URLOpening {
  var canOpen = false
  var openedURL: URL?
  func canOpenURL(_ url: URL) -> Bool {
    return canOpen
  }
  func open(_ url: URL,
      options: [String: Any],
      completionHandler: ((Bool) -> Void)?) {
      openedURL = url 
  }
}
func testDocumentOpenerWhenItCanOpen() {
  let urlOpener = MockURLOpener()
  urlOpener.canOpen = true
  let documentOpener = DocumentOpener(urlOpener: urlOpener)
  documentOpener.open(Document(identifier: "TheID"), mode: .edit)
  XCTAssertEqual(urlOpener.openedURL, URL(string: "myappscheme://open?id=TheID&mode=edit"))
}

This is a huge improvement over the original method. It allows us to test all the logic contained in DocumentOpener. It decouples the application singleton from the logic under test. My only objection is that the test is a little convoluted. As someone unfamiliar with the code, I need to examine the mock object to see how it works and I need to open up the DocumentOpener to understand how it interacts with the MockURLOpener. Additionally, the URLOpening protocol makes the production code harder to reason about. Was the protocol added to facilitate testing or did the writer truly intend for consumers of DocumentOpener to implement multiple URLOpening classes? What if we refactored this using some tips I’ve outlined in previous articles?

class DocumentOpener {
  enum Result {
    case openURL(URL), invalidURLError
  }
  let isURLValid: (URL) -> Bool
  init(isURLValid: (URL) -> Bool) {
    self.isURLValid = isURLValid
  }
  func open(_ document: Document, mode: OpenMode) -> DocumentOpener.Result {
    let modeString = mode.rawValue
    let url = URL(string: "myappscheme://open?id=\(document.identifier)&mode=\(modeString)")!
    if isURLValid(url) {
      return .openURL(url)
    } else {
      return .invalidURLError
    }
  }
}

Arguably this is a little nicer; we’ve removed a few lines of code and a type declaration. The real value comes when attempting to test this unit.

func validURL(_ url: URL) -> Bool {
  return true
}
func testDocumentOpenerWhenItCanOpen() {
  let documentOpener = DocumentOpener(isURLValid: validURL)
  let result = documentOpener.open(Document(identifier: "TheID"), mode: .edit)
  XCTAssertEqual(result, .openURL(URL(string: "myappscheme://open?id=TheID&mode=edit")))
}

As you can see, we are injecting a single method instead of a conforming object. Additionally, we are returning a value representing the action we’d like to perform. The advantage is that our test is reduced from 18 lines to 8, there is almost no arrange code, and the return value is easily verified.

This isn’t always the best approach. If DocumentOpener had a lot of interaction with the system via URLOpening, we’d end up injecting a lot of methods into DocumentOpener. At this point it might make sense to inject a URLOpening object instead.

In conclusion, by avoiding mocks and focusing on value types, it is possible to write tests that are shorter and easier to understand down the road.

I'm a freelance iOS developer based in San Francisco. Feel free to contact me.

Pt. 3 Swift Testability By Example

In my last article, I discuss the easiest path to testable Swift. In that article I list qualities that make tests valuable. Additionally, I show that business and application logic should be decoupled from volatile or asynchronous dependencies. Now I’d like to focus on the “State” object that houses all of this logic and illustrate some design decisions that will make it easier to test.
An experienced tester knows that certain features are trivial to test while others must be mangled and distorted before they yield to testing. Tests that are easy to write are ones that require little or no arrangement code, don’t require new classes to enable testing (ie. mocks or stubs) and produce easily verifiable output. The easiest thing in the world to test is a pure function:

Pt. 1 Effective Unit Testing in Swift

I’m coming clean. I’m embarrassed to admit it, but I’ve been a professional software developer for twelve years and have hardly written any tests. I’ve embraced the unit testing cult a few times, but every time I did, I spent more time hacking my code to make it testable than writing new features. My velocity went into the toilet, my once beautiful code became riddled with concessions to make it testable, tests would break after small changes, and worst of all, they never seemed to surface any genuine defects. I would eventually give up with the excuse that “iOS code isn’t testable” or “unit tests are for people who write bad code”. Such hubris!

Obscure Programming Laws And What They Can Do For You

In my last post I explained some of the common anti-patterns that developers fall into. In this article I’d like to explore some architectural principles that help make code more robust and extensible. Like all ‘rules’ in software development, these are really only guidelines, so if something feels awkward or forced, it probably is.

Bringing Your Mobile User Experience To The Next Level

Developers, repeat after me, “My app is not a web page”. Again! “My app is not a wep page”. It seems that most developers are writing their mobile apps like they did web pages in the 1990’s. They makes requests to the server, put the app into a waiting state, and display the response in the UI. If they are offline, an error is reported to the user and the app is unusable. This experience is fine for a webapp in the browser with little or no local storage and a ubiquitous network connection, but falls short in the mobile world.

Keep Your Sanity With Event Handling State Machines

In my previous post I taught you how to wrangle your game initialization code in order to keep it from breaking with every refactor. Today, I’d like to talk about event handling and how to get a handle on it (pun intended). If you’re anything like me, your event handling code (generally your touchesEnded:withEvent:) is the longest method in your whole application; littered with switch statements, if/else clauses and enough boolean flags to make George Boole turn over in his grave.

Sleep Easy with Sequencing and Dependency Resolution

Writing games is hard. Writing games for iOS is even harder. Typically, one of the most fragile and error prone parts of a game is the loading and initialization process. For obvious reasons, it is also one of the most critical pieces of your app. By using a sequencer, developers can define dependencies for steps in their loading process thereby removing some of the brittleness and instability from their game.