首先,我对 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 类型才能符合协议
太好了,现在我更困惑了。协议类型的值是什么意思?