在Swift 5的Result类型中指定自定义错误类型

时间:2019-04-21 16:38:01

标签: swift

我正在尝试创建一个带有自定义错误类型的Result变量,该错误类型具有Foundation for Swift 5中的内置Result类型,但是我无法完全使用类型系统来理解我想要的错误类型扔。

下面的代码无法编译。

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)")
    }

}

可以将完成闭包的签名更改为completion: @escaping (Result<Model, Error>) -> Void,但是我没有使用自定义错误类型。

如何使类型系统理解我想使用自定义错误类型?

3 个答案:

答案 0 :(得分:2)

为第二个答案道歉,但需要对fphilipe的答案进行纠正。

可以使用init(catching:)来形成结果,然后将其作为Result<Model, CustomError>返回。这就是mapError的目的!像这样:

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 NSError()
            } else {
                return Model(value: number)
            }
        }.mapError { err in
            return CustomError.somethingBadHappened
        }
        completion(result)
    }
}

我必须抛出 something 才能形成初始的Result<Model, Error>,所以我只是抛出NSError作为一种占位符。但是随后出现mapError并将其转换为Result<Model, CustomError>mapError的强大之处在于它仅更改发生故障时发生的情况。

因此我们能够保留代码的原始形式。

答案 1 :(得分:1)

  

将完成闭包的签名更改为completion: @escaping (Result<Model, Error>) -> Void可以,但是我没有使用自定义错误类型。

是的,你是!完全以这种方式更改签名,以便您进行编译,然后运行代码。当我们到达这一行时:

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

...我们打印"Failed with somethingBadHappened"。证明您的CustomError.somethingBadHappened实例通过得很好。

如果问题是您想显式地分离出CustomError,则在捕获时将其显式地分离出来:

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

或者,如果您想进一步了解和发现.somethingBadHappened情况,请指定:

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

这些示例是人为的,但是它们演示了它们打算演示的内容-CustomError实例正以完全完整性来实现。

答案 2 :(得分:0)

只需手动创建Result

let result: Result<Model, CustomError>
if (number < 20) {
    result = .failure(.somethingBadHappened)
} else {
    result = .success(Model(value: number))
}
completion(result)