Swift 协议类型的值和类型符合协议的对象有什么区别?

时间:2021-03-26 11:41:39

标签: ios swift api-design

首先,我对 Swift 和 iOS 开发完全陌生,但决定尝试只是为了一些乐趣。我目前也在从优达学城获得纳米学位。我设计了基本屏幕,现在正在尝试设计一个易于使用的 API 来定义后端调用(在课堂上不需要,但我出于好奇而选择了它)。

struct ApiDefinition<RequestType: Codable, ResponseType: Codable> {
    let url: String
    let method: HttpMethod
    let headers: [String : String]?
    
    func call(withPayload payload: RequestType, completion: @escaping (ResponseType?, Error?) -> Void) throws {
        let url = URL(string: self.url)!
        var request = URLRequest(url: url, cachePolicy: .reloadIgnoringLocalAndRemoteCacheData, timeoutInterval: 500)
        
        request.httpMethod = method.rawValue
        request.httpBody = try JSONEncoder().encode(payload)
        
        let task = URLSession.shared.dataTask(with: request) { data, response, error in
            guard let data = data else {
                completion(nil, error)
                return
            }
            
            let decoder = JSONDecoder()
            let value = try? decoder.decode(ResponseType.self, from: data)
            completion(value, nil)
        }
        
        task.resume()
    }
}

enum HttpMethod: String {
    case get = "GET"
    case post = "POST"
}

这按预期工作,我按如下方式定义端点。

let udacitySessionApi = ApiDefinition<SessionRequest, SessionResponse>(
    url: baseUrl + "/v1/session",
    method: .post,
    headers: [
        "Content-Type": "application/json",
        "Accept": "application/json"
    ]
)

这反过来又让我可以随时使用以下代码调用 API。

do {
    try udacitySessionApi.call(withPayload: request) { response, error in
        guard let response = response else {
            print(error!.localizedDescription)
            return
        }
        
        print(response)
    }
} catch {
    print(error.localizedDescription)
}

到目前为止,一切都很好。现在我想做的是允许 Codable 将自身包装起来以保持简洁。但是,现在的问题是一些请求类型经常需要嵌套在编码输出中,我不想写额外的结构体。

所以我想如果 RequestType 是 Codable 并且也符合 Wrappable 协议(我写的),我可以自动化。

所以我写了协议 Wrappable 如下:

protocol Wrappable: Codable {
    func wrap() -> Codable
}

然后在我的请求类型中,我使它符合该协议。

struct SessionRequest: Codable, Wrappable {
    // ...

    func wrap() -> Codable {
        return ["wrapped-key": self]
    }
}

现在为了访问这个东西,我写了一个函数

private func getEncodable<T: Codable>(_ payload: T) -> Codable {
    if let payload = payload as? Wrappable {
        return payload.wrap()
    }

    return payload
}

但是现在JSONEncoder的问题是encode的签名是

open func encode<T>(_ value: T) throws -> Data where T : Encodable

我收到的错误信息是这样的:

<块引用>

协议类型'Codable'(又名'Decodable & Encodable')的值不能符合'Encodable';只有 struct/enum/class 类型才能符合协议

线上发生

   request.httpBody = try JSONEncoder().encode(getEncodable(payload))
//                                             ^^

据我所知,如果一个结构体确认到 Codable 协议,那么它也会自动确认到 Encodable 协议,不是吗?

因此,我想将我的包装函数、Wrappable 协议和 getEncodable 函数中的 Codable 返回类型显式更改为 Encodable,并且我的错误消息更改为:

<块引用>

协议类型'Encodable'的值不能符合'Encodable';只有 struct/enum/class 类型才能符合协议

太好了,现在我更困惑了。协议类型的值是什么意思?

0 个答案:

没有答案