Use Cases
In the Lesson03-Result-Type playground, click Next to open the Lesson03-Result-b page.
Convert Throwing Expression to Result
Result has a nifty initializer init(catching:) to convert any throwing expression to a Result. You can use this to collect results while looping over a collection.
Returning to the bakery example’s orderPastry(item:amountRequested:flavor:), use init(catching:) to loop over itemsForSale, calling orderPastry to create an array of Result<Int, Error>:
let bakery = Bakery()
var results = [Result<Int, Error>]() // 1
for (key, value) in bakery.itemsForSale {
let result = Result { // 2
try bakery.orderPastry(
item: key,
amountRequested: 1,
flavor: value.flavor)
}
results.append(result) // 3
}
- Create an empty array of
Result<Int, Error>. When using this initializer, the.failurevalue isany Error, so you can’t declare an array ofResult<Int, BakeryError>. - While looping over the
itemsForSaledictionary, callorderPastry(item:amountRequested:flavor:)with arguments that produce failure only ifamountRequestedis greater thannumberOnHand. Instead of adoblock, pass the throwing expression to theResultinitializerinit(catching:)and store the result. - Append
resultto the array ofResult<Int, Error>.
To test this code, modify itemsForSale to ensure failure for at least one item:
let itemsForSale = [
"Cookie": Pastry(flavor: "ChocolateChip", numberOnHand: 20),
"PopTart": Pastry(flavor: "WildBerry", numberOnHand: 0), // guarantee error
"Donut" : Pastry(flavor: "Sprinkles", numberOnHand: 24),
"HandPie": Pastry(flavor: "Cherry", numberOnHand: 6)
]
Run the code and show results:
You get .success for three of the items, but PopTart results in .failure with associated value .tooFew and numberOnHand 0.
Completion Handlers
Click Next to open the Lesson03-Result-c page.
In the days before Swift concurrency, asynchronous APIs like URLSession used completion handlers to execute code when the asynchronous operation completed. The function would supply data, response, error values to the completion handler, but you couldn’t throw errors from the completion handler.
The Result type provides a solution for asynchronous functions with completion handlers. If you’re still using these instead of Swift concurrency’s async/await, this example from Saurabh Patel’s How to Implement Result Type in Swift: Step-by-Step Tutorial shows how you can use Result to capture error information.
First, here’s an Error enum and a typealias for the Result type:
enum DataFetchError: Error {
case networkFailure
case dataCorrupted
case unauthorized
}
// Define a Result type for fetching data
typealias FetchResult = Result<Data, DataFetchError>
fetchData(from:completion:) fetches data, and its last parameter is a custom completion handler that is called within the completion handler of the asynchronous URLSession method dataTask(with:completion:):
func fetchData(
from urlString: String,
completion: @escaping @Sendable (FetchResult) -> Void) { // 1
guard let url = URL(string: urlString) else {
completion(.failure(.networkFailure)) // 2
return
}
// Simulate fetching data
URLSession.shared.dataTask(with: url) { data, response, error in // 3
guard let data = data else {
completion(.failure(.dataCorrupted)) // 4
return
}
completion(.success(data)) // 5
}.resume()
}
- The function’s
completionparameter is a closure that takes aResult<Data, DataFetchError>argument and returns nothing. - If
urlStringdoesn’t create a valid URL, you pass this error tocompletion. - The completion handler of
dataTask(with:completion:). - If
dataisnil, you pass this error tocompletion. - If there’s
data, you pass this value tocompletion.
Now, your custom completion handler receives data when fetchData(from:completion:) is successful and useful error information when the function call fails.
fetchData(from: "https://example.com") { result in
switch result {
case .success(let data):
print("Successfully fetched \(data.count) bytes.")
case .failure(let error):
switch error {
case .networkFailure:
print("Network failure - Please check your connection.")
case .dataCorrupted:
print("Data corrupted - Unable to process the data received.")
case .unauthorized:
print("Unauthorized - Access denied.")
}
}
}
The completion argument of fetchData(from:completion:) is written as a trailing closure, which receives result from fetchData(from:completion:). It can switch over the Result and .failure cases, processing data in the .success case, and making appropriate suggestions in the .failure cases.