我已经构建了一个后端并且正在使用前端客户端,目前,我的客户端的登录响应返回:
{
"user": {
"email": "[email protected]",
"token": "eyJhbGciOiJIUzI"
}
}
这给我一个问题,我不能简单地解码到用户对象,我必须为所有内容做以下2层:
struct User: Codable {
let email: String
let token: String
}
struct UserResponseData: Codable {
let user: User
}
有没有更有效的方法来直接访问值并限制对象?也许我将user
json的父母编辑为像data
这样更通用的东西,然后将用户放在那里?我不确定......
我的客户端如下所示,如果这有助于通知改进的架构:
class APIClient {
static func signup(request: SignupRequestData, completion: @escaping (Result<UserResponseData>) -> Void) {
performRequest(route: APIRouter.signup(request), completion: completion)
}
@discardableResult
private static func performRequest<T:Decodable>(route: APIRouter,
decoder: JSONDecoder = JSONDecoder(),
completion:@escaping (Result<T>)->Void) -> DataRequest {
return AF.request(route).responseDecodable (decoder: decoder){ (response: DataResponse<T>) in
completion(response.result)
}
}
}
在一个更适合的结构中获得一些帮助,因此我不必继续展开父母以获得客户所需的价值
一种相当简单的方法是将其包装成通用的Response
类型,假设第一个键始终是正确的。
struct AnyStringKey: CodingKey {
var stringValue: String
init?(stringValue: String) { self.stringValue = stringValue }
var intValue: Int?
init?(intValue: Int) { return nil }
}
struct Response<Payload: Decodable>: Decodable {
let payload: Payload
init(from decoder: Decoder) throws {
let container = try decoder.container(keyedBy: AnyStringKey.self)
guard let key = container.allKeys.first else {
throw DecodingError.dataCorrupted(.init(codingPath: decoder.codingPath,
debugDescription: "Missing payload key"))
}
self.payload = try container.decode(Payload.self, forKey: key)
}
}
let user = try JSONDecoder().decode(Response<User>.self, from: json).payload
可以使这更高级,并检查密钥是否符合您的期望,但这可能足以满足您的需求。
这种方法将一些工作转移到调用者(调用.payload
)。您可以通过使用处理提取子密钥的协议将一些工作转移到解码类型中来消除这种情况。
protocol LayerDecodable: Decodable {
associatedtype CodingKeys: CodingKey
init(from container: KeyedDecodingContainer<CodingKeys>) throws
}
extension LayerDecodable {
init(from decoder: Decoder) throws {
let container = try decoder.container(keyedBy: AnyStringKey.self)
guard let key = container.allKeys.first else {
throw DecodingError.dataCorrupted(.init(codingPath: decoder.codingPath,
debugDescription: "Missing payload key"))
}
try self.init(from: container.nestedContainer(keyedBy: CodingKeys.self, forKey: key))
}
}
但有了这个,你需要手动实现解码。
struct User: LayerDecodable {
let email: String
let token: String
enum CodingKeys: CodingKey {
case email, token
}
init(from container: KeyedDecodingContainer<CodingKeys>) throws {
self.email = try container.decode(String.self, forKey: .email)
self.token = try container.decode(String.self, forKey: .token)
}
}
好处是调用者现在是干净的:
let user = try JSONDecoder().decode(User.self, from: json)