我有一个可解码的课程:
struct AuthenticationResponse : Decodable {
var status: String
var error: Error
var access_token: String? = ""
var expires_in: Double? = 0
var token_type: String? = ""
var scope: String? = ""
var refresh_token: String? = "
}
struct Error : Decodable {
var desc: String
var code: String
}
在Error类中我有:
要解码到这个课程,我有:
URLSession.shared.dataTask(with: request) { (data:Data?, response:URLResponse?, error:Error?) in
if let jsonData = data{
let decoder = JSONDecoder()
print("hey")
print("response: \(String(data:jsonData, encoding:.utf8))")
completion(try! decoder.decode(AuthenticationResponse.self, from: jsonData))
}
}.resume()
我收到的一些回复是(成功回复):
{
“status”: “SUCCESS” “error”: null, "access_token":
"MWVmOWQxMDYwMjQyNDQ4NzQyNTdkZjQ3NmI4YmVjMGZjZGM5N2IyZmNkOTA1 N2M0NDUzODEwYjM5ZWQyNGNkZg",
"expires_in": 3600, "token_type": "bearer", "scope": null,
"refresh_token":
"ZGEwOGZiOWZhMzhhYjBmMzAyOGRmZTA5NjJhMjY2MTk3YzMyMmE1ZDlkNWI2N mJjYmIxMjNkMjE1NWFhNWY0Mg"
}
然后失败的响应只包含一个带有desc和代码的错误对象。
我想要实现的是适合两种情况的可解码类(当响应成功并失败时)但是我不知道如何实现这一点。我知道我可以制作2个单独的可解码类但这会使事情变得更加混乱,因为我必须确定响应是否是错误并填充以返回不同的类。
有谁知道我应该如何实现这一点>
答案 0 :(得分:1)
我会给它一个try
,但首先我们要弄清楚我认为是一个有点粗暴的问题。由于Error
是(着名且广泛使用)protocol
的名称,因此应重命名,因为您希望能够将AuthenticationResponse
留空,所以它显然必须是可选的那里(提出为什么它根本在Response
,但我会把它放在一边)。这给我们留下了以下内容:
struct AuthError : Decodable {
var desc: String
var code: String
}
struct AuthenticationResponse : Decodable {
var status: String
var error: AuthError?
var access_token: String? = ""
var expires_in: Double? = 0
var token_type: String? = ""
var scope: String? = ""
var refresh_token: String? = ""
}
然后我们需要一些相关案例的示例数据,我用过:
let okData = """
{
"status": "SUCCESS",
"error": null,
"access_token":
"MWVmOWQxMDYwMjQyNDQ4NzQyNTdkZjQ3NmI4YmVjMGZjZGM5N2IyZmNkOTA1N2M0NDUzODEwYjM5ZWQyNGNkZg",
"expires_in": 3600,
"token_type": "bearer",
"scope": null,
"refresh_token":
"ZGEwOGZiOWZhMzhhYjBmMzAyOGRmZTA5NjJhMjY2MTk3YzMyMmE1ZDlkNWI2NmJjYmIxMjNkMjE1NWFhNWY0Mg"
}
""".data(using: .utf8)!
let errData = """
{
"desc": "username or password incorrect",
"code": "404"
}
""".data(using: .utf8)!
现在我们可以定义一个enum
返回类型,它允许我们所有的情况:
enum AuthResult {
case ok(response: AuthenticationResponse)
case authError(error: AuthError)
case parseError(description: String)
case fatal
}
最终允许我们为收到的身份验证数据编写parse
函数:
func parse(_ jsonData:Data) -> AuthResult {
let decoder = JSONDecoder()
do {
let authRes = try decoder.decode(AuthenticationResponse.self, from: jsonData)
return .ok(response: authRes)
} catch {
do {
let errRes = try decoder.decode(AuthError.self, from: jsonData)
return .authError(error: errRes)
} catch let errDecode {
return .parseError(description: errDecode.localizedDescription)
}
}
}
Playground中的所有内容都允许使用
switch parse(okData) {
case let .ok(response):
print(response)
case let .authError(error):
print(error)
case let .parseError(description):
print("You threw some garbage at me and I was only able to \(description)")
default:
print("don't know what to do here")
}
与大多数其他语言中的混乱相比,这仍然是优雅的,但是调用仍然存在,仅仅将AuthenticationResponse
定义为(常规)返回类型是没有意义的。 parse
功能,并通过throw
enum
(符合Error
)和一些合适的有效负载提供其余功能。
从(主要)来自Java我仍然避免使用异常作为“有点”的常规控制流(如“常规”登录失败),但鉴于Swifts采用更合理的异常方法,可能需要重新考虑。 / p>
无论如何,这将为您提供一个功能来解析服务回复的任何一种情况,以及以“统一”方式处理它们的体面方式。由于您可能无法修改处理request
的服务的行为,因此这可能是唯一可行的选择。但是,如果您能够修改服务,则应该争取通过单次调用JSONDecoder.decode
来解析的“统一”回复。您仍然需要解释选项(正如您在上面的示例中所应该的那样,因为它们仍然很难处理,即使Swifts精彩的编译器支持强迫您“做正确的事情”),但它会使您的解析不容易出错。