Skip to content

Commit e0ef251

Browse files
committed
[Update] Change APIParameterBuilder from block to struct type
Also contains several changes, see 1.1.0 release note for details
1 parent 2f01637 commit e0ef251

11 files changed

Lines changed: 156 additions & 140 deletions

File tree

Demo.playground/Pages/Basic.xcplaygroundpage/Contents.swift

Lines changed: 3 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -42,27 +42,19 @@ extension BasicAPI {
4242
/// The full api address is: [](https://postman-echo.com/post) .
4343
/// The api is entered as a **tuple** type and requires two parameters, where the second parameter can be `nil`.
4444
@POST("/post")
45-
static var postWithTuple: APIParameterBuilder<(foo1: String, foo2: Int?)>? = {
45+
static var postWithTuple: APIParameterBuilder<(foo1: String, foo2: Int?)>? = .init {
4646
[
4747
"foo1": $0.foo1,
48-
"foo2": $0.foo2,
48+
"foo2": $0.foo2
4949
]
50-
51-
// Eliminate the warning by explicitly converting to `[String: Any?]`.
52-
// Also ensure that `nil` parameters can be filtered.
53-
as [String: Any?]
5450
}
5551

5652
/// This is an api for requests using the **POST** method.
5753
///
5854
/// The full api address is: [](https://postman-echo.com/post) .
5955
/// This api is referenced with the `Arg` type.
6056
@POST("/post")
61-
static var postWithModel: APIParameterBuilder<Arg>? = {
62-
// You can have your model follow the `APIParameterConvertible` protocol.
63-
// or use `AnyAPIHashableParameter` to wrap your model in an outer layer.
64-
AnyAPIHashableParameter($0)
65-
}
57+
static var postWithModel: APIParameterBuilder<Arg>? = .init { $0 }
6658
}
6759

6860
do {

README.md

Lines changed: 7 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -31,17 +31,16 @@ The good thing is that you can easily integrate `RaAPIWrapper` into your existin
3131
static var noParamAPI: APIParameterBuilder<()>? = nil
3232

3333
@POST("/api/v1/tuple_param")
34-
static var tupleParamAPI: APIParameterBuilder<(id: Int, name: String?)>? = {
35-
// Eliminate the warning by explicitly converting to `[String: Any?]`.
36-
// Also ensure that `nil` parameters can be filtered.
37-
["id": $0.id, "name": $0.name] as [String: Any?]
34+
static var tupleParamAPI: APIParameterBuilder<(id: Int, name: String?)>? = .init {
35+
// `Dictionary` and `Array` can be used directly as parameters.
36+
["id": $0.id, "name": $0.name]
3837
}
3938

4039
@POST("/post")
41-
static var postWithModel: APIParameterBuilder<Arg>? = {
42-
// You can have your model follow the `APIParameterConvertible` protocol,
43-
// or use `AnyAPIParameter` to wrap your model in an outer layer.
44-
AnyAPIParameter($0)
40+
static var postWithModel: APIParameterBuilder<Arg>? = .init {
41+
// When the parameter `Arg` complies with the `APIParameter` (`Encodable & Hashable`) protocol,
42+
// it can be used directly as a parameter.
43+
$0
4544
}
4645
```
4746

README_CN.md

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -29,15 +29,15 @@
2929
static var noParamAPI: APIParameterBuilder<()>? = nil
3030

3131
@POST("/api/v1/tuple_param")
32-
static var tupleParamAPI: APIParameterBuilder<(id: Int, name: String?)>? = {
33-
// 通过显式转换为 `[String: Any?]` 来消除警告,同时确保为 `nil` 的参数能够被过滤。
34-
["id": $0.id, "name": $0.name] as [String: Any?]
32+
static var tupleParamAPI: APIParameterBuilder<(id: Int, name: String?)>? = .init {
33+
// 字典和数组可直接作为参数使用
34+
["id": $0.id, "name": $0.name]
3535
}
3636

3737
@POST("/post")
38-
static var postWithModel: APIParameterBuilder<Arg>? = {
39-
// 您可以让您的模型遵循 `APIParameterConvertible` 协议,或者使用 `AnyAPIParameter` 在外面包裹一层
40-
AnyAPIParameter($0)
38+
static var postWithModel: APIParameterBuilder<Arg>? = .init {
39+
// 当参数 `Arg` 遵守 `APIParameter`(`Encodable & Hashable`) 协议时,可直接作为参数使用
40+
$0
4141
}
4242
```
4343

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
//
2-
// AnyAPIParameter.swift
2+
// APIParameter.swift
33
// RaAPIWrapper
44
//
55
// Created by Rakuyo on 2023/01/13.
@@ -8,5 +8,8 @@
88

99
import Foundation
1010

11+
/// Used to constrain what types can be used as api parameters.
12+
public typealias APIParameter = AnyAPIHashableParameter.Input
13+
1114
/// Represents an arbitrary api parameter.
1215
public typealias AnyAPIParameter = AnyAPIHashableParameter

Sources/Core/RequestInfo/APIParameterConvertible.swift

Lines changed: 0 additions & 91 deletions
This file was deleted.

Sources/Core/RequestInfo/APIParametrizable.swift

Lines changed: 0 additions & 12 deletions
This file was deleted.

Sources/Core/RequestInfo/APIRequestInfo.swift

Lines changed: 18 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -31,20 +31,35 @@ public struct APIRequestInfo {
3131
/// You can use this property to store some custom data.
3232
public let userInfo: APIRequestUserInfo
3333

34-
public init(
34+
public init<ParamType>(
3535
path: String,
3636
specialBaseURL: URL? = nil,
3737
httpMethod: APIHTTPMethod,
3838
header: APIHeaders? = nil,
39-
parameters: AnyAPIParameter? = nil,
39+
parameterBuild: APIParameterBuilder<ParamType>? = nil,
40+
parameterInput: ParamType? = nil,
4041
userInfo: APIRequestUserInfo = [:]
4142
) {
4243
self.path = path
4344
self.specialBaseURL = specialBaseURL
4445
self.httpMethod = httpMethod
4546
self.header = header
46-
self.parameters = parameters
4747
self.userInfo = userInfo
48+
49+
self.parameters = {
50+
guard
51+
let parameter = parameterInput,
52+
let build = parameterBuild
53+
else {
54+
return nil
55+
}
56+
57+
let result = build(parameter)
58+
if let value = result as? AnyAPIParameter {
59+
return value
60+
}
61+
return .init(result)
62+
}()
4863
}
4964
}
5065

Sources/Core/Wrapper/API.swift

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -8,9 +8,6 @@
88

99
import Foundation
1010

11-
/// Parameter constructor for the api. Supports passing one parameter.
12-
public typealias APIParameterBuilder<ParamType> = (ParamType) -> APIParameterConvertible
13-
1411
/// Used to encapsulate the `APIHTTPMethod` object provided to the `API`.
1512
public protocol APIHTTPMethodWrapper {
1613
static var httpMethod: APIHTTPMethod { get }
@@ -75,7 +72,8 @@ public extension API {
7572
specialBaseURL: specialBaseURL,
7673
httpMethod: Self.httpMethod.httpMethod,
7774
header: headerBuilder?(parameter),
78-
parameters: wrappedValue?(parameter).toParameters,
75+
parameterBuild: wrappedValue,
76+
parameterInput: parameter,
7977
userInfo: userInfo
8078
)
8179
}
Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
//
2+
// APIParameterBuilder.swift
3+
// RaAPIWrapper
4+
//
5+
// Created by Rakuyo on 2023/01/13.
6+
// Copyright © 2022 Rakuyo. All rights reserved.
7+
//
8+
9+
import Foundation
10+
11+
public struct APIParameterBuilder<Input> {
12+
public typealias Output = any APIParameter
13+
14+
public typealias ParameterBuilder = (Input) -> Output
15+
16+
private let parameterBuild: ParameterBuilder
17+
18+
public init(_ build: @escaping ParameterBuilder) {
19+
self.parameterBuild = build
20+
}
21+
}
22+
23+
public extension APIParameterBuilder {
24+
init(_ build: @escaping (Input) -> [Any?]) {
25+
self.parameterBuild = { build($0).toParameters }
26+
}
27+
28+
init(_ build: @escaping (Input) -> [String: Any?]) {
29+
self.parameterBuild = { build($0).toParameters }
30+
}
31+
}
32+
33+
extension APIParameterBuilder {
34+
func callAsFunction(_ input: Input) -> Output {
35+
return parameterBuild(input)
36+
}
37+
}
Lines changed: 75 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,75 @@
1+
//
2+
// APIParameterConvertible.swift
3+
// RaAPIWrapper
4+
//
5+
// Created by Rakuyo on 2022/8/25.
6+
// Copyright © 2022 Rakuyo. All rights reserved.
7+
//
8+
9+
import Foundation
10+
11+
/// Means that the type can be converted to an interface parameter for requesting an api.
12+
protocol APIParameterConvertible {
13+
/// Converts the target to an Encodable-compliant type.
14+
var toParameters: AnyAPIParameter { get }
15+
}
16+
17+
// MARK: - Array
18+
19+
extension Array: APIParameterConvertible {
20+
/// For `[AnyObject]` arrays, or a mixed array with very complex elements.
21+
var toParameters: AnyAPIParameter {
22+
let filtered = (self as [Any?]).compactMap { $0 }
23+
24+
let result: [AnyAPIParameter] = filtered.compactMap {
25+
if let value = $0 as? AnyAPIParameter { return value }
26+
if let value = $0 as? (any APIParameter) { return .init(value) }
27+
if let value = $0 as? APIParameterConvertible { return value.toParameters }
28+
return mapAnyObjectToEncodable($0 as AnyObject)
29+
}
30+
31+
assert(filtered.count == result.count, "There are elements in the container type that are not nil that cannot be converted to AnyAPIParameter type. This behavior will cause the parameter to be discarded and not sent to the server. This behavior is usually dangerous and needs to be fixed, please check your code!")
32+
33+
return .init(result)
34+
}
35+
}
36+
37+
// MARK: - Dictionary
38+
39+
extension Dictionary: APIParameterConvertible where Key == String {
40+
/// For a `[String: AnyObject]` dictionary, or a mixed dictionary with very complex elements.
41+
var toParameters: AnyAPIParameter {
42+
let filtered = (self as [String: Any?]).compactMapValues { $0 }
43+
44+
let result: [String: AnyAPIParameter] = filtered.compactMapValues {
45+
if let value = $0 as? AnyAPIParameter { return value }
46+
if let value = $0 as? (any APIParameter) { return .init(value) }
47+
if let value = $0 as? APIParameterConvertible { return value.toParameters }
48+
return mapAnyObjectToEncodable($0 as AnyObject)
49+
}
50+
51+
assert(filtered.count == result.count, "There are elements in the container type that are not nil that cannot be converted to AnyAPIParameter type. This behavior will cause the parameter to be discarded and not sent to the server. This behavior is usually dangerous and needs to be fixed, please check your code!")
52+
53+
return .init(result)
54+
}
55+
}
56+
57+
// MARK: - Tools
58+
59+
private extension APIParameterConvertible {
60+
func mapAnyObjectToEncodable(_ value: AnyObject) -> AnyAPIParameter? {
61+
if let result = value as? String { return .init(result) }
62+
if let result = value as? Character { return .init(String(result)) }
63+
if let result = value as? Int { return .init(result) }
64+
if let result = value as? UInt { return .init(result) }
65+
if let result = value as? Double { return .init(result) }
66+
if let result = value as? Float { return .init(result) }
67+
if let result = value as? Bool { return .init(result) }
68+
if let result = value as? Data { return .init(result) }
69+
if let result = value as? Date { return .init(result) }
70+
if let result = value as? [String: Any] { return result.toParameters }
71+
if let result = value as? [Any] { return result.toParameters }
72+
73+
return nil
74+
}
75+
}

0 commit comments

Comments
 (0)