Different error-handling techniques in Swift for both recoverable and non-recoverable errors.
Swift prioritizes compile-time safety to reduce runtime errors. However, failures can still occur. Let’s explore Swift’s error-handling tools, including the differences between preconditionFailure()
, assert()
, and throwing errors.
Recoverable Errors:
1. Returning nil or an error enum case:
enum DataLoadingError: Error {
case networkError
case invalidData
}
func loadData(from url: URL) -> Data? {
do {
let data = try Data(contentsOf: url)
return data
} catch {
print("Error loading data: \(error)")
return nil
}
}
In this example, the `loadData` function returns `nil` if an error occurs during data loading.
2. Throwing an error:
enum StringFormattingError: Error {
case emptyString
}
func formatString(_ input: String) throws -> String {
guard !input.isEmpty else {
throw StringFormattingError.emptyString
}
return input.uppercased()
}
do {
let formattedString = try formatString("Hello, World!")
print(formattedString)
} catch {
print("Error formatting string: \(error)")
}
Here, the `formatString` function throws an error if the input string is empty. The caller must catch and handle the error.
Non-Recoverable Errors:
1. Using assert() and assertionFailure():
class SomeClass {
var importantValue: Int
init(value: Int) {
assert(value > 0, "Value must be greater than 0")
self.importantValue = value
}
func doSomething() {
assertionFailure("This function should not be called")
}
}
let instance = SomeClass(value: 42)
instance.doSomething() // This line triggers an assertion failure in debug builds.
In this example, the `assert` statement checks that the `importantValue` is greater than 0, and `assertionFailure` indicates that a function should not be called.
2. Using precondition() and preconditionFailure():
func divide(_ dividend: Int, by divisor: Int) -> Int {
precondition(divisor != 0, "Divisor must not be zero")
return dividend / divisor
}
let result = divide(10, by: 0) // This line triggers a precondition failure.
The `precondition` statement checks that the divisor is not zero before performing the division.
3. Calling fatalError():
func performCriticalOperation() {
// Critical operation logic here…
fatalError("Critical error: Operation failed")
}
performCriticalOperation() // This line causes the application to terminate.
`fatalError` is used to indicate a critical, unrecoverable error, causing the application to terminate.
4. Calling exit():
func processCommandLineArguments() {
// Process command-line arguments…
if errorInArguments {
exit(1) // Exit with a non-zero status code indicating an error.
}
}
processCommandLineArguments() // Exits the process if there's an error in arguments.
`exit` is typically used in command-line tools and scripts to exit the process with a specified status code.
These examples illustrate how to handle both recoverable and non-recoverable errors in Swift using various error-handling techniques.