Error Handling

It is often useful to have a way of representing the possibility of failure when creating methods. You have seen one way of representing failure throughout this book with the use of optionals. Optionals provide a simple way to represent failure when you do not care about the reason for failure. Consider the creation of an Int from a String.

let theMeaningOfLife = "42"
let numberFromString = Int(theMeaningOfLife)

This initializer on Int takes a String parameter and returns an optional Int (an Int?). This is because the string may not be able to be represented as an Int. The code above will successfully create an Int, but the following code will not:

let pi = "Apple Pie"
let numberFromString = Int(pi)

The string Apple Pie cannot be represented as an Int, so numberFromString will contain nil. An optional works well for representing failure here because you do not care why it failed. You just want to know whether it was successful.

When you need to know why something failed, an optional will not provide enough information. Think about removing the image from the filesystem – why might that fail? Perhaps there is no file at the specified URL, or the URL is malformed, or you do not have permission to remove that file. There are a number of reasons this method could fail, and you might want to handle each case differently.

Swift provides a rich error handling system with compiler support to ensure that you recognize when something bad could happen. You already saw this when the Swift compiler told you that you were not handling a possible error when removing the file from disk.

If a method could generate an error, its method signature needs to indicate this using the throws keyword. Here is the method definition for removeItem(at:):

func removeItem(at URL: URL) throws

The throws keyword indicates that this method could throw an error. (If you are familiar with throwing exception in other languages, Swift’s error handling is not the same as throwing exception.) By using this keyword, the compiler ensures that anyone who uses this method knows that this method can throw an error – and, more importantly, that the caller also handles any potential errors. This is how the compiler was able to let you know that you are not handling errors when attempting to remove a file from disk.

To call a method that can throw, you use a do-catch statement. Within the do block, you annotate any methods that might throw an error using the try keyword to reinforce the idea that the call might fail.

In ImageStore.swift, update deleteImage(forKey:) to call removeItem(at:) using a do-catch statement.

func deleteImage(forKey key: String) {
    cache.removeObject(forKey: key as NSString)

    let url = imageURL(forKey: key)
    FileManager.default.removeItem(at: url)
    do {
        try FileManager.default.removeItem(at: url)
    } catch {

    }
}

If a method does throw an error, then the program immediately exits the do block; no further code in the do block is executed. At that point, the error is passed to the catch block for it to be handled in some way.

Now, update deleteImage(forKey:) to print out the error to the console.

func deleteImage(forKey key: String) {
    cache.removeObject(forKey: key as NSString)

    let url = imageURL(forKey: key)
    do {
        try FileManager.default.removeItem(at: url)
    } catch {
        print("Error removing the image from disk: (error)")
    }
}

Within the catch block, there is an implicit error constant that contains information describing the error. You can optionally give this constant an explicit name.

Update deleteImage(forKey:) again to use an explicit name for the error being caught.

func deleteImage(forKey key: String) {
    cache.removeObject(forKey: key as NSString)

    let url = imageURL(forKey: key)
    do {
        try FileManager.default.removeItem(at: url)
    } catch let deleteError {
        print("Error removing the image from disk: (errordeleteError)")
    }
}

There is a lot more that you can do with error handling, but this is the basic knowledge that you need for now. We will cover more details as you progress through this book.

Build and run the application now that the ImageStore is complete. Take a photo for an item and exit the application to the Home screen (on the simulator, select HardwareHome or press Shift-Command-H; on a hardware device simply press the Home button). Launch the application again. Selecting that same item will show all its saved details – including the photo you just took.

Notice that the images are saved immediately after being taken, while the instances of Item are saved only when the application enters the background. You save the images right away because they are just too big to keep in memory for long.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset