Swift - 将通用代码重构为协议

Swift - refactoring common code to a protocol

我有多个 class 具有调用公共网络 class 进行 GET api 调用的代码。下面是一个例子:

public typealias Api1Result = (Result<Api1Model>) -> Void

private var path = "the/path/api1"

public enum Api1ServiceError: String, Error {
    case error = "Sorry, the api1 service returned something different than expected"
}

extension Api1Model {

    public static func getApi1(networkClient: NetworkClient = networkClient, completion: @escaping Api1Result) {
        networkClient.getPath(path) { result in
            switch result {
            case .success(let data):
                do {
                    let api1Model = try JSONDecoder().decode(Api1Model.self, from: data)
                    completion(.success(api1Model))
                } catch {
                    completion(.failure(Api1ServiceError.error))
                }
            case .failure(let error):
                completion(.failure(error))
            }
        }
    }    
}

如果感兴趣,这里是结果枚举:

public enum Result<Value> {
    case success(Value)
    case failure(Error)
}

还有其他几个模型classes,唯一的区别是被解码的实际模型class(在本例中为Api1Model),以及完成类型别名( Api1Result)。它在其他几个方法中做完全相同的事情,只是调用 networkClient.getPath() 方法,检查 success/failure,然后调用完成闭包。

想知道是否有任何协议专家可以协助简化和重构,这样我就不会在多个 classes 中使用相同的样板代码?

使用协议扩展(未测试)

protocol ApiModel {
    associatedtype ApiType : Decodable = Self

    static var path : String { get }
    static func getApi1(networkClient: NetworkClient, completion: @escaping (Result<ApiType>) -> Void)

}

extension ApiModel where Self : Decodable {   
    static func getApi1(networkClient: NetworkClient, completion: @escaping (Result<ApiType>) -> Void) {
        networkClient.getPath(path) { result in
            switch result {
            case .success(let data):
                do {
                    let api1Model = try JSONDecoder().decode(ApiType.self, from: data)
                    completion(.success(api1Model))
                } catch {
                    completion(.failure(Api1ServiceError.error))
                }
            case .failure(let error):
                completion(.failure(error))
            }
        }
    }
}

让你的所有 类 符合 ApiModel 并添加静态 path 属性。将推断类型别名。