[swift-evolution] [Review] SE-0112: Improved NSError Bridging

Mohsen Ramezanpoor me at mohsenr.com
Sun Jul 3 07:26:35 CDT 2016


> * I would like to see more work on `RecoverableError`. I know this is a more or less faithful translation of `NSErrorRecoveryAttempting`, but I don't think that's a particularly good API, and I would like to see it revisited.
> 
> The design I'd prefer would look more like this:
> 
> protocol RecoverableError: Error {
> var recoveryOptions: [ErrorRecoveryOption] { get }
> }
> 
> typealias ErrorRecoveryCompletionHandler = (recovered: Bool) ->Void
> 
> protocol ErrorRecoveryOption {
> var localizedName: String { get }
> func attemptRecovery(completion: ErrorRecoveryCompletionHandler) {…}
> }
> 
> struct AnyErrorRecoveryOption: ErrorRecoveryOption {
> typealias RecoveryAttempter = (ErrorRecoveryCompletionHandler) ->Void
> 
> var localizedName: String
> var attempter: RecoveryAttempter
> 
> init(localizedName: String, attempter: RecoveryAttempter) {…}
> 
> func attemptRecovery(completion: CompletionHandler) { attempter(completion) }
> }
> 
> Though further from the equivalent Foundation API, I think this is a much cleaner design. It requires no `switch` statements, avoids issues with matching indices between the option array and the recovery attempter, and makes it easier for interested subtypes to add or remove recovery options.
> 
> In particular, in my view, there should be only one way to attempt recovery, and that way should not be application-modal. If a client wants to recover modally, we should invoke the non-modal API and then spin the runloop until the completion handler runs.
> 
> If a better `RecoverableError` design isn't feasible in Swift 3, I think we can defer it. In a pinch, `CustomNSError` provides the ability to specify error-handling behavior the old-fashioned way.

I agree. This feels like a much better API, specially as it’s not tied to Cocoa (e.g. via `CustomNSError `). One tweak I would suggest is for the completion handler to return an error:

```
typealias ErrorRecoveryCompletionHandler = (recoveryError: Error?) ->Void
```

If the recovery from an error fails, It’s almost certainly because *another* error occurred.

I realise this is an even larger departure from Cocoa, but it should not be difficult to do technically. A negative result returned by, say, `NSErrorRecoveryAttempting` can will be converted to a new `RecoveryFailed` error struct which wraps the original error; conversely, a “recovered” boolean can be created by simply checking if the recovery error is `nil`.


More information about the swift-evolution mailing list