Swift Result Type
The Result type is a value that holds either a success value or a failure error. A function returns a Result when the caller should receive the outcome as plain data.
A function returns a success
The fetch function returns a Result and never throws. The switch binds the text inside the success case and prints it.
enum FetchError: Error {
case notFound
}
func fetch(id: Int) -> Result<String, FetchError> {
if id == 1 {
return .success("Page one")
}
return .failure(.notFound)
}
let result = fetch(id: 1)
switch result {
case .success(let text):
print(text)
case .failure(let error):
print(error)
}A function returns a failure
The same fetch function runs with an unknown id here. The switch binds the error inside the failure case, and printing it shows the case name.
enum FetchError: Error {
case notFound
}
func fetch(id: Int) -> Result<String, FetchError> {
if id == 1 {
return .success("Page one")
}
return .failure(.notFound)
}
let result = fetch(id: 9)
switch result {
case .success(let text):
print(text)
case .failure(let error):
print(error)
}A Result built from a throwing call
The load function still throws its error. Wrapping the try call in Result braces turns the outcome into a failure case that the switch can read.
enum FetchError: Error {
case notFound
}
func load(id: Int) throws -> String {
if id == 1 {
return "Page one"
}
throw FetchError.notFound
}
let result = Result { try load(id: 9) }
switch result {
case .success(let text):
print(text)
case .failure(let error):
print(error)
}Results stored in an array
Three fetch results sit in one array in call order. The loop switches over each element in turn and prints one summary line per element.
enum FetchError: Error {
case notFound
}
func fetch(id: Int) -> Result<String, FetchError> {
if id == 1 {
return .success("Page one")
}
return .failure(.notFound)
}
let results = [fetch(id: 1), fetch(id: 9), fetch(id: 1)]
for result in results {
switch result {
case .success(let text):
print("OK \(text)")
case .failure(let error):
print("Failed \(error)")
}
}Example
enum ParseError: Error {
case badText
}
func parseLevel(text: String) -> Result<Int, ParseError> {
if let number = Int(text) {
return .success(number)
}
return .failure(.badText)
}
let good = parseLevel(text: "4")
switch good {
case .success(let level):
print("Level \(level)")
case .failure(let error):
print(error)
}
let bad = parseLevel(text: "four")
switch bad {
case .success(let level):
print("Level \(level)")
case .failure(let error):
print(error)
}Key points
- A function can return a Result instead of throwing an error.
- The success case holds the value the function worked out.
- The failure case holds the error that stopped the work.
- A switch over a Result can bind the value or the error inside each case.
- A Result can be built from a throwing call by writing the try call inside the Result braces.
- Results can be stored in an array and read back in array order.
In this example, the parseLevel function returns a success holding the number when the text converts cleanly. It returns a failure holding badText when the text does not convert. The first switch binds the level 4 and prints it with its label. The second switch binds the error, and printing it shows badText.
Try it Yourself
Edit the code, then press Run.
Running is simulated
Exercise
A function returns a Result and the work fails. Which case holds the outcome?