I wasn’t quite happy with new error handling in Swift 2 (read my last post). Changed my opinion little bit after few
days of usage and here’s why.
When I saw try, catch, … for the first time I was shocked – exceptions? Noo! But when
you look closer, it looks similar to exceptions, but nothing more. Forget about exceptions. It’s just
another way to propagate errors.
Skipping pre Result<T,E> era. Here’s an example with Result<T,E> where E is NSError.
Actually, in this way we have lot of error handling in our logic, which makes it unclear
what it exactly does. We can forget to handle another error codes in switch statements, etc.
Lets improve it with ErrorType.
Cleaner and we get exhaustive switch statement. Nice. But imagine you have more doSomething() functions
in your logic and you have to handle errors from both functions. You’ll end up with something like this.
What check() function does? Hard to say in this error handling mess. Lets improve MyError and leverage guard as well.
Much better. But still not good enough. Still hard to say what this check() is all about. You
can collapse guard statements in Xcode, but why should you do this? There’s better way.
This way rocks, because:
we have error handling in one place,
we can easily check what check() is all about,
function signatures are much shorter and nicer.
What about cleanup? I saw solutions like this …
… which has some flaws. You can forget to call it in some places, you can endup with if hell if you’re
checking what to clean up, what not, … Solution is to use defer statement, which is executed at the end
of the current block of code (scope exit).
Nice, because we have cleanup logic in one place. You can also use several defer statements and all
of them are going to be executed.
To be more precise, not all of them. If doSomething() throws, only // Clean up 1 will be executed. If doSomething() doesn’t throw, // Clean up 1 and // Clean up 2 will be executed.
Write short functions or you can end up with defer mess (similar to error handling without throw).
As I wrote, I changed my opinion. New error handling wins. It produces nicer, shorter
and easier to read code.
Also started to like try annotation. I take it as a reminder – Hey, here it can fail. Which
makes it easier to read, debug, …
But what if thrown error is not fatal one? Like MyError.Busy, which can mean – wait for a while
and try again. It’s sad, because you’ll end up with error handling on several levels again. It
would be nice to have something like this with guard.
What’s nice is that you can cast your error to NSError, which makes some error handling
simpler and less confusing.
Hmmm, nice, but I don’t like 0, 1, … Would like to provide my own numbers for error codes. Lets
try it with raw values.
Nice try, but it doesn’t work. Still 0, 1. Maybe we can do something with it via ErrorType protocol, which
is declared as:
Ooops, nothing there, we can’t. Sad. Maybe it’s possible, but I didn’t find how yet. Question is if it
This new error handling still has some flaws, but overal score is very good and it wins over Result<T,E>
for me now. Going to fill some radars to make Swift better. Do it as well, because dupes are counted as
votes and they’re listening. I received response for all my bug reports (Swift related) very quickly,
which is unbelievable when I compare it to bug reports for another products.