|
1 | 1 | # TRON 1.0 Migration Guide |
| 2 | + |
| 3 | +## Wrapping Alamofire |
| 4 | + |
| 5 | +Starting from the beginning, `TRON` was a wrapper around [Alamofire](https://github.com/Alamofire/Alamofire). In 0.x releases we tried to hide this fact behind our own constructs and API facade. Basically, we took similar approach to [Moya](https://github.com/Moya/Moya) framework. And while usually completely hiding implementation details from the client is a good idea, we no longer feel it's viable in this current case. |
| 6 | + |
| 7 | +The benefits of facade approach is understandable - you need to only study one framework instead of two. However facade leads to several problems, here are some of them: |
| 8 | + |
| 9 | +#### Code duplication |
| 10 | + |
| 11 | +To completely hide implementation details from the client, you need to provide your own abstractions, that are basically copies of internal framework, for example, here's [some code from Moya](https://github.com/Moya/Moya/blob/72251f8910071568c626a61ad587367a1afb1e49/Source/Moya.swift#L8-L10). HTTP methods that we all know, are basically a duplicated enum from Alamofire, that can be easily removed. |
| 12 | + |
| 13 | +#### Incomplete functionality |
| 14 | + |
| 15 | +API facade is somewhat limited in terms of functionality. It's hard to wrap every single piece of Alamofire. And there's functionality, for example multipart upload, that heavily uses Alamofire data structures and therefore is very hard to implement in API facade. This is probably one of the reasons multipart upload takes a **year** [to implement](https://github.com/Moya/Moya/issues/114) in Moya. And when Alamofire introduces new feature, it takes even longer to port it to a wrapper. |
| 16 | + |
| 17 | +#### Hard customization |
| 18 | + |
| 19 | +Using facade sometimes locks you into artificial barriers that are created only to prevent you from knowing that you are using Alamofire. For example, there's a feature on Alamofire.Request, that allows you to print it's cURL to console using debugDescription variable. However, if used through API facade, you no longer know that you are using Alamofire.Request, then how do you allow using such feature without major headaches? |
| 20 | + |
| 21 | +### Our solution |
| 22 | + |
| 23 | +Starting with 1.0 release, we no longer hide Alamofire.Request from developer. So now, you can do this: |
| 24 | + |
| 25 | +```swift |
| 26 | +request.perform(success: { result in })?.responseString { string in |
| 27 | + print(string) |
| 28 | +}.progress { bytesWritten, totalBytesWritten, totalBytesExpectedToWrite in |
| 29 | + print(bytesWritten, totalBytesWritten, totalBytesExpectedToWrite) |
| 30 | +} |
| 31 | +``` |
| 32 | + |
| 33 | +`RequestToken` protocol is removed, all perform request method return Alamofire.Request?, which will be nil in case request was stubbed. |
| 34 | + |
| 35 | +Previously, progress closure was included into `MultipartAPIRequest` `perform` method. Now, since it's available on returned Alamofire.Request property, method is reworked: |
| 36 | + |
| 37 | +``` |
| 38 | +// Old: |
| 39 | +let request : MultipartAPIRequest<User,MyAppError> = tron.multipartRequest(path: "profile") |
| 40 | +multipartRequest.appendMultipartData(data, name: "avatar", filename: "avatar.jpg", mimeType: "image/jpeg") |
| 41 | +request.performWithSuccess({ user in |
| 42 | + print("user avatar updated!") |
| 43 | + }, |
| 44 | + failure: { error in |
| 45 | + print("failed to upload user avatar") |
| 46 | + }, |
| 47 | + progress: { progress in |
| 48 | + print("Picture is uploading, progress: \(CGFloat(progress.totalBytesWritten) / CGFloat(progress.totalBytesExpectedToWrite))") |
| 49 | + }) |
| 50 | + |
| 51 | +// New: |
| 52 | +let request: MultipartAPIRequest<User,MyAppError> = tron.uploadMultipart(path: "profile") { formData in |
| 53 | + formData.appendBodyPart(data: data,name: "avatar", mimeType: "image/jpeg") |
| 54 | +} |
| 55 | +request.performMultipart(success: { user in |
| 56 | + print("user avatar updated!") |
| 57 | + }, |
| 58 | + failure: { error in |
| 59 | + print("failed to upload user avatar") |
| 60 | + }, encodingCompletion: { completion in |
| 61 | + if case let Manager.MultipartFormDataEncodingResult.Success(request,_,_) = completion { |
| 62 | + request.progress { _, totalBytesWritten, totalBytesExpectedToWrite in |
| 63 | + print("Picture is uploading, progress: \(CGFloat(totalBytesWritten) / CGFloat(totalBytesExpectedToWrite))") |
| 64 | + } |
| 65 | + } |
| 66 | +}) |
| 67 | +``` |
| 68 | + |
| 69 | +## New features |
| 70 | + |
| 71 | +We are committed to providing all features Alamofire has to the framework user, and wrapping them into our convenience structures. 1.0 release has full support for download and upload tasks: |
| 72 | + |
| 73 | +#### Upload |
| 74 | + |
| 75 | +From file: |
| 76 | + |
| 77 | +* From file: |
| 78 | + |
| 79 | +```swift |
| 80 | +let request = tron.upload(path: "photo", file: fileUrl) |
| 81 | +``` |
| 82 | + |
| 83 | +* NSData: |
| 84 | + |
| 85 | +```swift |
| 86 | +let request = tron.upload(path: "photo", data: data) |
| 87 | +``` |
| 88 | + |
| 89 | +* Stream: |
| 90 | + |
| 91 | +```swift |
| 92 | +let request = tron.upload(path: "photo", stream: stream) |
| 93 | +``` |
| 94 | + |
| 95 | +#### Download |
| 96 | + |
| 97 | +```swift |
| 98 | +let request = tron.download(path: "file", destination: destination) |
| 99 | +``` |
| 100 | + |
| 101 | +Resume downloads: |
| 102 | + |
| 103 | +```swift |
| 104 | +let request = tron.download(path: "file", destination: destination, resumingFromData: data) |
| 105 | +``` |
| 106 | + |
| 107 | +#### Performing uploads and downloads |
| 108 | + |
| 109 | +All downloads and uploads(except for multipart) use the same `APIRequest` method: |
| 110 | + |
| 111 | +```swift |
| 112 | +request.perform(success: { result in }, failure: { error in }) |
| 113 | +``` |
| 114 | + |
| 115 | +Old `performWithSuccess(_:failure:)` method is deprecated and will be removed in future releases. |
| 116 | + |
| 117 | +#### Perform with completion block |
| 118 | + |
| 119 | +Since we no longer hide Alamofire from developer, we are now able to provide perform method with Alamofire completion block, which can be used in several useful ways, for example observing request timeline: |
| 120 | + |
| 121 | +```swift |
| 122 | +request.perform(completion: { response in |
| 123 | + print(response.result) // Alamofire.Result |
| 124 | + print(response.timeline) // Alamofire.Timeline |
| 125 | + print(response.response) // NSHTTPURLResponse? |
| 126 | +}) |
| 127 | +``` |
| 128 | + |
| 129 | +### Miscellaneous API improvements |
| 130 | + |
| 131 | +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. |
| 132 | + |
| 133 | +`Progress` and `ProgressClosure` typealiases are removed, since `TRON` framework no longer handles any progress closures. |
| 134 | + |
| 135 | +`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. |
| 136 | + |
| 137 | +`RxSwift` extension `rxMultipartResult()` method on `MultipartAPIRequest` now returns Observable<ModelType> instead of tuple of observables. |
0 commit comments