Swift: Throwing Functions
With throwing functions, you can easily handle the errors
In Swift 2.0, Apple introduced the throws
keyword in Swift. This keyword is so helpful for us on error handling. Basically throws
keyword allows us to throw errors inside a method, property, class, or struct.
After async/await came out with Swift 5.5, throwing functions became more popular. To be honest, I love throwing functions because they are so simple, understandable, and looks more readable.
In this article, we will learn all the use cases of throwing functions.
Using throws Keyword
All it takes to create a throwing function is to write throws
keyword to a method just before the return statement.
func exampleMethod(_ first: String, _ second: String) throws {
print(first, second)
}
To call this method, we need to use try
keyword before writing method.
try exampleMethod("First", "Second")
Throwing Errors
We use the “throw” keyword to throw errors inside the methods, classes, structs, and properties. To throw an error, we must define our Error
types like this:
enum ExampleError: Error {
case invalid
case uncorrect
}
Then we can use this ExampleError
type into exampleMethod
as follows:
func exampleMethod(_ first: String, _ second: String) throws {
if first == second {
throw ExampleError.invalid
} else if first == "" || second == "" {
throw ExampleError.uncorrect
}
print("First string: \(first), second string: \(second).")
}
So, if you give same String
values to first
and second
parameters, you will see the invalid
error on the terminal.
Throwing Initializer in Swift
Like any method, we can make throwing initializers. In this way, we can throw an error according to the value of the desired parameters after define an object.
For example, you might want to validate an email before creating a User
object.
class User {
// MARK: - Enumerations
enum EmailError: Error {
case invalid
case uncorrect
}
// MARK: - Properties
let email: String
let storedEmails = ["tcook@apple.com", "sjobs@apple.com", "cfederighi@apple.com"]
// MARK: - Life Cycle
init(email: String) throws {
if storedEmails.contains(email) {
throw EmailError.invalid
} else if email.count < 5 || email == "" {
throw EmailError.uncorrect
}
self.email = email
}
}
let user = try User(email: "contact.canbalkaya@gmail.com")
do-catch Statement
To catch a thrown error, we need to use do-catch statement. In this way, although an error is thrown, our app does not crash, but we can understand that an error is thrown.
do {
let user = try User(email: "tcook@apple.com")
print("Created user with name \(user.email)")
} catch {
print("User creation failed with error: \(error)")
}
When you run the above example, you will notice that when an error is thrown, the codes written in the catch block will run.
The above example is the most basic usage of do-catch statement. So, we can do more: We can catch the errors in separated blocks to display different alerts.
do {
let user = try User(email: "tcook@apple.com")
print("Created user with name \(user.email)")
} catch User.EmailError.uncorrect {
print("Email is uncorrect.")
} catch User.EmailError.invalid {
print("Email is invalid.")
} catch {
print("An another error is appeared.")
}
We can also catch two or more specific error types. In this case, you can use lists in your catch statements:
do {
let user = try User(email: "tcook@apple.com")
print("Created user with name \(user.email)")
} catch User.EmailError.uncorrect, User.EmailError.invalid {
print("Email is uncorrect or invalid.")
} catch {
print("An another error is appeared.")
}
try? & try!
If you prefer not to cache any thrown errors, you can use try?
. In this way, the app does not crash even though an error is thrown.
let user = try? User(email: "sjobs@apple.com")
print(user?.email)
If an error is given regarding the email
property of the User
object, the email
property will be nil
.
If you want the app to crash every time an error is thrown, you should use try!
. This will fail your app just like a fatalError
method.
let user = try! User(email: "sjobs@apple.com")
print(user.email)
Conclusion
Throwing functions can be very useful in writing tests and async methods. After Swift 5.5, It seems to use more throwing functions now than before.
We will continue to publish articles about throws
and rethrows
in the future.