16

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?

JAL
  • 41,701
  • 23
  • 172
  • 300
IgnazioC
  • 4,554
  • 4
  • 33
  • 46
  • Unfortanely, you have to check dokumentation (API). Be careful, there is no exception throwing but error throwing . If you are looking for a way how to organize the flow of your code, use proper checking instead. Catching throwing error is your last chance to recover from it, you are not able to recover from exception. – user3441734 Jan 19 '16 at 12:58
  • [See also this related question and the accepted answer.](http://stackoverflow.com/questions/31977738/how-to-find-the-kind-of-errors-a-method-may-throw-and-catch-them-in-swift) – Suragch Mar 29 '16 at 07:06
  • 1
    This is probably one of the dumbest parts of Swift, which I do actually enjoy programming in. It's one thing to rely on documentation to describe errors thrown, but when Apple's own documentation doesn't even list the errors or codes you should attempt to handle, then it becomes a big shrug and guessing game. – Nick Bedford May 08 '20 at 00:42

3 Answers3

3

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.

JAL
  • 41,701
  • 23
  • 172
  • 300
  • 1
    Except, there is 'pure' swift´s error ... The only requirement is that throwing error conforms to ErrorType protokol. It doesn't mean it is an NSError object. – user3441734 Jan 19 '16 at 13:05
  • @user3441734 I don't see any "pure" errors being thrown in the Cocoa Touch APIs or docs, this was just an example using a method provided by Apple. Can you provide me an example in the Docs of a "pure" Swift error? Additionally, [`ErrorType` is adopted by `NSError`.](https://developer.apple.com/library/ios/documentation/Swift/Reference/Swift_ErrorType_Protocol/index.html) – JAL Jan 19 '16 at 13:08
  • Either way, there aren't different types of errors being throw. All errors would be of type `NSError` or conform to the `ErrorType` protocol. You would have to look at the error's specific description or error code to figure out what went wrong. – JAL Jan 19 '16 at 13:10
1

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

AndreG
  • 1,948
  • 1
  • 9
  • 13
  • I had to cast it to NSError, indeed – Teodor Ciuraru Jul 01 '18 at 15:50
  • It seems like domain usually pertain to the whole framework. For example I'm trying to figure out the errors for Core Bluetooth. And there are CBATTError and CBError enumerations. But I can't know exactly which error can be given by `centralManager(_:didFailToConnect:error:)`, can I? – huggie Dec 02 '18 at 16:53
  • Do not hardcode error codes. Use the provided constants. Replace `257` with `NSFileReadNoPermissionError` and replace `260` with `NSFileReadNoSuchFileError`. – rmaddy Jun 10 '19 at 15:47
-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

JAL
  • 41,701
  • 23
  • 172
  • 300
John Wong
  • 342
  • 2
  • 10
  • 6
    the question is how can I get the list of all the possible errors thrown by a function. If I don't know the list, I cannot specify which ones I know how to deal with. – IgnazioC Jan 19 '16 at 12:51