Struct Codable Alamofire Generics

我半年前写了协议和枚举中的 Swift 泛型,以解决网络响应问题数据转换为泛型类型。

在 Swift 4 之后,JSONSerialization方法有点过时了

在更新进度时,我找到了一种更好的方法

Tools

Assume we have a Json structure:

1
2
3
4
5
6
7
{
"id": 0,
"List": [0, 1, 2],
"C": {
"id": 0
}
}

After you paste it to Quicktype with select Alamofire extensions, you can get:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
import Foundation
import Alamofire

struct A: Codable {
let id: Int
let list: [Int]? // optional, in case some date has no list
let c: C

enum CodingKeys: String, CodingKey {
case id
case list = "List"
case c = "C"
}
}

struct C: Codable {
let id: Int
}

// MARK: - Alamofire response handlers

extension DataRequest {
fileprivate func decodableResponseSerializer<T: Decodable>() -> DataResponseSerializer<T> {
return DataResponseSerializer { _, response, data, error in
guard error == nil else { return .failure(error!) }

guard let data = data else {
return .failure(AFError.responseSerializationFailed(reason: .inputDataNil))
}

return Result { try JSONDecoder().decode(T.self, from: data) }
}
}

@discardableResult
fileprivate func responseDecodable<T: Decodable>(queue: DispatchQueue? = nil, completionHandler: @escaping (DataResponse<T>) -> Void) -> Self {
return response(queue: queue, responseSerializer: decodableResponseSerializer(), completionHandler: completionHandler)
}

@discardableResult
func responseA(queue: DispatchQueue? = nil, completionHandler: @escaping (DataResponse<A>) -> Void) -> Self {
return responseDecodable(queue: queue, completionHandler: completionHandler)
}
}

请注意,func responseA是静态类型,仅适用于响应您粘贴的 JSON 的空间 API。因此,我们需要所有 API 的泛型类型。

因此我们可以删除func responseA并定义一个协议MYRequest,如下所示:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
protocol MYRequest {
var router: String { get }
var api: String { get }

associatedtype T: Decodable // generics type
}

extension MYRequest {
var api: String { return "v1" }

var domain: String {
return "https://domain.com"
}

// T is used in DataResponse<T>
func request(completionHandler: @escaping (DataResponse<T>) -> Void) {
let url = "\(domain)/\(api)/\(router)"
Alamofire.request(url).responseDecodable(completionHandler: completionHandler)
}
}

现在,我们可以根据需要实现不同的 Requset:

1
2
3
4
5
6
7
8
9
struct ARequset: MYRequest {
let router = "a"
typealias T = A
}

struct BRequset: MYRequest {
let router = "b"
typealias T = B
}

另外,我们可以定义另一个具有相同协议的协议 var api: String { get }MYRequest 如果我们有一个新的 CRequset 和 API 升级到 v2

1
2
3
4
5
6
7
8
9
10
11
protocol V2 {
var api: String { get }
}
extension V2 {
var api: String { return "v2" }
}

struct C: Request, V2 {
let router = "c"
typealias T = C
}

最后,ViewController 中的请求将如下所示:

1
2
3
4
5
6
7
8
9
10
ARequset().request { [weak self] response in
if let a = response.result.value { // a is A, not Any

}
}
BRequset().request { [weak self] response in
if let b = response.result.value { // b is B, not Any

}
}