Adventures in iOS Development
Unit Testing and Clean Code Exploration Toolbox Archive About Feed

NSErrorPointerWrapper: Simplified handling of Cocoa Touch API errors in Swift

The Pod source is here: NSErrorPointerWrapper. Read further for motivations and short overview.

Dealing with NSError in Objective-C is clunky at best. You have to create a variable of *NSError type, then pass it by reference to the potentially erroneous method. That variable may or may not be populated as a result of an action. In fact it’s recommended to check the result, not the error, to verify whether the action was successful.

Important: Success or failure is indicated by the return value of the method. Although Cocoa methods that indirectly return error objects in the Cocoa error domain are guaranteed to return such objects if the method indicates failure by directly returning nil or NO, you should always check that the return value is nil or NO before attempting to do anything with the NSError object.

Standard way of handling error in Cocoa Touch API in Objective-C looks like this:

NSError *error = nil;
id result = [object methodWithParameter: parameter, error: &error];
if (result) {
  // do something with the result
} else {
  // handle error
}

This is quite a lot of boilerplate code to write/read each time. In Objective-C when result is nil it fails the test as if you’ve passed false. Swift is very strict about implicit conversions and you need to make sure whether you’re getting a Bool or some variation of AnyObject, and handle it accordingly.

var error: NSError?
var result: AnyObject? = methodWithParameter(parameter: parameter, error: &error)
if let unwrapped = result {
  // do something with the unwrapped result
} else {
  // handle error
}

To reduce the need to write the same code over and over, I’ve created a wrapper for calls that need to take in NSErrorPointer. tryWithErrorPointer function takes in closure with a single NSErrorPointer argument. Sample usage:

tryWithErrorPointer({ (error: NSErrorPointer) -> Void in
    methodWithParameter(parameter: parameter, error: error) })

Using closure syntax goodness you can end up with a simplified call that looks like this:

tryWithErrorPointer { methodWithParameter(parameter: parameter, error: $0) }

where $0 is a shorthand argument name for the NSErrorPointer. OK, that pesky error variable is gone, but what about handling success and error? You can nicely chain handling of both cases. You can omit any of the handlers as you wish (example from demo app).

tryWithErrorPointer { methodWithParameter(parameter: parameter, error: $0) }
	.onSuccess{ result in 
	  // do something
	}
	.onError{ error in
	  // handle error, log, call abort during development stages, etc.
	}

Autocompletion tips us about the handlers and you have less code to write by hand. Often you need to cast the result to the expected type, since you can’t do much with AnyObject. Casting is just another thing that can go wrong. Here’s a version of tryWithErrorPointer that in addition takes in type you’d like to downcast to 1 . In case casting fails error in onError handler has NSErrorPointerWrapperFailedDowncast error code.

tryWithErrorPointer(castResultTo: NSDictionary.self) { NSJSONSerialization.JSONObjectWithData(JSONData, options: nil, error: $0) }
    .onError{ _ in XCTFail() }
    .onSuccess{ XCTAssertNotNil($0); return }

tryWithErrorPointer wrapper helps to focus only on the code that’s specific to current use case and forget the boilerplate. I think it also reads much better than the standard error handling.

  1. Note the `; return` - to override implicit return from closure, which messes up handler`s argument type.

    </li> </ol></div>