Error handling is a crucial aspect of programming in Swift, allowing developers to manage errors gracefully and maintain robust applications. Swift uses a combination of do
, try
, and catch
keywords to handle errors. This article presents three diverse examples of error handling in Swift to help you understand its implementation in real-world scenarios.
When working with file systems, errors can arise from missing files or incorrect paths. This example demonstrates how to handle such errors when attempting to read content from a file.
In this use case, we want to read a text file and handle potential errors related to file access.
import Foundation
enum FileError: Error {
case fileNotFound(String)
case unreadable(String)
}
func readFile(at path: String) throws -> String {
let fileURL = URL(fileURLWithPath: path)
guard FileManager.default.fileExists(atPath: path) else {
throw FileError.fileNotFound(path)
}
do {
let content = try String(contentsOf: fileURL, encoding: .utf8)
return content
} catch {
throw FileError.unreadable(path)
}
}
let filePath = "path/to/file.txt"
do {
let content = try readFile(at: filePath)
print(content)
} catch FileError.fileNotFound(let path) {
print("Error: File not found at \(path)")
} catch FileError.unreadable(let path) {
print("Error: Unable to read file at \(path)")
} catch {
print("An unexpected error occurred: \(error)")
}
In this example, we define a custom error type FileError
to categorize potential errors. The readFile
function checks if the file exists and attempts to read it, throwing appropriate errors if issues are encountered.
When making network requests, various issues such as connectivity problems or invalid responses can occur. This example illustrates how to handle errors during a network call using URLSession.
In this case, we want to fetch data from a web API and correctly handle possible network errors.
import Foundation
enum NetworkError: Error {
case badURL(String)
case requestFailed(Error)
case invalidResponse
}
func fetchData(from urlString: String, completion: @escaping (Result<Data, NetworkError>) -> Void) {
guard let url = URL(string: urlString) else {
completion(.failure(.badURL(urlString)))
return
}
let task = URLSession.shared.dataTask(with: url) { data, response, error in
if let error = error {
completion(.failure(.requestFailed(error)))
return
}
guard let httpResponse = response as? HTTPURLResponse, (200...299).contains(httpResponse.statusCode) else {
completion(.failure(.invalidResponse))
return
}
if let data = data {
completion(.success(data))
}
}
task.resume()
}
let url = "https://api.example.com/data"
fetchData(from: url) { result in
switch result {
case .success(let data):
print("Data received: \(data)")
case .failure(let error):
switch error {
case .badURL(let urlString):
print("Error: Bad URL \(urlString)")
case .requestFailed(let error):
print("Error: Request failed with error: \(error)")
case .invalidResponse:
print("Error: Received invalid response")
}
}
}
This example uses a completion handler to return the outcome of the network request, encapsulated in a Result
type. The NetworkError
enum captures various error scenarios, providing clear feedback on what went wrong during the request.
Parsing JSON data can often lead to errors, especially when the data structure does not match expectations. This example shows how to handle errors when decoding JSON into Swift structs.
In this scenario, we want to decode a JSON response from a web API into a Swift struct and handle potential decoding errors.
import Foundation
struct User: Codable {
let id: Int
let name: String
}
enum DecodingError: Error {
case invalidData
}
func decodeUser(from jsonData: Data) throws -> User {
let decoder = JSONDecoder()
do {
let user = try decoder.decode(User.self, from: jsonData)
return user
} catch {
throw DecodingError.invalidData
}
}
let jsonString = "{\"id\": 1, \"name\": \"John Doe\"}"
if let jsonData = jsonString.data(using: .utf8) {
do {
let user = try decodeUser(from: jsonData)
print("User: \(user.name)")
} catch DecodingError.invalidData {
print("Error: Failed to decode user data.")
} catch {
print("An unexpected error occurred: \(error)")
}
}
Here, we define a User
struct that conforms to the Codable
protocol for easy JSON decoding. The decodeUser
function attempts to decode the JSON data and throws an error if it fails, allowing for precise error handling during the decoding process.
By understanding and implementing these examples of error handling in Swift, you can create applications that are more resilient and user-friendly, effectively managing errors and improving overall user experience.