Result Type
Open the Lesson03-Result-Type playground. Start on the Lesson03-Result-a page.
Basic Use of Result Type
The easiest way to see how to use Result is to use it as the return value of a function. Code that calls this function can check whether its return value is .success or .failure, then take appropriate action.
Here’s an example of a function that takes an array of Int and returns an array containing the even numbers in that array. If the input or output array is empty, the function throws an error:
enum EvenNumberError: Error {
case emptyArray
}
// Function returns [Int], throws EvenNumberError
func evenNumbersThrow(in collection: [Int]) throws(EvenNumberError) -> [Int] {
guard !collection.isEmpty else { throw .emptyArray }
let evenNumbers = collection.filter { number in number % 2 == 0 }
if evenNumbers.isEmpty {
throw .emptyArray
} else {
return evenNumbers
}
}
Modify this function to perform the same operation, but return a Result type:
func evenNumbers(in collection: [Int]) -> Result<[Int], EvenNumberError> { // 1
guard !collection.isEmpty else {
return .failure(.emptyArray) // 2
}
let evenNumbers = collection.filter { number in number % 2 == 0 }
if evenNumbers.isEmpty {
return .failure(.emptyArray)
} else {
return .success(evenNumbers) // 3
}
}
- You return
Result<[Int], EvenNumberError>instead of[Int]: The.successvalue type is[Int], and the.failurevalue type isEvenNumberError. - Instead of throwing
.emptyArray, youreturn .failure(.emptyArray). - Instead of returning
evenNumbers, you pass it as the.successvalue.
You can test evenNumbers(in:) with the following arrays:
let emptyArray = [Int]()
let numbers: [Int] = [2,3,6,8,10]
let oddNumbers: [Int] = [1,3,5]
First, call evenNumbers(in:) with emptyArray:
let result = evenNumbers(in: emptyArray)
Add this code to switch on result:
switch result {
case .success(let array):
print(array)
case .failure(let error):
print("Array is empty")
}
And add this code to try result.get in a do-catch block:
do {
let array = try result.get()
print(array)
} catch {
print(error)
}
Now, run the code:
The sidebar shows result is .failure(.emptyArray), case .failure prints “Array is empty\n”, and the catch block prints “emptyArray\n”.
Replace emptyArray with numbers and run the code again:
Now, result is .success([2, 6, 8, 10]), and case .success and the do block print [2, 6, 8, 10]\n.
Replace numbers with oddArray and run the code again:
As you expect, result is .failure(.emptyArray), case .failure prints “Array is empty\n”, and the catch block prints “emptyArray\n”.
Task.result
Task has a result property whose type is Result. Here’s an example from Hacking With Swift’s Understanding Swift’s Result Type
func fetchReadings() async {
let fetchTask = Task {
let url = URL(string: "https://hws.dev/readings.json")!
let (data, _) = try await URLSession.shared.data(from: url)
let readings = try JSONDecoder().decode([Double].self, from: data)
return "Found \(readings.count) readings"
}
let result = await fetchTask.result // Note: this doesn't throw, so you don't try
}
Instead of calling the throwing functions in a do block and handling thrown errors in catch blocks, you call the functions in a named Task, then await the task’s result.
Now, the same as for the return value of evenNumbers(in:), write code to try result.get() in a do-catch block:
do {
let output = try result.get()
} catch {
let output = "Error: \(error.localizedDescription)"
}
And also switch on result:
switch result {
case .success(let str):
let output = str
case .failure(let error):
let output = "Error: \(error.localizedDescription)"
}
Although Result entered the language to solve “old” concurrency problems, and some think Swift concurrency removes any need for Result, it seems Swift concurrency still has a place for Result!