Swift 如何在结果中返回.map函数中的失败

Swift 如何在结果中返回.map函数中的失败,swift,Swift,我有一个方法execute,它通过一个接收Result的回调调用一个外部API。如何将可选的成功映射到未包装的结果或错误 func execute(then handle: @escaping (Result<Data, Error>) -> Void) { externalAPI.retrieveData { result in let mappedResult = result .map { g

我有一个方法
execute
,它通过一个接收
Result
的回调调用一个外部API。如何将可选的成功映射到未包装的结果或错误

func execute(then handle: @escaping (Result<Data, Error>) -> Void) {
    externalAPI.retrieveData { result in
        let mappedResult = result
            .map {
                guard let data = $0 else {
                    throw NSError(domain: "", code: 0, description: "error")
                }
                return data
            }
        handle(mappedResult)
    }
}
func执行(然后句柄:@escaping(Result)->Void){
externalAPI.retrieveData{结果为
设mappedResult=result
.地图{
guard let数据=$0其他{
抛出N错误(域:“”,代码:0,说明:“错误”)
}
返回数据
}
句柄(mappedResult)
}
}
此代码失败,从类型为“(可选)throws->”的抛出函数到类型为“(数据?)->NewSuccess”的非抛出函数的转换无效。

我可以通过一个简单的开关(如下所示)来实现这一点,但我想知道是否可以在.map中抛出一个失败

func execute(then handle: @escaping (Result<Data, Error>) -> Void) {
    externalAPI.retrieveData { result in
        switch result {
        case .failure(let error):
            handle(.failure(error))
        case .success(let data):
            guard let data = data else {
                handle(.failure(NSError(domain: "", code: 0, description: "error")))
                return
            }
            handle(.success(data))
        }
    }
}
func执行(然后句柄:@escaping(Result)->Void){
externalAPI.retrieveData{结果为
切换结果{
案例。失败(let错误):
句柄(.failure(error))
案例.成功(让数据):
guard let data=其他数据{
句柄(.failure(n错误(域:“”,代码:0,说明:“错误”))
返回
}
句柄(.success(数据))
}
}
}

显然,这可以使用。就我而言:

func execute(then handle: @escaping (Result<Data, Error>) -> Void) {
    externalAPI.retrieveData { result in
        let mappedResult = result
            .flatMap { data in
                Result<Data, Error> {
                    guard let data = data else {
                        throw NSError(domain: "", code: 0, description: "error")
                    }
                    return data
                }
            }
         handle(mappedResult)
    }
}
func执行(然后句柄:@escaping(Result)->Void){
externalAPI.retrieveData{结果为
设mappedResult=result
.flatMap{中的数据
结果{
guard let data=其他数据{
抛出N错误(域:“”,代码:0,说明:“错误”)
}
返回数据
}
}
句柄(mappedResult)
}
}

这有点让人困惑,但对我来说是可行的。

您可以使用
Result(catching:)
get()
抛出的函数和返回
结果的函数之间进行转换

这是您的原始
地图
通话:

.map {
    guard let data = $0 else {
        throw NSError(domain: "", code: 0, description: "error")
    }
    return data
}
Result.map
获取一个结果和一个函数,该函数转换
(Success)->NewSuccess
,并返回一个
结果

您的映射获取
数据
(成功),并返回
结果
(NewSuccess)。因此,插入NewSuccess的最后一种类型是:
Result
。这比你想要的要多。您希望将其展平为
结果
,这就是
flatMap
的作用所在

您的答案表明了这一点,但您也可以将其拉入一个更通用的工具中。它只在
Failure==Error
时起作用,因为
throws
是非类型化的,所以不能将其限制为某些错误子集。但不管怎样,这就是你正在做的。下面是
tryMap

extension Result where Failure == Error {
    func tryMap<NewSuccess>(_ transform: (Success) throws -> NewSuccess) -> Result<NewSuccess, Error> {
        self.flatMap { value in
            Result<NewSuccess, Error> { try transform(value) }
        }
    }
}
扩展结果,其中失败==错误{
func tryMap(uTransform:(Success)抛出->新闻成功)->结果{
self.flatMap{中的值
结果{尝试转换(值)}
}
}
}
这样,您可以将其改写为:

func execute(then handle: @escaping (Result<Data, Error>) -> Void) {
    externalAPI.retrieveData { result in
        handle(result
                .tryMap {
                    guard let data = $0 else {
                        throw NSError(domain: "", code: 0, description: "error")
                    }
                    return data
                })
    }
}
func执行(然后句柄:@escaping(Result)->Void){
externalAPI.retrieveData{结果为
处理(结果)
.tryMap{
guard let数据=$0其他{
抛出N错误(域:“”,代码:0,说明:“错误”)
}
返回数据
})
}
}
也就是说,我可能会这么写:

func execute(then handle: @escaping (Result<Data, Error>) -> Void) {
    externalAPI.retrieveData { result in
        handle(result
                .flatMap { maybeData in
                    maybeData.map(Result.success)
                        ?? .failure(NSError(domain: "", code: 0, description: "error"))
                })
    }
}
func执行(然后句柄:@escaping(Result)->Void){
externalAPI.retrieveData{结果为
处理(结果)
.flatMap{maybeData in
maybeData.map(Result.success)
??失败(N错误(域:“”,代码:0,说明:“错误”))
})
}
}
或者如果我希望有人以后能够真正阅读:

func execute(then handle: @escaping (Result<Data, Error>) -> Void) {
    externalAPI.retrieveData { result in
        handle(result
                .flatMap {
                    switch $0 {
                    case .some(let data): return .success(data)
                    case .none: return .failure(NSError(domain: "", code: 0, description: "error"))
                    }
                }
        )
    }
}
func执行(然后句柄:@escaping(Result)->Void){
externalAPI.retrieveData{结果为
处理(结果)
.平面图{
转换$0{
case.some(让数据):return.success(数据)
case.none:返回.failure(n错误(域:“”,代码:0,说明:“错误”))
}
}
)
}
}

与您的
开关相比,此
开关的优点是您不必打开和重新包装以前的故障。

您必须使用第二种方法,
在异步完成处理程序中抛出
是不可能的。开关中的
数据
真的是可选的吗?顺便说一句,您的自定义错误是毫无意义的。我无法控制外部API结果,所以很遗憾,它是可选的。至于自定义错误,它是一个占位符,而不是真正的实现。谢谢你的帮助!如果API返回带有关联类型的枚举,则这些类型应该是非可选的。如果
数据
nil
则API必须返回
。失败
传递错误。