Swift 2: Test Driving the Error Handling

I’d admit it, when Chris Lattner explained the new Swift 2 error handling code on Tuesday at WWDC (session available here), I was a bit overwhelmed. It looked pretty alien, Java-like, and unnecessarily complex. I felt like I’d have to sit down for a while to try to understand it.

However, yesterday morning for my Alt Conf talk, I had to change one of my code samples to Swift 2 code, with error handling included. While I did have to reference the slides from the WWDC session, the implementation turned out to be very easy, clean and natural!

The example I used was a very simple form with a name and an age text fields:

Simulator Screen Shot Jun 11, 2015, 6.46.59 AM

The implementation is as follows:

Model

The model is a simple Person value:

struct Person {
    let name: String
    var age: Int
}

View Model

To mitigate between the messiness of the data entry in the form and the clean model, I created a View Model:

struct PersonViewModel {
    var name: String?
    var age: String?
}

Since the view model is aware of the latest name and age data entry, it’s the perfect place to actually create the Person model. The problem of course, is that there are a few things that could go wrong before the model can be created. First, the user might not fill out all the required fields (name and age), or the age might be formatted wrong. This is where the error handling comes in! I’ve defined my Error Types as follows:

struct PersonViewModel {
    var name: String?
    var age: String?

    enum InputError: ErrorType {
        case InputMissing
        case AgeIncorrect
    }
}

Finally, I can create the person using the new Swift 2 error handling syntax:

struct PersonViewModel {
    var name: String?
    var age: String?

    enum InputError: ErrorType {
        case InputMissing
        case AgeIncorrect
    }

    func createPerson() throws -> Person {
        guard let age = age, let name = name 
             where name.characters.count > 0 && age.characters.count > 0 
        else {
            throw InputError.InputMissing
        }

        guard let ageFormatted = Int(age) else {
            throw InputError.AgeIncorrect
        }

        return Person(name: name, age: ageFormatted)
    }
}

Things I love about this:

  • I don’t have to return an optional!
  • the guard is super clean looking and easy to understand
  • I can put multiple conditions in my guard statement – I unwrap the age and name at the same time, since they produce the same error in this case

ViewController

Finally, I can easily catch the error in my ViewController when the user taps the Submit button:

import UIKit

class ViewController: UIViewController, UITextFieldDelegate {

    @IBOutlet weak var nameTextField: UITextField!
    @IBOutlet weak var ageTextField: UITextField!

    var personViewModel = PersonViewModel()

    override func viewDidLoad() {
        super.viewDidLoad()
    }

    func textFieldDidEndEditing(textField: UITextField) {
        personViewModel.name = nameTextField.text
        personViewModel.age = ageTextField.text
    }

    @IBAction func onSubmitButtonTap(sender: AnyObject) {
        view.endEditing(true)

        do {
            let person = try personViewModel.createPerson()
            print("Success! Person created. \(person)")
        } catch PersonViewModel.InputError.InputMissing {
            print("Input missing!")
        } catch PersonViewModel.InputError.AgeIncorrect {
            print("Age Incorrect!")
        } catch {
            print("Something went wrong, please try again!")
        }
    }

}

I love how simple and easy-to-read the error catching is!

Enjoy the article? Join over 20,000+ Swift developers and enthusiasts who get my weekly updates.