How get the list of errors thrown by a function?

2019-09-13 08:37发布

问题:

With Swift now some functions are marked with throws, and this force the developers to call the function inside a do - try catch block. But how the developer can know the list of different exceptions thrown by that function?

As reference, here is a line of Java code:

static void  employeeAge(int age) throws MyExceptionA,MyExceptionB

Here is clear that the exceptions are 2 MyExceptionA and MyExceptionB and the developer can decide to act differently depends of the error.

Can we achieve the same on Swift?

回答1:

When the Swift docs says a function throws, they mean that it throws an ErrorType (in Cocoa APIs usually an NSError), not an exception.

Consider the following do-try-catch flow for NSFileManager's createDirectoryAtPath:

let documentsPath = NSSearchPathForDirectoriesInDomains(.DocumentDirectory, .UserDomainMask, true)[0]

do {
    try NSFileManager.defaultManager().createDirectoryAtPath(documentsPath, withIntermediateDirectories: false, attributes: nil)
} catch { 
    // 'error' variable automatically populated
    print(error)
    print(error.dynamicType)
}

createDirectoryAtPath will fail because the documents directory already exists. Logging the dynamicType of the error shows that it is in fact an NSError object:

Error Domain=NSCocoaErrorDomain Code=516 "The file “Documents” couldn’t be saved in the folder “35B0B3BF-D502-4BA0-A991-D07568AB87C6” because a file with the same name already exists." UserInfo={NSFilePath=/Users/jal/Library/Developer/CoreSimulator/Devices/E8A35774-C9B7-42F0-93F1-8103FBBC7118/data/Containers/Data/Application/35B0B3BF-D502-4BA0-A991-D07568AB87C6/Documents, NSUnderlyingError=0x7fa88bd14410 {Error Domain=NSPOSIXErrorDomain Code=17 "File exists"}}

NSError

In order to see the different types of errors a function can throw, you would have to examine the error for information to determine the type of error thrown, and how to handle each error. In the case of NSError this would be its domain, code, and description.

In this particular case, a directory already exists at that path, so the file manager cannot create a new directory. An example of another reason why this operation could fail would be if the file manager did not have write access. That would be error code 256.



回答2:

I had the exact same question as the OP. Since no one really answered the question as he asked (and I as well), here goes my contribution.

In Swift 3 and Xcode 8.3.3 you would do as follows to treat the individual exceptions. Below I will give you an example with FileManager.

First you will have only one catch block to catch whatever error the method throws at you. Then you will cast that error as an NSError. Contrary to the Error protocol in Swift, NSError is a REAL error class. Then you can extract that error's code in a switch statement. You will have to know what domain that method throws error from and then find the error codes in the appropriate header file.

In my example below, the file related errors are thrown in the NSCocoaErrorDomain and these errors codes are defined/listed in Foundation/FoundationErrors.h. In my computer, they are located at

/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX.sdk/System/Library/Frameworks/Foundation.framework/Versions/C/Headers/FoundationErrors.h

for macOS apps and at

/Applications/Xcode.app/Contents/Developer/Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS.sdk/System/Library/Frameworks/Foundation.framework/Headers/

for iPhone apps.

So here is an example:

let home = FileManager.default.homeDirectoryForCurrentUser
let file = home.appendingPathComponent("file")
do {
    let loadedString = try String(contentsOf: file)
}
catch {
    let realError = error as NSError // As weird as it looks, Xcode actually wants this forced conversion
    print(realError.localizedDescription)
    switch realError.code {
    case 257: // No permission
        handleNoPermission()
    case 260: // File not found
        handleFileNotFound()
    default:
        handleUndefinedError()
    }
}

The .localizedDescription contains a user friendly message in your user's language about that error. If file is not found above it prints: The file “file” couldn’t be opened because there is no such file. in English. It is meant to be used directly in the error dialogs you present to your user.

You may also find more information about what error is thrown by each domain here: https://developer.apple.com/library/content/documentation/Cocoa/Conceptual/ErrorHandlingCocoa/ErrorObjectsDomains/ErrorObjectsDomains.html



回答3:

You write a pattern after catch to indicate what errors that clause can handle.

do {
    try expression
    statements
} catch pattern 1 {
    statements
} catch pattern 2 where condition {
    statements
}

See section Handling Errors Using Do-Catch of Swift Programming Language