Swift Error Handling
Swift error handling is a very important aspect of writing better code. Swift try statement is used for error handling in swift programs. Let’s get started by launching XCode playground.
- Swift Error Handling is all about handling the failing conditions gracefully
- An error can lead to run time errors or changes in the flow of the program.
We come across different kinds of errors in our projects:
- Logic Errors
- Type Conversion Errors.
- External Errors such as FileNotFound etc.
The brute force way to handle errors is by using if-else statements where we check each and every possible error. But this can lead to bloated codes with too many nested conditions.
In Swift, Errors are just values of a certain type. Swift does not support checked exceptions.
Swift Error Protocol
- Error Protocol is just a type for representing error values that can be thrown.
- Swift requires you to create a custom Error type. Typically an Enum is used which conforms to the Error Protocol.
- The Error Protocol is more or less empty. Hence you don’t need to override anything from them. Error Protocol is a must for Error Handling and creating Error types.
Let’s create a basic enum which conforms to this Error Protocol.
enum UserDetailError: Error { case noValidName case noValidAge }
Now let’s use this Error Type in our classes and functions.
throws and throw
If a function or an initializer can throw an error, the throws modifier must be added in the definition itself right after the paratheses and just before the return type.
func userTest() throws -> <ReturnType> { }
The throws keyword would propagate the error from the function to the calling code. Otherwise, a non-throwing function must handle the error inside that function’s code itself.
throw keyword is used for throwing errors from the error type defined. Let’s look at an example demonstrating throws and throw in a function:
func userTest() throws { if <condition_matches> { //Add your function code here } else{ throw UserDetailError.noValidName } }
In Error Handling, guard let is useful in the sense that we can replace the return statement in the else block with the throwing error. This prevents too many if-else conditions.
Let’s look at it with the example below.
func userTest(age: Int, name: String) throws { guard age > 0 else { throw UserDetailError.noValidAge } guard name.count > 0 else { throw UserDetailError.noValidName } }
Note: You cannot add the Error type after the throws keyword in Swift.
In the above code, if the condition in the guard let fails it’ll throw an error and the function would return there itself.
Let’s look at how to handle these errors.
Swift try, do-catch
- In Swift, contrary to Java, do-catch block is used to handle errors in place of try-catch.
- Every function that has throws needs to set in the try statement since it has a potential error.
- Swift try statement is executed only when it is inside the do-catch block as shown below.
do { try userTest(age: -1, name: "") } catch let error { print("Error: \(error)") }
Below image shows the output of the above program. Alternatively, we can do this:
do { try userTest(age: -1, name: "") } catch UserDetailError.noValidName { print("The name isn't valid") } catch UserDetailError.noValidAge { print("The age isn't valid") } catch let error { print("Unspecified Error: \(error)") }
Throwing Errors in Initializers
We can add throws in the initializer in the following way.
import Foundation enum StudentError: Error { case invalid(String) case tooShort } class Student { var name: String? init(name: String?) throws { guard let name = name else{ throw StudentError.invalid("Invalid") } self.name = name } func myName(str: String) throws -> String { guard str.count > 5 else { throw StudentError.tooShort } return "My name is \(str)" } }
Now to initialize the class we normally do the following, right?
var s = Student(name: nil) //compiler error
Since the initializer is throwing errors we need to append try keyword as shown below.
do { var s = try Student(name: nil) } catch let error { print(error) }
Swift try, try? and try!
- Swift try is the most basic way of dealing with functions that can throw errors.
- try? is used to handle errors by converting the error into an optional value. This way if an error occurs, the function would return nil and we known Optionals can be nil in Swift. Hence for try? you can get rid of do-catch block.
- try! is used to assert that the error won’t occur. Should be only used when you’re absolutely sure that the function won’t throw an error. Like try?, try! works without a do-catch block.
var t1 = try? Student(name: nil) var t2 = try! Student(name: "BaluTutorial")