Skip to content

Commit c43dca2

Browse files
committed
allow modelDecoder and errorDecoder to be customized in CodableSerializer. Allow JSONSerialization.ReadingOptions to be specified for SwiftyJSON. Use beta and alpha releases of RxSwift and SwiftyJSON.
1 parent 4d682a2 commit c43dca2

9 files changed

+195
-85
lines changed

Diff for: CHANGELOG.md

+11
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,17 @@ All notable changes to this project will be documented in this file.
33

44
# Next
55

6+
* Added ability to customize `JSONSerialization.ReadingOptions` for `JSONDecodableSerializer`. Unlike previous releases, no options are specified by default(Previously SwiftyJSON used `allowFragments` option).
7+
8+
To have old behavior that allows fragmented json, use:
9+
10+
```swift
11+
let request = tron.swiftyJSON(readingOptions: .allowFragments).request("path")
12+
```
13+
14+
* Added ability to customize `JSONDecoder` for `CodableSerializer` for both model parsing and error parsing.
15+
* `CodableParser` and `JSONDecodableParser` are now classes, that are subclassible.
16+
617
## [4.0.0-beta.1](https://github.com/MLSDev/TRON/releases/tag/4.0.0-beta.1)
718

819
**This is major release, containing breaking API changes, please read [TRON 4.0 Migration Guide](https://github.com/MLSDev/TRON/blob/master/Docs/4.0%20Migration%20Guide.md)**

Diff for: Cartfile

+2-2
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,3 @@
11
github "Alamofire/Alamofire"
2-
github "SwiftyJSON/SwiftyJSON"
3-
github "ReactiveX/RxSwift"
2+
github "SwiftyJSON/SwiftyJSON" "4.0.0-alpha.1"
3+
github "ReactiveX/RxSwift" "4.0.0-beta.0"

Diff for: Cartfile.resolved

+3-3
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
11
github "Alamofire/Alamofire" "4.5.1"
2-
github "Quick/Nimble" "v7.0.1"
3-
github "ReactiveX/RxSwift" "3.6.1"
4-
github "SwiftyJSON/SwiftyJSON" "3.1.4"
2+
github "Quick/Nimble" "v7.0.2"
3+
github "ReactiveX/RxSwift" "4.0.0-beta.0"
4+
github "SwiftyJSON/SwiftyJSON" "4.0.0-alpha.1"

Diff for: README.md

+17
Original file line numberDiff line numberDiff line change
@@ -197,6 +197,17 @@ request.perform(withSuccess: { user in
197197
})
198198
```
199199

200+
It's possible to customize decoders for both model and error parsing:
201+
202+
```swift
203+
let userDecoder = JSONDecoder()
204+
// Customization for user decoder...
205+
let errorDecoder = JSONDecoder()
206+
// Customization for error decoder...
207+
208+
let request : APIRequest<User,MyAppError> = tron.codable(modelDecoder: userDecoder, errorDecoder: errorDecoder).request("me")
209+
```
210+
200211
### JSONDecodable
201212

202213
`TRON` provides `JSONDecodable` protocol, that allows us to parse models using [SwiftyJSON](https://github.com/SwiftyJSON/SwiftyJSON):
@@ -243,6 +254,12 @@ You can also use `EmptyResponse` struct in cases where you don't care about actu
243254

244255
Some concepts for response serialization, including array response serializer, are described in [Response Serializers document](https://github.com/MLSDev/TRON/blob/master/Docs/Response%20Serializers.md)
245256

257+
It's possible to customize `JSONSerialization.ReadingOptions`, that are used by `SwiftyJSON.JSON` object while parsing data of the response:
258+
259+
```swift
260+
let request : APIRequest<String, MyAppError> = tron.swiftyJSON(readingOptions: .allowFragments).request("status")
261+
```
262+
246263
## RxSwift
247264

248265
```swift

Diff for: Source/SwiftyJSONDecodable.swift

+66-40
Original file line numberDiff line numberDiff line change
@@ -37,17 +37,22 @@ public protocol JSONDecodable {
3737
}
3838

3939
/// `JSONDecodable` data response parser
40-
public struct JSONDecodableParser<Model: JSONDecodable, ErrorModel: JSONDecodable> : ErrorHandlingDataResponseSerializerProtocol
40+
open class JSONDecodableParser<Model: JSONDecodable, ErrorModel: JSONDecodable> : ErrorHandlingDataResponseSerializerProtocol
4141
{
4242
public typealias SerializedError = ErrorModel
4343

44-
public init() {}
44+
public let options: JSONSerialization.ReadingOptions
45+
46+
public init(options: JSONSerialization.ReadingOptions) {
47+
self.options = options
48+
}
4549

4650
/// A closure used by response handlers that takes a request, response, data and error and returns a result.
47-
public var serializeResponse: (URLRequest?, HTTPURLResponse?, Data?, Error?) -> Result<Model> {
48-
return { request, response, data, error in
49-
let json = JSON(data: data ?? Data())
51+
open var serializeResponse: (URLRequest?, HTTPURLResponse?, Data?, Error?) -> Result<Model> {
52+
return { [weak self] request, response, data, error in
5053
do {
54+
let data = data ?? Data()
55+
let json = (try? JSON(data: data, options: self?.options ?? [])) ?? JSON.null
5156
let model = try Model.init(json: json)
5257
return Result.success(model)
5358
}
@@ -56,16 +61,15 @@ public struct JSONDecodableParser<Model: JSONDecodable, ErrorModel: JSONDecodabl
5661
}
5762
}
5863
}
59-
}
60-
61-
extension ErrorHandlingDataResponseSerializerProtocol where SerializedError : JSONDecodable
62-
{
64+
6365
/// A closure used by response handlers that takes a parsed result, request, response, data and error and returns a serialized error.
64-
public var serializeError: (Result<SerializedObject>?,URLRequest?, HTTPURLResponse?, Data?, Error?) -> APIError<SerializedError> {
65-
return { erroredResponse, request, response, data, error in
66+
open var serializeError: (Result<SerializedObject>?,URLRequest?, HTTPURLResponse?, Data?, Error?) -> APIError<SerializedError> {
67+
return { [weak self] erroredResponse, request, response, data, error in
6668
let serializationError : Error? = erroredResponse?.error ?? error
6769
var error = APIError<SerializedError>(request: request, response: response,data: data, error: serializationError)
68-
error.errorModel = try? SerializedError.init(json: JSON(data: data ?? Data()))
70+
let data = data ?? Data()
71+
let json = (try? JSON(data: data, options: self?.options ?? [])) ?? JSON.null
72+
error.errorModel = try? SerializedError.init(json: json)
6973
return error
7074
}
7175
}
@@ -77,18 +81,22 @@ public enum JSONDecodableDownloadSerializationError : Error {
7781
}
7882

7983
/// `JSONDecodable` download response parser
80-
public struct JSONDecodableDownloadParser<Model: JSONDecodable, ErrorModel: JSONDecodable> : ErrorHandlingDownloadResponseSerializerProtocol
84+
open class JSONDecodableDownloadParser<Model: JSONDecodable, ErrorModel: JSONDecodable> : ErrorHandlingDownloadResponseSerializerProtocol
8185
{
8286
public typealias SerializedError = ErrorModel
8387

84-
public init() {}
88+
public let options: JSONSerialization.ReadingOptions
89+
90+
public init(options: JSONSerialization.ReadingOptions) {
91+
self.options = options
92+
}
8593

8694
/// A closure used by response handlers that takes a request, response, url and error and returns a result.
87-
public var serializeResponse: (URLRequest?, HTTPURLResponse?, URL?, Error?) -> Result<Model> {
88-
return { request, response, url, error in
95+
open var serializeResponse: (URLRequest?, HTTPURLResponse?, URL?, Error?) -> Result<Model> {
96+
return { [weak self] request, response, url, error in
8997
if let url = url, let data = try? Data(contentsOf: url) {
90-
let json = JSON(data: data)
9198
do {
99+
let json = (try? JSON(data: data, options: self?.options ?? [])) ?? JSON.null
92100
let model = try Model.init(json: json)
93101
return Result.success(model)
94102
}
@@ -99,20 +107,18 @@ public struct JSONDecodableDownloadParser<Model: JSONDecodable, ErrorModel: JSON
99107
return .failure(JSONDecodableDownloadSerializationError.failedToCreateJSONResponse)
100108
}
101109
}
102-
}
103-
104-
extension ErrorHandlingDownloadResponseSerializerProtocol where SerializedError : JSONDecodable
105-
{
110+
106111
/// A closure used by response handlers that takes a parsed result, request, response, url and error and returns a serialized error.
107-
public var serializeError: (Result<SerializedObject>?,URLRequest?, HTTPURLResponse?, URL?, Error?) -> APIError<SerializedError> {
108-
return { erroredResponse, request, response, url, error in
112+
open var serializeError: (Result<SerializedObject>?,URLRequest?, HTTPURLResponse?, URL?, Error?) -> APIError<SerializedError> {
113+
return { [weak self] erroredResponse, request, response, url, error in
109114
let serializationError : Error? = erroredResponse?.error ?? error
110115
var data : Data?
111116
if let url = url {
112117
data = try? Data(contentsOf: url)
113118
}
114119
var error = APIError<SerializedError>(request: request, response: response,data: data, error: serializationError)
115-
error.errorModel = try? SerializedError.init(json: JSON(data: data ?? Data()))
120+
let json: JSON = (try? JSON(data: data ?? Data(), options: self?.options ?? [])) ?? JSON.null
121+
error.errorModel = try? SerializedError.init(json: json)
116122
return error
117123
}
118124
}
@@ -126,9 +132,20 @@ extension JSON : JSONDecodable {
126132
}
127133
}
128134

129-
public struct JSONDecodableSerializer
135+
// Serializer for objects, that conform to `JSONDecodable` protocol.
136+
open class JSONDecodableSerializer
130137
{
131-
public let tron: TRON
138+
// `TRON` instance to be used to send requests
139+
let tron: TRON
140+
141+
// Reading options to use while calling `JSONSerialization.jsonObject(withData:options:)`
142+
open var options : JSONSerialization.ReadingOptions
143+
144+
// Creates `JSONDecodableSerializer` with `tron` instance to send requests, and JSON reading `options` to be passed to `JSONSerialization`.
145+
public init(tron: TRON, options: JSONSerialization.ReadingOptions = []) {
146+
self.tron = tron
147+
self.options = options
148+
}
132149

133150
/**
134151
Creates APIRequest with specified relative path and type RequestType.Default.
@@ -137,9 +154,9 @@ public struct JSONDecodableSerializer
137154

138155
- returns: APIRequest instance.
139156
*/
140-
public func request<Model:JSONDecodable,ErrorModel:JSONDecodable>(_ path: String) -> APIRequest<Model,ErrorModel>
157+
open func request<Model:JSONDecodable,ErrorModel:JSONDecodable>(_ path: String) -> APIRequest<Model,ErrorModel>
141158
{
142-
return tron.request(path, responseSerializer: JSONDecodableParser())
159+
return tron.request(path, responseSerializer: JSONDecodableParser(options: options))
143160
}
144161

145162
/**
@@ -151,9 +168,9 @@ public struct JSONDecodableSerializer
151168

152169
- returns: APIRequest instance.
153170
*/
154-
public func upload<Model:JSONDecodable, ErrorModel:JSONDecodable>(_ path: String, fromFileAt fileURL: URL) -> UploadAPIRequest<Model,ErrorModel>
171+
open func upload<Model:JSONDecodable, ErrorModel:JSONDecodable>(_ path: String, fromFileAt fileURL: URL) -> UploadAPIRequest<Model,ErrorModel>
155172
{
156-
return tron.upload(path, fromFileAt: fileURL, responseSerializer: JSONDecodableParser())
173+
return tron.upload(path, fromFileAt: fileURL, responseSerializer: JSONDecodableParser(options: options))
157174
}
158175

159176
/**
@@ -165,9 +182,9 @@ public struct JSONDecodableSerializer
165182

166183
- returns: APIRequest instance.
167184
*/
168-
public func upload<Model:JSONDecodable, ErrorModel:JSONDecodable>(_ path: String, data: Data) -> UploadAPIRequest<Model,ErrorModel>
185+
open func upload<Model:JSONDecodable, ErrorModel:JSONDecodable>(_ path: String, data: Data) -> UploadAPIRequest<Model,ErrorModel>
169186
{
170-
return tron.upload(path, data: data, responseSerializer: JSONDecodableParser())
187+
return tron.upload(path, data: data, responseSerializer: JSONDecodableParser(options: options))
171188
}
172189

173190
/**
@@ -179,9 +196,9 @@ public struct JSONDecodableSerializer
179196

180197
- returns: APIRequest instance.
181198
*/
182-
public func upload<Model:JSONDecodable, ErrorModel:JSONDecodable>(_ path: String, from stream: InputStream) -> UploadAPIRequest<Model,ErrorModel>
199+
open func upload<Model:JSONDecodable, ErrorModel:JSONDecodable>(_ path: String, from stream: InputStream) -> UploadAPIRequest<Model,ErrorModel>
183200
{
184-
return tron.upload(path, from: stream, responseSerializer: JSONDecodableParser())
201+
return tron.upload(path, from: stream, responseSerializer: JSONDecodableParser(options: options))
185202
}
186203

187204
/**
@@ -193,11 +210,11 @@ public struct JSONDecodableSerializer
193210

194211
- returns: MultipartAPIRequest instance.
195212
*/
196-
public func uploadMultipart<Model:JSONDecodable, ErrorModel:JSONDecodable>(_ path: String,
213+
open func uploadMultipart<Model:JSONDecodable, ErrorModel:JSONDecodable>(_ path: String,
197214
formData: @escaping (MultipartFormData) -> Void) -> UploadAPIRequest<Model,ErrorModel>
198215
{
199216
return tron.uploadMultipart(path,
200-
responseSerializer: JSONDecodableParser(),
217+
responseSerializer: JSONDecodableParser(options: options),
201218
formData: formData)
202219
}
203220

@@ -212,11 +229,11 @@ public struct JSONDecodableSerializer
212229

213230
- seealso: `Alamofire.Request.suggestedDownloadDestination(directory:domain:)` method.
214231
*/
215-
public func download<Model:JSONDecodable, ErrorModel:JSONDecodable>(_ path: String, to destination: @escaping DownloadRequest.DownloadFileDestination) -> DownloadAPIRequest<Model, ErrorModel>
232+
open func download<Model:JSONDecodable, ErrorModel:JSONDecodable>(_ path: String, to destination: @escaping DownloadRequest.DownloadFileDestination) -> DownloadAPIRequest<Model, ErrorModel>
216233
{
217234
return tron.download(path,
218235
to: destination,
219-
responseSerializer: JSONDecodableDownloadParser())
236+
responseSerializer: JSONDecodableDownloadParser(options: options))
220237
}
221238

222239
/**
@@ -232,18 +249,27 @@ public struct JSONDecodableSerializer
232249

233250
- seealso: `Alamofire.Request.suggestedDownloadDestination(directory:domain:)` method.
234251
*/
235-
public func download<Model:JSONDecodable,ErrorModel:JSONDecodable>(_ path: String, to destination: @escaping DownloadRequest.DownloadFileDestination, resumingFrom: Data) -> DownloadAPIRequest<Model, ErrorModel>
252+
open func download<Model:JSONDecodable,ErrorModel:JSONDecodable>(_ path: String, to destination: @escaping DownloadRequest.DownloadFileDestination, resumingFrom: Data) -> DownloadAPIRequest<Model, ErrorModel>
236253
{
237-
return tron.download(path, to: destination, resumingFrom: resumingFrom, responseSerializer: JSONDecodableDownloadParser())
254+
return tron.download(path, to: destination, resumingFrom: resumingFrom, responseSerializer: JSONDecodableDownloadParser(options: options))
238255
}
239256
}
240257

241258
extension TRON {
259+
// Creates `JSONDecodableSerializer` with current `TRON` instance.
242260
open var swiftyJSON : JSONDecodableSerializer {
243261
return JSONDecodableSerializer(tron: self)
244262
}
263+
264+
// Creates `CodableSerializer` with current `TRON` instance and specific `options` for `JSONSerialization`.
265+
open func swiftyJSON(readingOptions options: JSONSerialization.ReadingOptions) -> JSONDecodableSerializer {
266+
return JSONDecodableSerializer(tron: self, options: options)
267+
}
245268
}
246269

270+
// This approach is bad, because it allows any JSONDecodable.Type to be created, not just specific one.
271+
// See https://github.com/MLSDev/TRON/issues/17 for details
272+
247273
//extension Array : JSONDecodable {
248274
// public init(json: JSON) {
249275
// self.init(json.arrayValue.flatMap {

0 commit comments

Comments
 (0)