7

In Swift 3, the RecoverableError protocol was introduced, but here is very little documentation on how to use this.

This sounds like a native way to offer retry functionality for failed processes, which could be pretty useful. What's an example of how this can be used?

TruMan1
  • 27,054
  • 46
  • 150
  • 273

1 Answers1

2

One thing you can utilize RecoverableError is Document-based application for macOS.

  1. Create a new project, macOS > Cocoa, checking "Create Document-Based Application".

  2. Define your own Error type conforming to RecoverableError.

    enum MyError {
        case documentSavingError
        //...
    }
    extension MyError: RecoverableError {
        var recoveryOptions: [String] {
            return [
                "Retry", "Ignore"
            ]
        }
    
        func attemptRecovery(optionIndex recoveryOptionIndex: Int) -> Bool {
            if recoveryOptionIndex == 0 {//for "Retry"
                var result = false
                switch self {
                case .documentSavingError:
                    //Attempt "Retry" recovery for the failed operation, and return the result
                    //...
                    print("assume recovery attempt successfully finished...")
                    result = true
                //...
                }
                return result
            } else if recoveryOptionIndex == 1 {//for "Ignore"
                return true
            }
            fatalError("something wrong...")
        }
    }
    
  3. Modify the data(ofType:) method in Document.swift as to throw the error.

    override func data(ofType typeName: String) throws -> Data {
        //throw RecoverableError
        throw MyError.documentSavingError
    }
    
  4. You need one more trick in the current Document-based application...

    Create a new swift code to subclass NSApplication.

    import AppKit
    
    @objc(Application)
    class Application: NSApplication {
        override func presentError(_ error: Error, modalFor window: NSWindow, delegate: Any?, didPresent didPresentSelector: Selector?, contextInfo: UnsafeMutableRawPointer?) {
            //print(error)
            let underlyingError = (error as NSError).userInfo[NSUnderlyingErrorKey] as? Error ?? error
            //print(underlyingError)
            super.presentError(underlyingError, modalFor: window, delegate: delegate, didPresent: didPresentSelector, contextInfo: contextInfo)
        }
    }
    

    The current Document-based application wraps thrown RecoverableError in a usual NSError, so, without this, automatically generated NSDocumentErrorRecoveryAttempter does not work well.

    Do not forget to modify the Info.plist to designate this class as its Principal class.

  5. Build and run the app, then when a document window is presented, File > Save..., and click Save in the save sheet.

    You may find how your RecoverableError is working...


So, RecoverableError is useful when your app already implements the standard recovery feature described in the Error Handling Programming Guide. And its Introduction says clearly:

Important: The NSError class is available on both OS X and iOS. However, the error-responder and error-recovery APIs and mechanisms are available only in the Application Kit (OS X).

OOPer
  • 44,179
  • 5
  • 90
  • 123