Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Specifying custom error type within the Result type in Swift 5

Tags:

swift

I am trying to create a Result variable with a custom error type with the builtin Result type in Foundation for Swift 5, but I can't quite get the type system to understand the kind of error I want to throw.

The code below does not compile.

import Foundation

enum CustomError: String, Error {
    case somethingBadHappened
}

struct Model {
    let value: Int
}

class Request {
    func execute(number: Int, completion: @escaping (Result<Model, CustomError>) -> Void) {
        let result = Result { () throws -> Model in
            if (number < 20) {
                throw CustomError.somethingBadHappened
            } else {
                return Model(value: number)
            }
        }
        // compiler complains here about: Cannot convert value of type 'Result<Model, Error>' to expected argument type 'Result<Model, CustomError>'
        completion(result)
    }
}

let request = Request()
request.execute(number: 19) { result in
    switch result {
    case .success(let value): print("Succeded with \(value)")
    case .failure(let error): print("Failed with \(error)")
    }

}

Changing the signature of the completion closure to completion: @escaping (Result<Model, Error>) -> Void works, but then I am not using the custom error type.

How can I make the type system understand I would like to use the custom error type?

like image 990
atineoSE Avatar asked Apr 21 '19 16:04

atineoSE


People also ask

How do I create a custom error in Swift?

To add a description to a new error type, extend the custom error to conform to CustomStringConvertible and add a property description : // For each error type return the appropriate description extension CustomError: CustomStringConvertible { public var description: String { switch self { case .

When would you use Swift Result type?

Swift provides a special type called Result that allows us to encapsulate either a successful value or some kind of error type, all in a single piece of data. So, in the same way that an optional might hold a string or might hold nothing at all, for example, Result might hold a string or might hold an error.

How do you convert error to NSError?

It should be possible to turn an arbitrary Swift enum that conforms to Error into an NSError by using the qualified type name as the domain key, the enumerator as the error code, and turning the payload into user data.


1 Answers

Changing the signature of the completion closure to completion: @escaping (Result<Model, Error>) -> Void works, but then I am not using the custom error type.

Yes, you are! Change the signature in exactly that way, so that you compile, and then run your code. When we get to this line:

case .failure(let error): print("Failed with \(error)")

... we print "Failed with somethingBadHappened". That proves that your CustomError.somethingBadHappened instance came through just fine.

If the problem is that you want to separate out your CustomError explicitly, then separate it out explicitly as you catch it:

case .failure(let error as CustomError): print(error)
default : fatalError("oops, got some other error")

Or if you want to winnow it down still further and catch only the .somethingBadHappened case, specify that:

case .failure(CustomError.somethingBadHappened): print("Something bad happened")
default : fatalError("oops, got some other error")

Those examples are artificial, but they demonstrate what they are intended to demonstrate — that your CustomError instance is coming through with full integrity.

like image 189
matt Avatar answered Sep 23 '22 01:09

matt