【问题标题】:Passing concrete instance to closure that expects a protocol parameter将具体实例传递给需要协议参数的闭包
【发布时间】:2020-11-15 18:18:35
【问题描述】:

在我的应用程序中,我有一个函数可以调用我的特定 API 端点,并且该函数接受一个闭包作为完成处理程序。在success 情况下,该闭包接受我的自定义Decodable 类型(Category) 的Result,在failure 情况下接受Error。总而言之,它的方法签名如下所示:

static func getAllRestaurantCategories(completion: @escaping (Result<[Category], Error>) -> Void) -> Void

此函数调用 Alamofire 以确定服务器支持的语言,然后获取所有可能的餐厅类别的列表。它是这样实现的:

static func getAllRestaurantCategories(completion: @escaping (Result<[Category], Error>) -> Void) -> Void{
    API.localizedRequest(API.categories) { (request: DataRequest) in
        request.responseDecodable(of: [Category].self) { (response: DataResponse<[Category], AFError>) in
            completion(response.result)
        }
    }
}

但是,在completion(response.result) 的行中,我收到一个编译器错误,显示为Cannot convert value of type 'Result&lt;[Category], AFError&gt;' to expected argument type 'Result&lt;[Category], Error&gt;'。如果我在failure 情况下更改我的方法接受的闭包以接受AFError,则此错误消失,如下所示:

static func getAllRestaurantCategories(completion: @escaping (Result<[Category], AFError>) -> Void) -> Void{
    API.localizedRequest(API.categories) { (request: DataRequest) in
        request.responseDecodable(of: [Category].self) { (response: DataResponse<[Category], AFError>) in
            completion(response.result)
        }
    }
}

Alamofire 的AFError 符合Error,所以在我看来这应该可以正常工作。我知道我可以自己解析 Alamofire 的 Result 并生成我自己的以传递给我的完成处理程序,但如果我不需要,我宁愿不编写所有额外的自定义代码。我怎样才能让类型系统理解这应该没问题?

【问题讨论】:

  • 我假设编译器抱怨的是 swift Result 类型 vs Alamofire Result 类型而不是错误类型
  • 一开始我也是这么认为的,但是当我深入挖掘时,我发现Alamofire的Result其实是Swift.Result。如果我更改我的方法接受的闭包以接受AFError 作为Result 给出的failure 情况,编译器会非常高兴。这让我觉得它不知道 AFError 不符合 Error,出于某种原因

标签: ios swift macos types alamofire


【解决方案1】:

简单地说,在(至少是当前版本的)Swift 中,如果SubBase 的子类型,那并不意味着Container&lt;Sub&gt;Container&lt;Base&gt; 的子类型。

其实Container&lt;Sub&gt;Container&lt;Base&gt;是不相关的类型。

所以,虽然我们可以做到以下几点:

protocol Car {}
struct Toyota: Car {}

let a: Car = Toyota()

但我们通常不能(Swift 的标准库集合类型除外)这样做:

struct Container<T> {}

let c: Container<Car> = Container<Toyota>() // ERROR

据说Container&lt;Car&gt;Container&lt;Toyota&gt;不是协变的


Result 有一个 mapError 函数,应该让它相当轻松:

completion(response.result.mapError { $0 as Error } )

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2015-07-25
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-07-22
    • 2014-07-05
    相关资源
    最近更新 更多