Skip to content

Commit 2acce47

Browse files
committed
update readme and migration guide, replace Argo playground with Unbox.
1 parent b90756f commit 2acce47

File tree

8 files changed

+114
-80
lines changed

8 files changed

+114
-80
lines changed

Custom mappers/Argo.playground/Contents.swift

-33
This file was deleted.
Original file line numberDiff line numberDiff line change
@@ -1,21 +1,23 @@
11
//: Playground - noun: a place where people can play
22

3+
import Cocoa
34
import ObjectMapper
45
import TRON
6+
import Foundation
57

68
struct ObjectMapperError: ErrorType {}
79

810
public protocol ObjectMapperParseable : ResponseParseable {
9-
init?(map: Map)
11+
init(map: Map)
1012
}
1113

12-
public extension ResponseParseable where Self.ModelType : ObjectMapperParseable {
13-
public static func from(json: AnyObject) throws -> ModelType {
14-
let map = Map(mappingType: .FromJSON, JSONDictionary: json as! [String:AnyObject], toObject: true)
15-
guard let model = ModelType(map: map) else {
14+
public extension ResponseParseable where Self : ObjectMapperParseable {
15+
public init(data: NSData) throws {
16+
guard let dictionary = try? NSJSONSerialization.JSONObjectWithData(data, options: NSJSONReadingOptions.AllowFragments) as? [String:AnyObject] else {
1617
throw ObjectMapperError()
1718
}
18-
return model
19+
let map = Map(mappingType: .FromJSON, JSONDictionary: dictionary ?? [:])
20+
self.init(map: map)
1921
}
2022
}
2123

@@ -30,10 +32,8 @@ struct Headers: ObjectMapperParseable {
3032

3133
let tron = TRON(baseURL: "http://httpbin.org")
3234
let request: APIRequest<Headers,Int> = tron.request(path: "headers")
33-
request.performWithSuccess({ headers in
35+
request.perform(success: { headers in
3436
print(headers.host)
37+
}, failure: { error in
38+
print(error)
3539
})
36-
37-
38-
39-
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
11
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
2-
<playground version='5.0' target-platform='ios' executeOnSourceChanges='false'>
2+
<playground version='5.0' target-platform='osx'>
33
<timeline fileName='timeline.xctimeline'/>
44
</playground>
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
//: Playground - noun: a place where people can play
2+
3+
import Cocoa
4+
5+
import Unbox
6+
import TRON
7+
import Foundation
8+
9+
struct UnboxMapperError: ErrorType {}
10+
11+
public extension ResponseParseable where Self: Unboxable {
12+
init(data: NSData) throws {
13+
// There seems to be no way to directly call initializers with unbox, so we forbid such initializer
14+
throw UnboxMapperError()
15+
}
16+
}
17+
18+
public class UnboxResponseBuilder<T:ResponseParseable where T: Unboxable> : ResponseBuilder<T>
19+
{
20+
public override init() {}
21+
22+
public override func buildResponseFromData(data: NSData) throws -> T {
23+
return try Unbox(data)
24+
}
25+
}
26+
27+
struct Headers: Unboxable, ResponseParseable {
28+
29+
var host : String
30+
31+
init(unboxer: Unboxer) {
32+
host = unboxer.unbox("host")
33+
}
34+
}
35+
36+
let tron = TRON(baseURL: "http://httpbin.org")
37+
let request: APIRequest<Headers,Int> = tron.request(path: "headers")
38+
request.responseBuilder = UnboxResponseBuilder()
39+
request.perform(success: { headers in
40+
print(headers.host)
41+
}, failure: { error in
42+
print(error)
43+
})
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
11
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
2-
<playground version='5.0' target-platform='ios'>
2+
<playground version='5.0' target-platform='osx'>
33
<timeline fileName='timeline.xctimeline'/>
44
</playground>

Docs/1.0 Migration Guide.md

+31-5
Original file line numberDiff line numberDiff line change
@@ -47,12 +47,12 @@ request.performWithSuccess({ user in
4747
progress: { progress in
4848
print("Picture is uploading, progress: \(CGFloat(progress.totalBytesWritten) / CGFloat(progress.totalBytesExpectedToWrite))")
4949
})
50-
50+
5151
// New:
52-
let request: MultipartAPIRequest<User,MyAppError> = tron.uploadMultipart(path: "profile") { formData in
52+
let request: MultipartAPIRequest<User,MyAppError> = tron.uploadMultipart(path: "profile") { formData in
5353
formData.appendBodyPart(data: data,name: "avatar", mimeType: "image/jpeg")
5454
}
55-
request.performMultipart(success: { user in
55+
request.performMultipart(success: { user in
5656
print("user avatar updated!")
5757
},
5858
failure: { error in
@@ -119,13 +119,39 @@ Old `performWithSuccess(_:failure:)` method is deprecated and will be removed in
119119
Since we no longer hide Alamofire from developer, we are now able to provide perform method with `Alamofire.Response` completion block, which can be used in several useful ways, for example observing request timeline:
120120

121121
```swift
122-
request.perform(completion: { response in
122+
request.perform(completion: { response in
123123
print(response.result) // Alamofire.Result
124124
print(response.timeline) // Alamofire.Timeline
125125
print(response.response) // NSHTTPURLResponse?
126126
})
127127
```
128128

129+
### ResponseParseable protocol
130+
131+
`ResponseParseable` protocol received major overhaul in this release. Previously, we used associatedtype ModelType to define type of parsed response. It was not very obvious, and required declaring all generic constraints as `Model.ModelType`. In 1.0.0, we changed protocol to contain simple throwing initializer:
132+
133+
```swift
134+
public protocol ResponseParseable {
135+
init(data: NSData) throws
136+
}
137+
```
138+
139+
This way we are allowing to use any kind of parser, not only JSON, and any other mapper as well. For example, here's how SwiftyJSON extension is implemented:
140+
141+
```swift
142+
public protocol JSONDecodable : ResponseParseable {
143+
init(json: JSON) throws
144+
}
145+
146+
public extension ResponseParseable where Self: JSONDecodable {
147+
init(data: NSData) throws {
148+
try self.init(json: JSON(data: data))
149+
}
150+
}
151+
```
152+
153+
That's really all you have to do to use a custom mapper with TRON.
154+
129155
### Miscellaneous API improvements
130156

131157
Previously, `MultipartAPIRequest` was a subclass of `APIRequest`, which allowed to define it's variable as `APIRequest`, and potentially could lead to runtime crash with improper use. Now, both `MultipartAPIRequest` and `APIRequest` subclass `BaseRequest`, thus preventing this use case.
@@ -134,4 +160,4 @@ Previously, `MultipartAPIRequest` was a subclass of `APIRequest`, which allowed
134160

135161
`EventDispatcher` class, `TRON.dispatcher` and `APIRequest.dispatcher` properties are replaced with `processingQueue` and `resultDeliveryQueue` properties on `TRON` and `APIRequest`. `processingQueue` property determines on which queue should response parsing proceed, and `resultDeliveryQueue` determines on which queue completion blocks should be called.
136162

137-
`RxSwift` extension `rxMultipartResult()` method on `MultipartAPIRequest` now returns Observable<ModelType> instead of tuple of observables.
163+
`RxSwift` extension `rxMultipartResult()` method on `MultipartAPIRequest` now returns Observable<Model> instead of tuple of observables.

README.md

+23-25
Original file line numberDiff line numberDiff line change
@@ -42,8 +42,8 @@ request.perform(success: { user in
4242

4343
## Requirements
4444

45-
- XCode 7.3+
46-
- Swift 2.2+
45+
- XCode 7.3
46+
- Swift 2.2
4747
- iOS 8
4848

4949
## Installation
@@ -96,7 +96,7 @@ public protocol NSURLBuildable {
9696
}
9797
```
9898

99-
By default, `TRON` uses `URLBuilder` class, that simply appends relative path to base URL, which is sufficient in most cases. You can customize url building process globally by changing `urlBuilder` property on `Tron` or locally, for a single request by modifying `urlBuilder` property on `APIRequest`.
99+
By default, `TRON` uses `URLBuilder` class, that simply appends relative path to base URL, which is sufficient in most cases. You can customize url building process globally by changing `urlBuilder` property on `TRON` or locally, for a single request by modifying `urlBuilder` property on `APIRequest`.
100100

101101
### HeaderBuildable
102102

@@ -116,7 +116,7 @@ public enum AuthorizationRequirement {
116116
}
117117
```
118118

119-
It represents scenarios where user is not authorized, user is authorized, but authentication is not required, and a case, where request requires authentication.
119+
It represents scenarios where user is not authorized, user is authorized, but authorization is not required, and a case, where request requires authorization.
120120

121121
By default, `TRON` uses `HeaderBuilder` class, which adds "Accept":"application/json" header to your requests.
122122

@@ -149,15 +149,25 @@ request.perform(success: { result in }, failure: { error in })?.progress { bytes
149149

150150
## Response parsing
151151

152-
Generic `APIRequest` implementation allows us to define expected response type before request is even sent. It also allows us to setup basic parsing rules, which is where `SwiftyJSON` comes in. We define a simple `JSONDecodable` protocol, that allows us to create models of specific type:
152+
Generic `APIRequest` implementation allows us to define expected response type before request is even sent. It also allows us to setup basic parsing rules, which is where `ResponseParseable` protocol comes in.
153153

154154
```swift
155-
public protocol JSONDecodable {
156-
init(json: JSON)
155+
public protocol ResponseParseable {
156+
init(data: NSData) throws
157157
}
158158
```
159159

160-
To parse your response from the server, all you need to do is to create `JSONDecodable` conforming type, for example:
160+
As you can see, protocol accepts NSData in initializer, which means anything can be parsed - JSON, or XML or something else.
161+
162+
`TRON` also provides `JSONDecodable` protocol, that allows us to parse models using SwiftyJSON:
163+
164+
```swift
165+
public protocol JSONDecodable: ResponseParseable {
166+
init(json: JSON) throws
167+
}
168+
```
169+
170+
To parse your response from the server using `SwiftyJSON`, all you need to do is to create `JSONDecodable` conforming type, for example:
161171

162172
```swift
163173
class User: JSONDecodable {
@@ -180,7 +190,7 @@ request.perform(success: { user in
180190
})
181191
```
182192

183-
There are also default implementations of `JSONDecodable` protocol for Swift built-in types like String, Int, Float, Double and Bool, so you can easily do something like this:
193+
There are also default implementations of `JSONDecodable` protocol for Swift built-in types like Array, String, Int, Float, Double and Bool, so you can easily do something like this:
184194

185195
```swift
186196
let request : APIRequest<String,MyAppError> = tron.request(path: "status")
@@ -189,25 +199,15 @@ request.perform(success: { status in
189199
})
190200
```
191201

192-
You can also use `EmptyResponse` struct in cases where you don't care about actual response, or response from the server is empty(<>).
202+
You can also use `EmptyResponse` struct in cases where you don't care about actual response.
193203

194204
## Custom mappers
195205

196-
Instead of `JSONDecodable`, all generic constraints on TRON accept `ResponseParseable` protocol, that can be easily implemented for your mapper.
197-
198-
By default, we are using SwiftyJSON, and adding protocol default implementations on it.
206+
All generic constraints on TRON accept `ResponseParseable` protocol, that can be easily implemented for your mapper.
199207

200-
**Note** Custom mappers are supported only when installing framework from CocoaPods due to inability of Carthage to split framework to subspecs.
208+
We are providing code examples on how to do this with two most mappers available in Swift - Unbox and ObjectMapper.
201209

202-
To use custom mapper, use Core podspec of TRON:
203-
204-
```ruby
205-
pod 'TRON/Core'
206-
```
207-
208-
Then add your custom mapper protocol extension. We are providing code examples on how to do this with two most popular mappers available in Swift - Argo and ObjectMapper.
209-
210-
[Playground with Argo ResponseParseable implementation](https://github.com/MLSDev/TRON/blob/master/Custom%20mappers/Argo.playground/Contents.swift)
210+
[Playground with Unbox ResponseParseable implementation](https://github.com/MLSDev/TRON/blob/master/Custom%20mappers/Unbox.playground/Contents.swift)
211211

212212
[Playground with ObjectMapper ResponseParseable implementation](https://github.com/MLSDev/TRON/blob/master/Custom%20mappers/ObjectMapper.playground/Contents.swift)
213213

@@ -443,8 +443,6 @@ We are dedicated to building best possible tool for interacting with RESTful web
443443

444444
`TRON` was heavily inspired by [Moya framework](https://github.com/Moya/Moya) and [LevelUPSDK](https://github.com/TheLevelUp/levelup-sdk-ios/blob/master/Source/API/Client/LUAPIClient.h)
445445

446-
There are also alternative JSON mappers available, such as [Unbox](https://github.com/JohnSundell/Unbox)
447-
448446
## License
449447

450448
`TRON` is released under the MIT license. See LICENSE for details.

TRON.xcodeproj/project.pbxproj

+4-4
Original file line numberDiff line numberDiff line change
@@ -632,6 +632,8 @@
632632
9A2F5F731CE8775900C3C0F9 /* MultipartAPIRequest.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = MultipartAPIRequest.swift; path = Source/Core/MultipartAPIRequest.swift; sourceTree = "<group>"; };
633633
9A51AABC1CC61ED80053CC48 /* Tron+RxSwift.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = "Tron+RxSwift.swift"; path = "Source/RxSwift/Tron+RxSwift.swift"; sourceTree = "<group>"; };
634634
9A51AAC11CC620D60053CC48 /* RxSwiftExtensionTestCase.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = RxSwiftExtensionTestCase.swift; path = Tests/RxSwiftExtensionTestCase.swift; sourceTree = "<group>"; };
635+
9A6693561CEF2A7C007CBC45 /* Unbox.playground */ = {isa = PBXFileReference; lastKnownFileType = file.playground; name = Unbox.playground; path = "Custom mappers/Unbox.playground"; sourceTree = "<group>"; };
636+
9A6693571CEF30EF007CBC45 /* ObjectMapper.playground */ = {isa = PBXFileReference; lastKnownFileType = file.playground; name = ObjectMapper.playground; path = "Custom mappers/ObjectMapper.playground"; sourceTree = "<group>"; };
635637
9A6AD3711CE5D5E4007AD9B5 /* UploadTestCase.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = UploadTestCase.swift; path = Tests/UploadTestCase.swift; sourceTree = "<group>"; };
636638
9A704E4F1C5D01B300392DE5 /* Example.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Example.app; sourceTree = BUILT_PRODUCTS_DIR; };
637639
9A704E511C5D01B300392DE5 /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = "<group>"; };
@@ -665,8 +667,6 @@
665667
9AEE0B951C5A4E09003222A8 /* Alamofire.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = Alamofire.xcodeproj; path = Carthage/Checkouts/Alamofire/Alamofire.xcodeproj; sourceTree = "<group>"; };
666668
9AEE0BAE1C5A4E2D003222A8 /* SwiftyJSON.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = SwiftyJSON.xcodeproj; path = Carthage/Checkouts/SwiftyJSON/SwiftyJSON.xcodeproj; sourceTree = "<group>"; };
667669
9AEE0BC71C5A4FB5003222A8 /* Nimble.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = Nimble.xcodeproj; path = Carthage/Checkouts/Nimble/Nimble.xcodeproj; sourceTree = "<group>"; };
668-
9AFAB7C91C6F536A008C74D7 /* Argo.playground */ = {isa = PBXFileReference; lastKnownFileType = file.playground; name = Argo.playground; path = "Custom mappers/Argo.playground"; sourceTree = "<group>"; };
669-
9AFAB7CA1C6F5410008C74D7 /* ObjectMapper.playground */ = {isa = PBXFileReference; lastKnownFileType = file.playground; name = ObjectMapper.playground; path = "Custom mappers/ObjectMapper.playground"; sourceTree = "<group>"; };
670670
9AFAE4D41CC5210F002739B0 /* CHANGELOG.md */ = {isa = PBXFileReference; lastKnownFileType = net.daringfireball.markdown; path = CHANGELOG.md; sourceTree = "<group>"; };
671671
9AFAE4D51CC52118002739B0 /* README.md */ = {isa = PBXFileReference; lastKnownFileType = net.daringfireball.markdown; path = README.md; sourceTree = "<group>"; };
672672
9AFAE4D61CC52367002739B0 /* Rx.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = Rx.xcodeproj; path = Carthage/Checkouts/RxSwift/Rx.xcodeproj; sourceTree = "<group>"; };
@@ -790,8 +790,8 @@
790790
isa = PBXGroup;
791791
children = (
792792
9AFAE4D31CC520F6002739B0 /* Documentation */,
793-
9AFAB7CA1C6F5410008C74D7 /* ObjectMapper.playground */,
794-
9AFAB7C91C6F536A008C74D7 /* Argo.playground */,
793+
9A6693571CEF30EF007CBC45 /* ObjectMapper.playground */,
794+
9A6693561CEF2A7C007CBC45 /* Unbox.playground */,
795795
9AEE0BC71C5A4FB5003222A8 /* Nimble.xcodeproj */,
796796
9AEE0BAE1C5A4E2D003222A8 /* SwiftyJSON.xcodeproj */,
797797
9AFAE4D61CC52367002739B0 /* Rx.xcodeproj */,

0 commit comments

Comments
 (0)