Swift allows us to throw, propagate and catch errors across functions. Here we’ll take a look at how this works with a few examples.Functions that throws errors.There are several already implemented functions that we use on a daily basis that throws errors. You can see them on Xcode when it contains the keyword throws at the end.
To call these functions we need to handle any error that it might throw, as we are going to see later on. We can also implement our own function that throws an error. For that we need to add the keyword throws at the end, like this:
func functionName() throws -> Int {}
The error could be anything we like as long as it conforms to the Error protocol.
The Do – Try – Catch syntax
Let’s get started by writing a function that opens an image to be converted to Data. The initializer for Data can throw an error.
So Xcode will ask us to use try before calling the initializer.
Now we have two options: Use try? and don’t handle the error. With this the result would be an optional Implement a Do-Try-Catch to handle the errorUsing optional try We can simply add a try? which will not throw an error but the response would be an optional. If there’s an error the result would be nil.
let imageData = try? Data(contents0f: URL(filePath: "imagePath"))
Implementing a do-try-catch
With this syntax we have a successful block and a failure block.
do {
let imageData = try Data(contents0f: URL(filePath: "imagePath"))
// Successful block
// Handle the image data
} catch {
// Failure block
// Handles the error
print(error.localizedDescription)
}
Create you own error
You can create your own error Enum that works according to what your application needs. The enum must conform to the Error protocol. This is an example of an error enum for a login feature:
enum LoginError: Error {
case incompleteForm
case shortPassword
case invalidEmail
case invalidPassword
}
How to propagate an error
Instead of handling the error, our function can throw them by propagating to the function that called our function.
func getUserData throws {
do {
let user = try getUser ()
} catch {
throw LoginError.differentError
}
}
Some use cases in iOS development
Implementing a login function that throws errors
With the same LoginError enum we can implement a login function that throws different errors.
func login(email: String, password: String) throws {
if email.isEmpty || password. isEmpty {
throw LoginError.incompleteForm
}
if password.count < 6 {
throw LoginError.shortPassword
}
if !isValid(email: email) {
throw LoginError.inavlidEmail
}
if !isValid(password: password) {
throw LoginError.invalidPassword
}
}
And another function catches and handles those errors. We need to implement the last catch is not specific to an error.
do {
try login(email: email, password: password)
// Login succeeded
} catch LoginError.incompleteForm {
alertMessage = "Incomplete form"
} catch LoginError.shortPassword {
alertMessage = "Password is too short"
} catch LoginError.inavlidEmail {
alertMessage = "Invalid email"
} catch LoginError.invalidPassword {
alertMessage = "Invalid password"
} catch {
alertMessage = "Unable to login" {
}
JSON parsing
Decoding JSON data for the details of a user.
do {
let userDetails = try JSONDecoder).decode(UserDetails.self, from: data)
completion(.success(userDetails))
} catch {
print(error.localizedDescription)
completion (.failure(parseError))
}
Conclusion
Error handling is an essential part of developing apps. Knowing how to do so will greatly improve your ability to work on different user cases and properly handle any error.