如何解决如何使用泛型在 Swift 中实现 REST APIManager
APIManager 应该能够接受不同的 HTTP 主体作为基于相关 API 的输入,并将响应映射到所需的模型结构以在 ui 中使用
这是我想在我的代码中使用的示例 JSON 响应:
{
"response_code": 0,"data": {
"app_version_update": "","offers": [
{
"title": "Special Scheme1","image": "http://59.145.109.138:11101/Offers/BGL_banner_1080_x_540_1.jpg","r": 1.0,"result_count": 5.0
},{
"title": "test 1","image": "http://59.145.109.138:11101/Offers/Yoho-National-Park2018-10-27_10-10-52-11.jpg","r": 2.0,{
"title": "Offer Test 1234444","image": "http://59.145.109.138:11101/Offers/Stanley-Park2018-10-27_10-11-27-44.jpg","r": 3.0,"result_count": 5.0
}
],"rate": 2000
},"meta": {
"api_version": 2.0
}
}
下面是一个执行 HTTP Post 调用的非通用函数 load():
func load(urlRequest: URLRequest,withCompletion completion: @escaping (_ response: APIResponse) -> Void) {
let task = URLSession.shared.dataTask(with: urlRequest) { (data,response,error) in
guard error == nil else {
print("Error fetching data from server\nERROR: \(String(describing: error))")
return
}
guard let jsonData = data else {
print("Response Data is empty")
return
}
printResponseBody(response: data)
let decoder = JSONDecoder()
let response = try? decoder.decode(APIResponse.self,from: jsonData)
guard let decodedResponse = response else {
print("Unable to parse data from response")
return
}
print("Decoded Response: ",decodedResponse)
DispatchQueue.main.async { completion(decodedResponse) }
}
task.resume()
}
以下是上述示例 API 的非通用 APIResponse 结构:
struct APIResponse: Codable {
let responseCode: Int
let data: ResultSet
let meta: Meta
enum CodingKeys: String,CodingKey {
case responseCode = "response_code"
case data,meta
}
}
// MARK: - DataClass
struct ResultSet: Codable {
let appVersionUpdate: String
let offers: [Offer]
let rate: Int
enum CodingKeys: String,CodingKey {
case appVersionUpdate = "app_version_update"
case offers,rate
}
}
// MARK: - Offer
struct Offer: Codable,Identifiable {
let id: Int
let title: String
let image: String?
let r,resultCount: Int
enum CodingKeys: CodingKey {
case id,r,title,image,resultCount
var stringValue: String {
switch self {
case .id,.r: return "r"
case .title: return "title"
case .image: return "image"
case .resultCount: return "result_count"
}
}
}
}
// MARK: - Meta
struct Meta: Codable {
let apiVersion: Int
enum CodingKeys: String,CodingKey {
case apiVersion = "api_version"
}
注意:
所有 api 共享相同的响应结构,但仅数据部分基于 api 发生变化。
谁能帮我实现一个通用的 APIManager 文件,它可以接受不同的 UrlRequest 输入,启动一个 URLSession 并将获得的 JSON 响应数据对象解码为不同的模型结构以用于 ui 代码
解决方法
首先使结构通用 - 顺便说一下,如果您指定 CodingKeys
密钥解码策略,您可以省略 convertFromSnakeCase
- 如果您不打算将数据一致性编码为 {{ 1}} 就足够了
Decodable
然后使函数也通用并返回 struct APIResponse<D : Decodable>: Decodable {
let responseCode: Int
let data: D
let meta: Meta
enum CodingKeys: String,CodingKey {
case responseCode = "response_code",data,meta
}
}
类型的所有错误
在解码环境中永远不要忽略 Result
的错误
try?
关于感叹号:如果 func load<T : Decodable>(urlRequest: URLRequest,type: T.Type,withCompletion completion: @escaping (Result<APIResponse<T>,Error>) -> Void) {
let task = URLSession.shared.dataTask(with: urlRequest) { data,_,error in
if let error = error { completion(.failure(error)); return }
do {
let response = try JSONDecoder().decode(APIResponse<T>.self,from: data!)
DispatchQueue.main.async { completion(.success(response)) }
} catch {
DispatchQueue.main.async { completion(.failure(error)) }
}
}
task.resume()
}
为 data
,则保证 error
具有值。
如果将 nil
闭包放入调用方法中,您甚至可以编写
DispatchQueue
如果你在调用方法时注释了闭包类型,你甚至可以省略 func load<T : Decodable>(urlRequest: URLRequest,error in
if let error = error { completion(.failure(error)); return }
completion( Result{ try JSONDecoder().decode(APIResponse<T>.self,from: data!)})
}
task.resume()
}
参数
type
func load<T : Decodable>(urlRequest: URLRequest,completion: @escaping (Result<APIResponse<T>,Error>) -> Void) { ...
版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。