Skip to content

Commit 564bfd0

Browse files
committed
Implement Codable headers
Add more encodable types Exclude nil values from encoding
1 parent 7182ad3 commit 564bfd0

8 files changed

+248
-56
lines changed

CodyFire.podspec

+1-1
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88

99
Pod::Spec.new do |s|
1010
s.name = 'CodyFire'
11-
s.version = '1.2.3'
11+
s.version = '1.3'
1212
s.summary = '❤️ Powerful codable API requests builder and manager for iOS based on Alamofire'
1313

1414
# This description is used to generate tags and improve search results.

CodyFire/Classes/APIRequest+Build.swift

+1-1
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ import Alamofire
1111
extension APIRequest {
1212
var url: String {
1313
var url = CodyFire.shared.apiURL + "/" + endpoint
14-
if let query = query {
14+
if let query = query, query.count > 0 {
1515
if url.contains("?") {
1616
url.append("&")
1717
} else {

CodyFire/Classes/APIRequest+BuildQuery.swift

+56-16
Original file line numberDiff line numberDiff line change
@@ -13,17 +13,45 @@ extension APIRequest {
1313
func buildQuery(_ params: Codable) -> String? {
1414
let dateEncodingStrategy = self.dateEncodingStrategy(for: params)
1515
let mirror = Mirror(reflecting: params)
16-
guard mirror.children.count > 0 else { return nil }
1716
var params: [Param] = []
18-
mirror.children.forEach { children in
19-
if let key = children.label {
20-
switch children.value {
21-
case let v as [Date]: self.parse(v, as: key + "[]", dateCodingStrategy: dateEncodingStrategy).forEach { params.append($0) }
22-
case let v as Date: self.parse([v], as: key, dateCodingStrategy: dateEncodingStrategy).forEach { params.append($0) }
23-
case let v as [String]: self.parse(v, as: key + "[]").forEach { params.append($0) }
24-
case let v as String: self.parse([v], as: key).forEach { params.append($0) }
25-
default: self.parse(any: children.value, as: key).forEach { params.append($0) }
26-
}
17+
for children in mirror.children {
18+
guard let key = children.label else { continue }
19+
switch children.value {
20+
case let v as [Date]: self.parse(v, as: key + "[]", dateCodingStrategy: dateEncodingStrategy).forEach { params.append($0) }
21+
case let v as Date: self.parse([v], as: key, dateCodingStrategy: dateEncodingStrategy).forEach { params.append($0) }
22+
case let v as [String]: self.parse(v, as: key + "[]").forEach { params.append($0) }
23+
case let v as String: self.parse([v], as: key).forEach { params.append($0) }
24+
case let v as [UUID]: self.parse(v, as: key + "[]").forEach { params.append($0) }
25+
case let v as UUID: self.parse([v], as: key).forEach { params.append($0) }
26+
case let v as [UInt]: self.parse(v.map { Int64($0) }, as: key + "[]").forEach { params.append($0) }
27+
case let v as UInt: self.parse([v].map { Int64($0) }, as: key).forEach { params.append($0) }
28+
case let v as [UInt8]: self.parse(v.map { Int64($0) }, as: key + "[]").forEach { params.append($0) }
29+
case let v as UInt8: self.parse([v].map { Int64($0) }, as: key).forEach { params.append($0) }
30+
case let v as [UInt16]: self.parse(v.map { Int64($0) }, as: key + "[]").forEach { params.append($0) }
31+
case let v as UInt16: self.parse([v].map { Int64($0) }, as: key).forEach { params.append($0) }
32+
case let v as [UInt32]: self.parse(v.map { Int64($0) }, as: key + "[]").forEach { params.append($0) }
33+
case let v as UInt32: self.parse([v].map { Int64($0) }, as: key).forEach { params.append($0) }
34+
case let v as [UInt64]: self.parse(v.map { Int64($0) }, as: key + "[]").forEach { params.append($0) }
35+
case let v as UInt64: self.parse([v].map { Int64($0) }, as: key).forEach { params.append($0) }
36+
case let v as [Int]: self.parse(v.map { Int64($0) }, as: key + "[]").forEach { params.append($0) }
37+
case let v as Int: self.parse([v].map { Int64($0) }, as: key).forEach { params.append($0) }
38+
case let v as [Int8]: self.parse(v.map { Int64($0) }, as: key + "[]").forEach { params.append($0) }
39+
case let v as Int8: self.parse([v].map { Int64($0) }, as: key).forEach { params.append($0) }
40+
case let v as [Int16]: self.parse(v.map { Int64($0) }, as: key + "[]").forEach { params.append($0) }
41+
case let v as Int16: self.parse([v].map { Int64($0) }, as: key).forEach { params.append($0) }
42+
case let v as [Int32]: self.parse(v.map { Int64($0) }, as: key + "[]").forEach { params.append($0) }
43+
case let v as Int32: self.parse([v].map { Int64($0) }, as: key).forEach { params.append($0) }
44+
case let v as [Int64]: self.parse(v, as: key + "[]").forEach { params.append($0) }
45+
case let v as Int64: self.parse([v], as: key).forEach { params.append($0) }
46+
case let v as [Float]: self.parse(v, as: key + "[]").forEach { params.append($0) }
47+
case let v as Float: self.parse([v], as: key).forEach { params.append($0) }
48+
case let v as [Double]: self.parse(v, as: key + "[]").forEach { params.append($0) }
49+
case let v as Double: self.parse([v], as: key).forEach { params.append($0) }
50+
case let v as [Decimal]: self.parse(v, as: key + "[]").forEach { params.append($0) }
51+
case let v as Decimal: self.parse([v], as: key).forEach { params.append($0) }
52+
default:
53+
guard !String(describing: type(of: children.value)).contains("Optional") else { continue }
54+
print("⚠️ query key `\(key)` with \(type(of: children.value)) type is not supported")
2755
}
2856
}
2957
return params.map { $0.key + "=" + $0.value }.joined(separator: "&")
@@ -39,11 +67,23 @@ extension APIRequest {
3967
return v.compactMap { $0.addingPercentEncoding(withAllowedCharacters: .urlPathAllowed) }.map { Param(key: key, value: $0) }
4068
}
4169

42-
fileprivate func parse(any: Any, as key: String) -> [Param] {
43-
if let any = any as? [Any] {
44-
return parse(any.map { String(describing: $0) }, as: key)
45-
} else {
46-
return parse([String(describing: any)], as: key)
47-
}
70+
fileprivate func parse(_ v: [UUID], as key: String) -> [Param] {
71+
return parse(v.map { $0.uuidString }, as: key)
72+
}
73+
74+
fileprivate func parse(_ v: [Int64], as key: String) -> [Param] {
75+
return parse(v.map { String(describing: $0) }, as: key)
76+
}
77+
78+
fileprivate func parse(_ v: [Float], as key: String) -> [Param] {
79+
return parse(v.map { String(describing: $0) }, as: key)
80+
}
81+
82+
fileprivate func parse(_ v: [Double], as key: String) -> [Param] {
83+
return parse(v.map { String(describing: $0) }, as: key)
84+
}
85+
86+
fileprivate func parse(_ v: [Decimal], as key: String) -> [Param] {
87+
return parse(v.map { String(describing: $0) }, as: key)
4888
}
4989
}

CodyFire/Classes/APIRequest+SendMultipart.swift

+60-19
Original file line numberDiff line numberDiff line change
@@ -14,19 +14,48 @@ extension APIRequest {
1414
let dateEncodingStrategy = self.dateEncodingStrategy(for: payload)
1515
SessionManager.default.upload(multipartFormData: { multipart in
1616
let mirror = Mirror(reflecting: payload)
17-
mirror.children.forEach { children in
18-
if let key = children.label {
19-
switch children.value {
20-
case let v as [Attachment]: self.add(v, as: key + "[]", into: multipart)
21-
case let v as Attachment: self.add([v], as: key, into: multipart)
22-
case let v as [Data]: self.add(v, as: key + "[]", into: multipart)
23-
case let v as Data: self.add([v], as: key, into: multipart)
24-
case let v as [Date]: self.add(v, as: key + "[]", into: multipart, dateCodingStrategy: dateEncodingStrategy)
25-
case let v as Date: self.add([v], as: key, into: multipart, dateCodingStrategy: dateEncodingStrategy)
26-
case let v as [String]: self.add(v, as: key + "[]", into: multipart)
27-
case let v as String: self.add([v], as: key, into: multipart)
28-
default: self.add(any: children.value, as: key, into: multipart)
29-
}
17+
for children in mirror.children {
18+
guard let key = children.label else { continue }
19+
switch children.value {
20+
case let v as [Attachment]: self.add(v, as: key + "[]", into: multipart)
21+
case let v as Attachment: self.add([v], as: key, into: multipart)
22+
case let v as [Data]: self.add(v, as: key + "[]", into: multipart)
23+
case let v as Data: self.add([v], as: key, into: multipart)
24+
case let v as [Date]: self.add(v, as: key + "[]", into: multipart, dateCodingStrategy: dateEncodingStrategy)
25+
case let v as Date: self.add([v], as: key, into: multipart, dateCodingStrategy: dateEncodingStrategy)
26+
case let v as [String]: self.add(v, as: key + "[]", into: multipart)
27+
case let v as String: self.add([v], as: key, into: multipart)
28+
case let v as [UUID]: self.add(v, as: key + "[]", into: multipart)
29+
case let v as UUID: self.add([v], as: key, into: multipart)
30+
case let v as [UInt]: self.add(v.map { Int64($0) }, as: key + "[]", into: multipart)
31+
case let v as UInt: self.add([v].map { Int64($0) }, as: key, into: multipart)
32+
case let v as [UInt8]: self.add(v.map { Int64($0) }, as: key + "[]", into: multipart)
33+
case let v as UInt8: self.add([v].map { Int64($0) }, as: key, into: multipart)
34+
case let v as [UInt16]: self.add(v.map { Int64($0) }, as: key + "[]", into: multipart)
35+
case let v as UInt16: self.add([v].map { Int64($0) }, as: key, into: multipart)
36+
case let v as [UInt32]: self.add(v.map { Int64($0) }, as: key + "[]", into: multipart)
37+
case let v as UInt32: self.add([v].map { Int64($0) }, as: key, into: multipart)
38+
case let v as [UInt64]: self.add(v.map { Int64($0) }, as: key + "[]", into: multipart)
39+
case let v as UInt64: self.add([v].map { Int64($0) }, as: key, into: multipart)
40+
case let v as [Int]: self.add(v.map { Int64($0) }, as: key + "[]", into: multipart)
41+
case let v as Int: self.add([v].map { Int64($0) }, as: key, into: multipart)
42+
case let v as [Int8]: self.add(v.map { Int64($0) }, as: key + "[]", into: multipart)
43+
case let v as Int8: self.add([v].map { Int64($0) }, as: key, into: multipart)
44+
case let v as [Int16]: self.add(v.map { Int64($0) }, as: key + "[]", into: multipart)
45+
case let v as Int16: self.add([v].map { Int64($0) }, as: key, into: multipart)
46+
case let v as [Int32]: self.add(v.map { Int64($0) }, as: key + "[]", into: multipart)
47+
case let v as Int32: self.add([v].map { Int64($0) }, as: key, into: multipart)
48+
case let v as [Int64]: self.add(v, as: key + "[]", into: multipart)
49+
case let v as Int64: self.add([v], as: key, into: multipart)
50+
case let v as [Float]: self.add(v, as: key + "[]", into: multipart)
51+
case let v as Float: self.add([v], as: key, into: multipart)
52+
case let v as [Double]: self.add(v, as: key + "[]", into: multipart)
53+
case let v as Double: self.add([v], as: key, into: multipart)
54+
case let v as [Decimal]: self.add(v, as: key + "[]", into: multipart)
55+
case let v as Decimal: self.add([v], as: key, into: multipart)
56+
default:
57+
guard !String(describing: type(of: children.value)).contains("Optional") else { continue }
58+
print("⚠️ multipart key `\(key)` with `\(type(of: children.value))` type is not supported")
3059
}
3160
}
3261
}, to: url, method: .post, headers: headers) { encodingResult in
@@ -63,11 +92,23 @@ extension APIRequest {
6392
v.compactMap { $0.data(using: .utf8) }.forEach { add([$0], as: key, into: multipart) }
6493
}
6594

66-
fileprivate func add(any: Any, as key: String, into multipart: MultipartFormData) {
67-
if let any = any as? [Any] {
68-
any.forEach { add([String(describing: $0)], as: key, into: multipart) }
69-
} else {
70-
add([String(describing: any)], as: key, into: multipart)
71-
}
95+
fileprivate func add(_ v: [UUID], as key: String, into multipart: MultipartFormData) {
96+
add(v.map { $0.uuidString }, as: key, into: multipart)
97+
}
98+
99+
fileprivate func add(_ v: [Int64], as key: String, into multipart: MultipartFormData) {
100+
add(v.map { String(describing: $0) }, as: key, into: multipart)
101+
}
102+
103+
fileprivate func add(_ v: [Float], as key: String, into multipart: MultipartFormData) {
104+
add(v.map { String(describing: $0) }, as: key, into: multipart)
105+
}
106+
107+
fileprivate func add(_ v: [Double], as key: String, into multipart: MultipartFormData) {
108+
add(v.map { String(describing: $0) }, as: key, into: multipart)
109+
}
110+
111+
fileprivate func add(_ v: [Decimal], as key: String, into multipart: MultipartFormData) {
112+
add(v.map { String(describing: $0) }, as: key, into: multipart)
72113
}
73114
}

CodyFire/Classes/APIRequest.swift

+1-1
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,7 @@ public class APIRequest<PayloadType: PayloadProtocol, ResultType: Codable> {
4040
var method: HTTPMethod = .get
4141
var payload: PayloadType?
4242
var query: String?
43-
var headers: [String: String] = CodyFire.shared.fillHeaders?() ?? [:]
43+
var headers: [String: String] = CodyFire.shared.globalHeaders ?? [:]
4444
var desiredStatusCode: HTTPStatusCode = .ok
4545
var successCallback: SuccessResponse?
4646
var knownErrorCallback: KnownErrorResponse?
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,95 @@
1+
//
2+
// CodyFire+PrepareHeaders.swift
3+
// CodyFire
4+
//
5+
// Created by Mihael Isaev on 25/10/2018.
6+
//
7+
8+
import Foundation
9+
10+
extension CodyFire {
11+
fileprivate struct Param { var key, value: String }
12+
13+
var globalHeaders: [String: String] {
14+
var headers = fillHeaders?() ?? [:]
15+
if let codableHeaders = fillCodableHeaders?() {
16+
var dateEncodingStrategy = self.dateEncodingStrategy ?? DateCodingStrategy.default
17+
if let codableHeaders = codableHeaders as? CustomDateEncodingStrategy {
18+
dateEncodingStrategy = codableHeaders.dateEncodingStrategy
19+
}
20+
var params: [Param] = []
21+
for children in Mirror(reflecting: codableHeaders).children {
22+
guard let key = children.label else { continue }
23+
switch children.value {
24+
case let v as [Date]: self.parse(v, as: key + "[]", dateCodingStrategy: dateEncodingStrategy).forEach { params.append($0) }
25+
case let v as Date: self.parse([v], as: key, dateCodingStrategy: dateEncodingStrategy).forEach { params.append($0) }
26+
case let v as [String]: self.parse(v, as: key + "[]").forEach { params.append($0) }
27+
case let v as String: self.parse([v], as: key).forEach { params.append($0) }
28+
case let v as [UUID]: self.parse(v, as: key + "[]").forEach { params.append($0) }
29+
case let v as UUID: self.parse([v], as: key).forEach { params.append($0) }
30+
case let v as [UInt]: self.parse(v.map { Int64($0) }, as: key + "[]").forEach { params.append($0) }
31+
case let v as UInt: self.parse([v].map { Int64($0) }, as: key).forEach { params.append($0) }
32+
case let v as [UInt8]: self.parse(v.map { Int64($0) }, as: key + "[]").forEach { params.append($0) }
33+
case let v as UInt8: self.parse([v].map { Int64($0) }, as: key).forEach { params.append($0) }
34+
case let v as [UInt16]: self.parse(v.map { Int64($0) }, as: key + "[]").forEach { params.append($0) }
35+
case let v as UInt16: self.parse([v].map { Int64($0) }, as: key).forEach { params.append($0) }
36+
case let v as [UInt32]: self.parse(v.map { Int64($0) }, as: key + "[]").forEach { params.append($0) }
37+
case let v as UInt32: self.parse([v].map { Int64($0) }, as: key).forEach { params.append($0) }
38+
case let v as [UInt64]: self.parse(v.map { Int64($0) }, as: key + "[]").forEach { params.append($0) }
39+
case let v as UInt64: self.parse([v].map { Int64($0) }, as: key).forEach { params.append($0) }
40+
case let v as [Int]: self.parse(v.map { Int64($0) }, as: key + "[]").forEach { params.append($0) }
41+
case let v as Int: self.parse([v].map { Int64($0) }, as: key).forEach { params.append($0) }
42+
case let v as [Int8]: self.parse(v.map { Int64($0) }, as: key + "[]").forEach { params.append($0) }
43+
case let v as Int8: self.parse([v].map { Int64($0) }, as: key).forEach { params.append($0) }
44+
case let v as [Int16]: self.parse(v.map { Int64($0) }, as: key + "[]").forEach { params.append($0) }
45+
case let v as Int16: self.parse([v].map { Int64($0) }, as: key).forEach { params.append($0) }
46+
case let v as [Int32]: self.parse(v.map { Int64($0) }, as: key + "[]").forEach { params.append($0) }
47+
case let v as Int32: self.parse([v].map { Int64($0) }, as: key).forEach { params.append($0) }
48+
case let v as [Int64]: self.parse(v, as: key + "[]").forEach { params.append($0) }
49+
case let v as Int64: self.parse([v], as: key).forEach { params.append($0) }
50+
case let v as [Float]: self.parse(v, as: key + "[]").forEach { params.append($0) }
51+
case let v as Float: self.parse([v], as: key).forEach { params.append($0) }
52+
case let v as [Double]: self.parse(v, as: key + "[]").forEach { params.append($0) }
53+
case let v as Double: self.parse([v], as: key).forEach { params.append($0) }
54+
case let v as [Decimal]: self.parse(v, as: key + "[]").forEach { params.append($0) }
55+
case let v as Decimal: self.parse([v], as: key).forEach { params.append($0) }
56+
default:
57+
guard !String(describing: type(of: children.value)).contains("Optional") else { continue }
58+
print("⚠️ query key `\(key)` with \(type(of: children.value)) type is not supported")
59+
}
60+
}
61+
params.forEach { headers[$0.key] = $0.value }
62+
}
63+
return headers
64+
}
65+
66+
//MARK: Converting methods
67+
68+
fileprivate func parse(_ v: [Date], as key: String, dateCodingStrategy: DateCodingStrategy) -> [Param] {
69+
return parse(v.map { dateCodingStrategy.convert($0) }, as: key)
70+
}
71+
72+
fileprivate func parse(_ v: [String], as key: String) -> [Param] {
73+
return v.compactMap { $0.addingPercentEncoding(withAllowedCharacters: .urlPathAllowed) }.map { Param(key: key, value: $0) }
74+
}
75+
76+
fileprivate func parse(_ v: [UUID], as key: String) -> [Param] {
77+
return parse(v.map { $0.uuidString }, as: key)
78+
}
79+
80+
fileprivate func parse(_ v: [Int64], as key: String) -> [Param] {
81+
return parse(v.map { String(describing: $0) }, as: key)
82+
}
83+
84+
fileprivate func parse(_ v: [Float], as key: String) -> [Param] {
85+
return parse(v.map { String(describing: $0) }, as: key)
86+
}
87+
88+
fileprivate func parse(_ v: [Double], as key: String) -> [Param] {
89+
return parse(v.map { String(describing: $0) }, as: key)
90+
}
91+
92+
fileprivate func parse(_ v: [Decimal], as key: String) -> [Param] {
93+
return parse(v.map { String(describing: $0) }, as: key)
94+
}
95+
}

CodyFire/Classes/CodyFire.swift

+2
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ private var _sharedInstance = CodyFire()
1111

1212
public typealias UnauthorizedHandler = ()->()
1313
public typealias FillHeaders = ()->([String: String])
14+
public typealias FillCodableHeaders = ()->(Codable)
1415

1516
open class CodyFire {
1617
public class var shared: CodyFire {
@@ -35,6 +36,7 @@ open class CodyFire {
3536
}
3637

3738
public var fillHeaders: FillHeaders?
39+
public var fillCodableHeaders: FillCodableHeaders?
3840

3941
#if DEBUG
4042
public var logLevel: LogLevel = .debug

0 commit comments

Comments
 (0)