使用通用协议作为变量类型

时间:2019-04-19 20:20:03

标签: swift

我想为数据获取服务创建通用协议,如下所示:

protocol FetchDataDelegate: AnyObject {

    associatedtype ResultData

    func didStartFetchingData()
    func didFinishFetchingData(with data: ResultData)
    func didFinishFetchingData(with error: Error)
}

class Controller: UIViewController, FetchDataDelegate {

    func didStartFetchingData() {
        //...
    }

    func didFinishFetchingData(with data: ResultData) {
        //...
    }

    func didFinishFetchingData(with error: Error) {
        //...
    }
}

class NetworkManager {

    weak var delegate: FetchDataDelegate?
    //...
}

Controller类将具有对NetworkManager实例的引用,通过这个,我想开始网络操作。操作结束后,我想调用适当的委托函数以将结果传递回控制器。但是使用此设置,我得到了以下错误: 协议“ FetchDataDelegate”只能用作通用约束,因为它具有“自我”或相关的类型要求 问题是我应该怎么做才能将此通用协议用作变量类型?或者,如果不可能的话,正确的方法是什么?谢谢!

2 个答案:

答案 0 :(得分:2)

虽然这与question David Smith linked密切相关(并且您也应该阅读),但是值得单独回答,因为这是一个不同的具体用例,因此我们可以讨论它。

首先,假设您可以存储此变量。你会怎么做? NetworkManager中的哪个函数可以调用delegate.didFinishFetchingData?当您不知道ResultData是什么时,您将如何生成它?

重点在于,这不是PAT(具有关联类型的协议)的目的。这不是他们的目标。他们的目标是帮助您向其他类型添加扩展,或限制可以将哪些类型的类型传递给通用算法。出于这些目的,它们非常强大。但是您想要的是泛型,而不是协议。

您应该使用泛型函数来处理特定调用的结果,而不是创建委托,而不是尝试将每个视图控制器固定为特定的结果类型(无论如何都不是很灵活)。例如,以最简单,最不灵活的方式仍然可以为您提供进度报告:

struct APIClient {
    func fetch<Model: Decodable>(_: Model.Type,
                                 with urlRequest: URLRequest,
                                 completion: @escaping (Result<Model, Error>) -> Void)
    -> Progress {

        let session = URLSession.shared

        let task = session.dataTask(with: urlRequest) { (data, _, error) in
            if let error = error {
                completion(.failure(error))
            }
            else if let data = data {
                let decoder = JSONDecoder()
                completion(Result {
                    try decoder.decode(Model.self, from: data)
                })
            }
        }
        task.resume()
        return task.progress
    }
}

let progress = APIClient().fetch(User.self, with: urlRequest) { user in ... }

此结构是解决整个问题类别的基本方法。 It can be made much, much more flexible depending on your specific needs.

(如果您的didStartFetchingData方法非常重要,以Progress无法解决的方式,请留下评论,我将向您展示如何实现这种方法。这并不困难,但这个答案已经很长了。)

答案 1 :(得分:0)

可能的this副本

总而言之,您不能将通用协议用作变量类型,因为您在编译时不知道<span style="display:none" id='ProjectTrackingCookie"> ProjectTrancingGuid </span> 的类型,因此必须将其用作通用约束