AppVenture has been updated

All articles have been rewritten and improved. You will be forwarded to the updated article.

Click here to go there directly.

Tue, 25 Aug 2015 #

Optional throw via try? in Swift 2 beta 6

This post is also available in 🇨🇳Chinese Thanks to SwiftGG

Swift 2.0 b6 includes a new way of handling exceptions via the try? keyword. This is a quick post to explain the basics, and why this is cool.

In Swift 1.x, all we had for error handling were optionals and NSError. Which is why many people adopted Either / Result types as they can be found in other programming languages:

let success = Result<String, NSError>.Success("success")

With Swift 2 and the introduction of try / catch exception handling. Internally, this doesn't use expensive stack unwinding, as other (say, Objective-C or Java) do it, but instead seems to pretty much return something akin to Either or Result. Only the syntax hides this from the user in order to make it simpler to use1.

1 Swift 2 b5 and earlier

However, once you start using the new do / try / catch more, what happens from time to time is that you start nesting code into messy branches because do is (was) incompatible with the other major way of handling potentially unknown states: optionals. Here's a particular ugly piece of code. Observe how we're nesting if let with let with do with let 2.

import Foundation
// get the currently logged in user
func loggedInUser() -> Int? { return 0 }
// get his name
func getUserName (userId: Int) throws -> String { return "Claus" }
// create a new image post with this username. Returns the post data
func imagePostForUserName(name: String, imageURL: NSURL?) -> NSData? { return NSData() }
// post the data to a server
func postImage(data: NSData) throws -> Bool { return true }

if let uid = loggedInUser() {
    do {
	let username = try getUserName(uid)
	if let data = imagePostForUserName(username, imageURL: nil) {
	    do {
		let success = try postImage(data)
		if success {
		    print ("Submitted")
		}  
	    } catch {
		// more error handling
	    }
	}
    } catch {
	// todo: error handling
    }
}

One reason why this is difficult to simplify is that the do forces a break in any multi guard or multi let 3.

2 Swift 2 b6

With beta 6, we get a new keyword, try? which performs a throwing operation and returns optional None in case of failure and optional Some in case of success. Quoting straight from the changelog:

A new keyword 'try?' has been added to Swift. 'try?' attempts to perform an operation that may throw. If the operation succeeds, the result is wrapped in an optional; if it fails (I.e. if an error is thrown), the result is 'nil' and the error is discarded. ‘try?’ is particularly useful when used in conjunction with “if let” and “guard”.

This makes it possible to retrieve the value of a potential throwing operation as an optional. If we apply this to our code above, we can simplify it quite a bit:


if let uid = loggedInUser(),
   username = try? getUserName(uid),
   data = imagePostForUserName(username, imageURL: nil),
   success = try? postImage(data)
   where success == true {
      print ("Submitted")
}

Submitted

This is, of course a bit of a contrived example, engineered to explain try?. But still, that's definitely less code. We're, of course, loosing a lot of possibly valuable error information that would otherwise be available in the catch.

3 Which to choose?

try? can help you write terser code at the expense of loosing insights. Using try? only returns an optional without further information on the particular cause of the error / exception. The benefit, of course, is beautiful composability with a lot of the other Swift syntax, like map, flatmap, switch, guard, if let, for case, and others.

The non-optional try works great for distinct task where you're not dependant on earlier or later possible optional results.

The aforementioned Result type, on the other hand offers both; either the requested value, or a possible error. You can just continue using Result, which also has support for wrapping throws and much more, however keep in mind that this is not the direction Swift seems to intend to go 4. Otherwise, we'd have a full blown Result or Either type in Swift 2.

I'm really happy about the introduction of try? as it will make many snippets, particularly when interacting with the Cocoa API, easier to solve.

Footnotes:

1
Much like the lifting into / out of monads in Swift optionals is hidden via the ? syntax
2
There're ways to improve this code without try?, of course, but it makes for a nice example
3
Another is, of course, that I'm using raw NSRegularExpression here, instead of a simplifying library
4
Also, you'll always need to add additional dependencies to your project

If you read this far, you should follow me (@terhechte)
on Twitter