A Swift Recoverable Precondition That Can Throw
What on Earth is a recoverable precondition? Isn’t the point of a precondition to be non-recoverable?
Yes. Sorry I couldn’t think of a better name1.
The Result
/// Checks a necessary condition for making forward progress.
///
/// The difference with `precondition` is that if an error is _thrown_ when the condition is executed, the error will
/// be _rethrown_ so that it can be recovered. But this recoverability does not apply if the condition executes
/// properly and its condition fails.
///
/// In other words, this function is a wrapper around `precondition` so that it can be fed a condition closure that can
/// throw.
func recoverablePrecondition(
_ condition: @autoclosure () throws -> Bool,
_ message: @autoclosure () -> String = String(),
file: StaticString = #file,
line: UInt = #line
) rethrows {
if !(try condition()) {
preconditionFailure(message(), file: file, line: line)
}
}
What is this?
Let me explain. Here is the code I had:
precondition(aLittleTenderness())
But then, I had to make aLittleTenderness
able to throw, and precondition
was yelling at me about it:
Property access can throw, but it is not marked with 'try' and it is executed in
a non-throwing autoclosure
Even when I tried a little tenderness2:
precondition(try aLittleTenderness())
The compiler would complain:
Property access can throw, but it is executed in a non-throwing autoclosure
And this alternative looked terrible:
precondition((try? aLittleTenderness()) ?? false)
So I created a recoverable precondition, which means:
- if the code throws, then try to recover from it;
- if the code does not throw, treat it as a precondition.
Don’t hate me. Listen to soul music instead.
Lessons Learned
Look at this badass signature! Of course I started from the signature of
precondition
and I adapted it to my needs. It features some keywords I
don’t use everyday. Each keyword deserves its own extensive explanation. But
wait, other people have already written about them: