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?
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.
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
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