123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041 |
- //
- // RxAlamofire.swift
- // RxAlamofire
- //
- // Created by Junior B. (@bontojr) on 23/08/15.
- // Developed with the kind help of Krunoslav Zaher (@KrunoslavZaher)
- //
- // Updated by Ivan Đikić for the latest version of Alamofire(3) and RxSwift(2) on 21/10/15
- // Updated by Krunoslav Zaher to better wrap Alamofire (3) on 1/10/15
- //
- // Copyright © 2015 Bonto.ch. All rights reserved.
- //
- import Foundation
- import Alamofire
- import RxSwift
- /// Default instance of unknown error
- public let RxAlamofireUnknownError = NSError(domain: "RxAlamofireDomain", code: -1, userInfo: nil)
- // MARK: Convenience functions
- /**
- Creates a NSMutableURLRequest using all necessary parameters.
- - parameter method: Alamofire method object
- - parameter url: An object adopting `URLConvertible`
- - parameter parameters: A dictionary containing all necessary options
- - parameter encoding: The kind of encoding used to process parameters
- - parameter header: A dictionary containing all the additional headers
- - returns: An instance of `NSMutableURLRequest`
- */
- public func urlRequest(_ method: Alamofire.HTTPMethod,
- _ url: URLConvertible,
- parameters: [String: Any]? = nil,
- encoding: ParameterEncoding = URLEncoding.default,
- headers: [String: String]? = nil)
- throws -> Foundation.URLRequest
- {
- var mutableURLRequest = Foundation.URLRequest(url: try url.asURL())
- mutableURLRequest.httpMethod = method.rawValue
- if let headers = headers {
- for (headerField, headerValue) in headers {
- mutableURLRequest.setValue(headerValue, forHTTPHeaderField: headerField)
- }
- }
- if let parameters = parameters {
- mutableURLRequest = try encoding.encode(mutableURLRequest, with: parameters)
- }
- return mutableURLRequest
- }
- // MARK: Request
- /**
- Creates an observable of the generated `Request`.
- - parameter method: Alamofire method object
- - parameter url: An object adopting `URLConvertible`
- - parameter parameters: A dictionary containing all necessary options
- - parameter encoding: The kind of encoding used to process parameters
- - parameter header: A dictionary containing all the additional headers
- - returns: An observable of a the `Request`
- */
- public func request(_ method: Alamofire.HTTPMethod,
- _ url: URLConvertible,
- parameters: [String: Any]? = nil,
- encoding: ParameterEncoding = URLEncoding.default,
- headers: [String: String]? = nil)
- -> Observable<DataRequest>
- {
- return SessionManager.default.rx.request(
- method,
- url,
- parameters: parameters,
- encoding: encoding,
- headers: headers
- )
- }
- /**
- Creates an observable of the generated `Request`.
- - parameter urlRequest: An object adopting `URLRequestConvertible`
- - returns: An observable of a the `Request`
- */
- public func request(_ urlRequest: URLRequestConvertible) -> Observable<DataRequest> {
- return SessionManager.default.rx.request(urlRequest: urlRequest)
- }
- // MARK: data
- /**
- Creates an observable of the `(NSHTTPURLResponse, NSData)` instance.
- - parameter method: Alamofire method object
- - parameter url: An object adopting `URLConvertible`
- - parameter parameters: A dictionary containing all necessary options
- - parameter encoding: The kind of encoding used to process parameters
- - parameter header: A dictionary containing all the additional headers
- - returns: An observable of a tuple containing `(NSHTTPURLResponse, NSData)`
- */
- public func requestData(_ method: Alamofire.HTTPMethod,
- _ url: URLConvertible,
- parameters: [String: Any]? = nil,
- encoding: ParameterEncoding = URLEncoding.default,
- headers: [String: String]? = nil)
- -> Observable<(HTTPURLResponse, Data)>
- {
- return SessionManager.default.rx.responseData(
- method,
- url,
- parameters: parameters,
- encoding: encoding,
- headers: headers
- )
- }
- /**
- Creates an observable of the `(NSHTTPURLResponse, NSData)` instance.
- - parameter urlRequest: An object adopting `URLRequestConvertible`
- - returns: An observable of a tuple containing `(NSHTTPURLResponse, NSData)`
- */
- public func requestData(_ urlRequest: URLRequestConvertible) -> Observable<(HTTPURLResponse, Data)> {
- return request(urlRequest).flatMap { $0.rx.responseData() }
- }
- /**
- Creates an observable of the returned data.
- - parameter method: Alamofire method object
- - parameter url: An object adopting `URLConvertible`
- - parameter parameters: A dictionary containing all necessary options
- - parameter encoding: The kind of encoding used to process parameters
- - parameter header: A dictionary containing all the additional headers
- - returns: An observable of `NSData`
- */
- public func data(_ method: Alamofire.HTTPMethod,
- _ url: URLConvertible,
- parameters: [String: Any]? = nil,
- encoding: ParameterEncoding = URLEncoding.default,
- headers: [String: String]? = nil)
- -> Observable<Data>
- {
- return SessionManager.default.rx.data(
- method,
- url,
- parameters: parameters,
- encoding: encoding,
- headers: headers
- )
- }
- // MARK: string
- /**
- Creates an observable of the returned decoded string and response.
- - parameter method: Alamofire method object
- - parameter url: An object adopting `URLConvertible`
- - parameter parameters: A dictionary containing all necessary options
- - parameter encoding: The kind of encoding used to process parameters
- - parameter header: A dictionary containing all the additional headers
- - returns: An observable of the tuple `(NSHTTPURLResponse, String)`
- */
- public func requestString(_ method: Alamofire.HTTPMethod,
- _ url: URLConvertible,
- parameters: [String: Any]? = nil,
- encoding: ParameterEncoding = URLEncoding.default,
- headers: [String: String]? = nil)
- -> Observable<(HTTPURLResponse, String)>
- {
- return SessionManager.default.rx.responseString(
- method,
- url,
- parameters: parameters,
- encoding: encoding,
- headers: headers
- )
- }
- /**
- Creates an observable of the returned decoded string and response.
- - parameter urlRequest: An object adopting `URLRequestConvertible`
- - returns: An observable of the tuple `(NSHTTPURLResponse, String)`
- */
- public func requestString(_ urlRequest: URLRequestConvertible) -> Observable<(HTTPURLResponse, String)> {
- return request(urlRequest).flatMap { $0.rx.responseString() }
- }
- /**
- Creates an observable of the returned decoded string.
- - parameter method: Alamofire method object
- - parameter url: An object adopting `URLConvertible`
- - parameter parameters: A dictionary containing all necessary options
- - parameter encoding: The kind of encoding used to process parameters
- - parameter header: A dictionary containing all the additional headers
- - returns: An observable of `String`
- */
- public func string(_ method: Alamofire.HTTPMethod,
- _ url: URLConvertible,
- parameters: [String: Any]? = nil,
- encoding: ParameterEncoding = URLEncoding.default,
- headers: [String: String]? = nil)
- -> Observable<String>
- {
- return SessionManager.default.rx.string(
- method,
- url,
- parameters: parameters,
- encoding: encoding,
- headers: headers
- )
- }
- // MARK: JSON
- /**
- Creates an observable of the returned decoded JSON as `AnyObject` and the response.
- - parameter method: Alamofire method object
- - parameter url: An object adopting `URLConvertible`
- - parameter parameters: A dictionary containing all necessary options
- - parameter encoding: The kind of encoding used to process parameters
- - parameter header: A dictionary containing all the additional headers
- - returns: An observable of the tuple `(NSHTTPURLResponse, AnyObject)`
- */
- public func requestJSON(_ method: Alamofire.HTTPMethod,
- _ url: URLConvertible,
- parameters: [String: Any]? = nil,
- encoding: ParameterEncoding = URLEncoding.default,
- headers: [String: String]? = nil)
- -> Observable<(HTTPURLResponse, Any)>
- {
- return SessionManager.default.rx.responseJSON(
- method,
- url,
- parameters: parameters,
- encoding: encoding,
- headers: headers
- )
- }
- /**
- Creates an observable of the returned decoded JSON as `AnyObject` and the response.
- - parameter urlRequest: An object adopting `URLRequestConvertible`
- - returns: An observable of the tuple `(NSHTTPURLResponse, AnyObject)`
- */
- public func requestJSON(_ urlRequest: URLRequestConvertible) -> Observable<(HTTPURLResponse, Any)> {
- return request(urlRequest).flatMap { $0.rx.responseJSON() }
- }
- /**
- Creates an observable of the returned decoded JSON.
- - parameter method: Alamofire method object
- - parameter url: An object adopting `URLConvertible`
- - parameter parameters: A dictionary containing all necessary options
- - parameter encoding: The kind of encoding used to process parameters
- - parameter header: A dictionary containing all the additional headers
- - returns: An observable of the decoded JSON as `Any`
- */
- public func json(_ method: Alamofire.HTTPMethod,
- _ url: URLConvertible,
- parameters: [String: Any]? = nil,
- encoding: ParameterEncoding = URLEncoding.default,
- headers: [String: String]? = nil)
- -> Observable<Any>
- {
- return SessionManager.default.rx.json(
- method,
- url,
- parameters: parameters,
- encoding: encoding,
- headers: headers
- )
- }
- // MARK: Upload
- /**
- Returns an observable of a request using the shared manager instance to upload a specific file to a specified URL.
- The request is started immediately.
- - parameter urlRequest: The request object to start the upload.
- - paramenter file: An instance of NSURL holding the information of the local file.
- - returns: The observable of `UploadRequest` for the created request.
- */
- public func upload(_ file: URL, urlRequest: URLRequestConvertible) -> Observable<UploadRequest> {
- return SessionManager.default.rx.upload(file, urlRequest: urlRequest)
- }
- /**
- Returns an observable of a request using the shared manager instance to upload any data to a specified URL.
- The request is started immediately.
- - parameter urlRequest: The request object to start the upload.
- - paramenter data: An instance of NSData holdint the data to upload.
- - returns: The observable of `UploadRequest` for the created request.
- */
- public func upload(_ data: Data, urlRequest: URLRequestConvertible) -> Observable<UploadRequest> {
- return SessionManager.default.rx.upload(data , urlRequest: urlRequest)
- }
- /**
- Returns an observable of a request using the shared manager instance to upload any stream to a specified URL.
- The request is started immediately.
- - parameter urlRequest: The request object to start the upload.
- - paramenter stream: The stream to upload.
- - returns: The observable of `Request` for the created upload request.
- */
- public func upload(_ stream: InputStream, urlRequest: URLRequestConvertible) -> Observable<UploadRequest> {
- return SessionManager.default.rx.upload(stream, urlRequest: urlRequest)
- }
- // MARK: Download
- /**
- Creates a download request using the shared manager instance for the specified URL request.
- - parameter urlRequest: The URL request.
- - parameter destination: The closure used to determine the destination of the downloaded file.
- - returns: The observable of `DownloadRequest` for the created download request.
- */
- public func download(_ urlRequest: URLRequestConvertible,
- to destination: @escaping DownloadRequest.DownloadFileDestination) -> Observable<DownloadRequest> {
- return SessionManager.default.rx.download(urlRequest, to: destination)
- }
- // MARK: Resume Data
- /**
- Creates a request using the shared manager instance for downloading from the resume data produced from a
- previous request cancellation.
- - parameter resumeData: The resume data. This is an opaque data blob produced by `NSURLSessionDownloadTask`
- when a task is cancelled. See `NSURLSession -downloadTaskWithResumeData:` for additional
- information.
- - parameter destination: The closure used to determine the destination of the downloaded file.
- - returns: The observable of `Request` for the created download request.
- */
- public func download(resumeData: Data,
- to destination: @escaping DownloadRequest.DownloadFileDestination) -> Observable<DownloadRequest> {
- return SessionManager.default.rx.download(resumeData: resumeData, to: destination)
- }
- // MARK: Manager - Extension of Manager
- extension SessionManager: ReactiveCompatible {
- }
- protocol RxAlamofireRequest {
- func responseWith(completionHandler: @escaping (RxAlamofireResponse) -> Void)
- func resume()
- func cancel()
- }
- protocol RxAlamofireResponse {
- var error: Error? {get}
- }
- extension DefaultDataResponse: RxAlamofireResponse {}
- extension DefaultDownloadResponse: RxAlamofireResponse {}
- extension DataRequest: RxAlamofireRequest {
- func responseWith(completionHandler: @escaping (RxAlamofireResponse) -> Void) {
- response { (response) in
- completionHandler(response)
- }
- }
- }
- extension DownloadRequest: RxAlamofireRequest {
- func responseWith(completionHandler: @escaping (RxAlamofireResponse) -> Void) {
- response { (response) in
- completionHandler(response)
- }
- }
- }
- extension Reactive where Base: SessionManager {
- // MARK: Generic request convenience
- /**
- Creates an observable of the DataRequest.
- - parameter createRequest: A function used to create a `Request` using a `Manager`
- - returns: A generic observable of created data request
- */
- func request<R: RxAlamofireRequest>(_ createRequest: @escaping (SessionManager) throws -> R) -> Observable<R> {
- return Observable.create { observer -> Disposable in
- let request: R
- do {
- request = try createRequest(self.base)
- observer.on(.next(request))
- request.responseWith(completionHandler: { (response) in
- if let error = response.error {
- observer.on(.error(error))
- } else {
- observer.on(.completed)
- }
- })
- if !self.base.startRequestsImmediately {
- request.resume()
- }
- return Disposables.create {
- request.cancel()
- }
- }
- catch let error {
- observer.on(.error(error))
- return Disposables.create()
- }
- }
- }
- /**
- Creates an observable of the `Request`.
- - parameter method: Alamofire method object
- - parameter url: An object adopting `URLConvertible`
- - parameter parameters: A dictionary containing all necessary options
- - parameter encoding: The kind of encoding used to process parameters
- - parameter header: A dictionary containing all the additional headers
- - returns: An observable of the `Request`
- */
- public func request(_ method: Alamofire.HTTPMethod,
- _ url: URLConvertible,
- parameters: [String: Any]? = nil,
- encoding: ParameterEncoding = URLEncoding.default,
- headers: [String: String]? = nil
- )
- -> Observable<DataRequest>
- {
- return request { manager in
- return manager.request(url,
- method: method,
- parameters: parameters,
- encoding: encoding,
- headers: headers)
- }
- }
- /**
- Creates an observable of the `Request`.
- - parameter URLRequest: An object adopting `URLRequestConvertible`
- - parameter parameters: A dictionary containing all necessary options
- - parameter encoding: The kind of encoding used to process parameters
- - parameter header: A dictionary containing all the additional headers
- - returns: An observable of the `Request`
- */
- public func request(urlRequest: URLRequestConvertible)
- -> Observable<DataRequest>
- {
- return request { manager in
- return manager.request(urlRequest)
- }
- }
- // MARK: data
- /**
- Creates an observable of the data.
- - parameter url: An object adopting `URLConvertible`
- - parameter parameters: A dictionary containing all necessary options
- - parameter encoding: The kind of encoding used to process parameters
- - parameter header: A dictionary containing all the additional headers
- - returns: An observable of the tuple `(NSHTTPURLResponse, NSData)`
- */
- public func responseData(_ method: Alamofire.HTTPMethod,
- _ url: URLConvertible,
- parameters: [String: Any]? = nil,
- encoding: ParameterEncoding = URLEncoding.default,
- headers: [String: String]? = nil
- )
- -> Observable<(HTTPURLResponse, Data)>
- {
- return request(
- method,
- url,
- parameters: parameters,
- encoding: encoding,
- headers: headers
- ).flatMap { $0.rx.responseData() }
- }
- /**
- Creates an observable of the data.
- - parameter URLRequest: An object adopting `URLRequestConvertible`
- - parameter parameters: A dictionary containing all necessary options
- - parameter encoding: The kind of encoding used to process parameters
- - parameter header: A dictionary containing all the additional headers
- - returns: An observable of `NSData`
- */
- public func data(_ method: Alamofire.HTTPMethod,
- _ url: URLConvertible,
- parameters: [String: Any]? = nil,
- encoding: ParameterEncoding = URLEncoding.default,
- headers: [String: String]? = nil
- )
- -> Observable<Data>
- {
- return request(
- method,
- url,
- parameters: parameters,
- encoding: encoding,
- headers: headers
- ).flatMap { $0.rx.data() }
- }
- // MARK: string
- /**
- Creates an observable of the tuple `(NSHTTPURLResponse, String)`.
- - parameter url: An object adopting `URLRequestConvertible`
- - parameter parameters: A dictionary containing all necessary options
- - parameter encoding: The kind of encoding used to process parameters
- - parameter header: A dictionary containing all the additional headers
- - returns: An observable of the tuple `(NSHTTPURLResponse, String)`
- */
- public func responseString(_ method: Alamofire.HTTPMethod,
- _ url: URLConvertible,
- parameters: [String: Any]? = nil,
- encoding: ParameterEncoding = URLEncoding.default,
- headers: [String: String]? = nil
- )
- -> Observable<(HTTPURLResponse, String)>
- {
- return request(
- method,
- url,
- parameters: parameters,
- encoding: encoding,
- headers: headers
- ).flatMap { $0.rx.responseString() }
- }
- /**
- Creates an observable of the data encoded as String.
- - parameter url: An object adopting `URLConvertible`
- - parameter parameters: A dictionary containing all necessary options
- - parameter encoding: The kind of encoding used to process parameters
- - parameter header: A dictionary containing all the additional headers
- - returns: An observable of `String`
- */
- public func string(_ method: Alamofire.HTTPMethod,
- _ url: URLConvertible,
- parameters: [String: Any]? = nil,
- encoding: ParameterEncoding = URLEncoding.default,
- headers: [String: String]? = nil
- )
- -> Observable<String>
- {
- return request(
- method,
- url,
- parameters: parameters,
- encoding: encoding,
- headers: headers
- )
- .flatMap { (request) -> Observable<String> in
- return request.rx.string()
- }
- }
- // MARK: JSON
- /**
- Creates an observable of the data decoded from JSON and processed as tuple `(NSHTTPURLResponse, AnyObject)`.
- - parameter url: An object adopting `URLRequestConvertible`
- - parameter parameters: A dictionary containing all necessary options
- - parameter encoding: The kind of encoding used to process parameters
- - parameter header: A dictionary containing all the additional headers
- - returns: An observable of the tuple `(NSHTTPURLResponse, AnyObject)`
- */
- public func responseJSON(_ method: Alamofire.HTTPMethod,
- _ url: URLConvertible,
- parameters: [String: Any]? = nil,
- encoding: ParameterEncoding = URLEncoding.default,
- headers: [String: String]? = nil
- )
- -> Observable<(HTTPURLResponse, Any)>
- {
- return request(method,
- url,
- parameters: parameters,
- encoding: encoding,
- headers: headers
- ).flatMap { $0.rx.responseJSON() }
- }
- /**
- Creates an observable of the data decoded from JSON and processed as `AnyObject`.
- - parameter URLRequest: An object adopting `URLRequestConvertible`
- - parameter parameters: A dictionary containing all necessary options
- - parameter encoding: The kind of encoding used to process parameters
- - parameter header: A dictionary containing all the additional headers
- - returns: An observable of `AnyObject`
- */
- public func json(_ method: Alamofire.HTTPMethod,
- _ url: URLConvertible,
- parameters: [String: Any]? = nil,
- encoding: ParameterEncoding = URLEncoding.default,
- headers: [String: String]? = nil
- )
- -> Observable<Any>
- {
- return request(
- method,
- url,
- parameters: parameters,
- encoding: encoding,
- headers: headers
- ).flatMap { $0.rx.json() }
- }
- // MARK: Upload
- /**
- Returns an observable of a request using the shared manager instance to upload a specific file to a specified URL.
- The request is started immediately.
- - parameter urlRequest: The request object to start the upload.
- - paramenter file: An instance of NSURL holding the information of the local file.
- - returns: The observable of `AnyObject` for the created request.
- */
- public func upload(_ file: URL, urlRequest: URLRequestConvertible) -> Observable<UploadRequest> {
- return request { manager in
- return manager.upload(file, with: urlRequest)
- }
- }
- /**
- Returns an observable of a request using the shared manager instance to upload any data to a specified URL.
- The request is started immediately.
- - parameter urlRequest: The request object to start the upload.
- - paramenter data: An instance of Data holdint the data to upload.
- - returns: The observable of `UploadRequest` for the created request.
- */
- public func upload(_ data: Data, urlRequest: URLRequestConvertible) -> Observable<UploadRequest> {
- return request { manager in
- return manager.upload(data, with: urlRequest)
- }
- }
- /**
- Returns an observable of a request using the shared manager instance to upload any stream to a specified URL.
- The request is started immediately.
- - parameter urlRequest: The request object to start the upload.
- - paramenter stream: The stream to upload.
- - returns: The observable of `(NSData?, RxProgress)` for the created upload request.
- */
- public func upload(_ stream: InputStream,
- urlRequest: URLRequestConvertible) -> Observable<UploadRequest> {
- return request { manager in
- return manager.upload(stream, with: urlRequest)
- }
- }
- // MARK: Download
- /**
- Creates a download request using the shared manager instance for the specified URL request.
- - parameter urlRequest: The URL request.
- - parameter destination: The closure used to determine the destination of the downloaded file.
- - returns: The observable of `(NSData?, RxProgress)` for the created download request.
- */
- public func download(_ urlRequest: URLRequestConvertible,
- to destination: @escaping DownloadRequest.DownloadFileDestination) -> Observable<DownloadRequest> {
- return request { manager in
- return manager.download(urlRequest, to: destination)
- }
- }
- /**
- Creates a request using the shared manager instance for downloading with a resume data produced from a
- previous request cancellation.
- - parameter resumeData: The resume data. This is an opaque data blob produced by `NSURLSessionDownloadTask`
- when a task is cancelled. See `NSURLSession -downloadTaskWithResumeData:` for additional
- information.
- - parameter destination: The closure used to determine the destination of the downloaded file.
- - returns: The observable of `(NSData?, RxProgress)` for the created download request.
- */
- public func download(resumeData: Data,
- to destination: @escaping DownloadRequest.DownloadFileDestination) -> Observable<DownloadRequest> {
- return request { manager in
- return manager.download(resumingWith: resumeData, to: destination)
- }
- }
- }
- // MARK: Request - Common Response Handlers
- extension ObservableType where E == DataRequest {
- public func responseJSON() -> Observable<DataResponse<Any>> {
- return flatMap { $0.rx.responseJSON() }
- }
-
- public func json(options: JSONSerialization.ReadingOptions = .allowFragments) -> Observable<Any> {
- return flatMap { $0.rx.json(options: options) }
- }
-
- public func responseString(encoding: String.Encoding? = nil) -> Observable<(HTTPURLResponse, String)> {
- return flatMap { $0.rx.responseString(encoding: encoding) }
- }
-
- public func string(encoding: String.Encoding? = nil) -> Observable<String> {
- return flatMap { $0.rx.string(encoding: encoding) }
- }
-
- public func responseData() -> Observable<(HTTPURLResponse, Data)> {
- return flatMap { $0.rx.responseData() }
- }
-
- public func data() -> Observable<Data> {
- return flatMap { $0.rx.data() }
- }
-
- public func responsePropertyList(options: PropertyListSerialization.ReadOptions = PropertyListSerialization.ReadOptions()) -> Observable<(HTTPURLResponse, Any)> {
- return flatMap { $0.rx.responsePropertyList(options: options) }
- }
-
- public func propertyList(options: PropertyListSerialization.ReadOptions = PropertyListSerialization.ReadOptions()) -> Observable<Any> {
- return flatMap { $0.rx.propertyList(options: options) }
- }
-
- public func progress() -> Observable<RxProgress> {
- return flatMap { $0.rx.progress() }
- }
- }
- // MARK: Request - Validation
- extension ObservableType where E == DataRequest {
- public func validate<S: Sequence>(statusCode: S) -> Observable<E> where S.Element == Int {
- return map { $0.validate(statusCode: statusCode) }
- }
-
- public func validate() -> Observable<E> {
- return map { $0.validate() }
- }
-
- public func validate<S: Sequence>(contentType acceptableContentTypes: S) -> Observable<E> where S.Iterator.Element == String {
- return map { $0.validate(contentType: acceptableContentTypes) }
- }
-
- public func validate(_ validation: @escaping DataRequest.Validation) -> Observable<E> {
- return map { $0.validate(validation) }
- }
- }
- extension Request: ReactiveCompatible {
- }
- extension Reactive where Base: DataRequest {
- // MARK: Defaults
- /// - returns: A validated request based on the status code
- func validateSuccessfulResponse() -> DataRequest {
- return self.base.validate(statusCode: 200 ..< 300)
- }
- /**
- Transform a request into an observable of the response and serialized object.
- - parameter queue: The dispatch queue to use.
- - parameter responseSerializer: The the serializer.
- - returns: The observable of `(NSHTTPURLResponse, T.SerializedObject)` for the created download request.
- */
- public func responseResult<T: DataResponseSerializerProtocol>(queue: DispatchQueue? = nil,
- responseSerializer: T)
- -> Observable<(HTTPURLResponse, T.SerializedObject)>
- {
- return Observable.create { observer in
- let dataRequest = self.base
- .response(queue: queue, responseSerializer: responseSerializer) { (packedResponse) -> Void in
- switch packedResponse.result {
- case .success(let result):
- if let httpResponse = packedResponse.response {
- observer.on(.next((httpResponse, result)))
- observer.on(.completed)
- }
- else {
- observer.on(.error(RxAlamofireUnknownError))
- }
- case .failure(let error):
- observer.on(.error(error as Error))
- }
- }
- return Disposables.create {
- dataRequest.cancel()
- }
- }
- }
- public func responseJSON() -> Observable<DataResponse<Any>> {
- return Observable.create { observer in
- let request = self.base
- request.responseJSON { response in
- if let error = response.result.error {
- observer.on(.error(error))
- } else {
- observer.on(.next(response))
- observer.on(.completed)
- }
- }
- return Disposables.create {
- request.cancel()
- }
- }
- }
- /**
- Transform a request into an observable of the serialized object.
- - parameter queue: The dispatch queue to use.
- - parameter responseSerializer: The the serializer.
- - returns: The observable of `T.SerializedObject` for the created download request.
- */
- public func result<T: DataResponseSerializerProtocol>(
- queue: DispatchQueue? = nil,
- responseSerializer: T)
- -> Observable<T.SerializedObject>
- {
- return Observable.create { observer in
- let dataRequest = self.validateSuccessfulResponse()
- .response(queue: queue, responseSerializer: responseSerializer) { (packedResponse) -> Void in
- switch packedResponse.result {
- case .success(let result):
- if let _ = packedResponse.response {
- observer.on(.next(result))
- observer.on(.completed)
- }
- else {
- observer.on(.error(RxAlamofireUnknownError))
- }
- case .failure(let error):
- observer.on(.error(error as Error))
- }
- }
- return Disposables.create {
- dataRequest.cancel()
- }
- }
- }
- /**
- Returns an `Observable` of NSData for the current request.
- - parameter cancelOnDispose: Indicates if the request has to be canceled when the observer is disposed, **default:** `false`
- - returns: An instance of `Observable<NSData>`
- */
- public func responseData() -> Observable<(HTTPURLResponse, Data)> {
- return responseResult(responseSerializer: DataRequest.dataResponseSerializer())
- }
- public func data() -> Observable<Data> {
- return result(responseSerializer: DataRequest.dataResponseSerializer())
- }
- /**
- Returns an `Observable` of a String for the current request
- - parameter encoding: Type of the string encoding, **default:** `nil`
- - returns: An instance of `Observable<String>`
- */
- public func responseString(encoding: String.Encoding? = nil) -> Observable<(HTTPURLResponse, String)> {
- return responseResult(responseSerializer: Base.stringResponseSerializer(encoding: encoding))
- }
- public func string(encoding: String.Encoding? = nil) -> Observable<String> {
- return result(responseSerializer: Base.stringResponseSerializer(encoding: encoding))
- }
- /**
- Returns an `Observable` of a serialized JSON for the current request.
- - parameter options: Reading options for JSON decoding process, **default:** `.AllowFragments`
- - returns: An instance of `Observable<AnyObject>`
- */
- public func responseJSON(options: JSONSerialization.ReadingOptions = .allowFragments) -> Observable<(HTTPURLResponse, Any)> {
- return responseResult(responseSerializer: Base.jsonResponseSerializer(options: options))
- }
- /**
- Returns an `Observable` of a serialized JSON for the current request.
- - parameter options: Reading options for JSON decoding process, **default:** `.AllowFragments`
- - returns: An instance of `Observable<AnyObject>`
- */
- public func json(options: JSONSerialization.ReadingOptions = .allowFragments) -> Observable<Any> {
- return result(responseSerializer: Base.jsonResponseSerializer(options: options))
- }
- /**
- Returns and `Observable` of a serialized property list for the current request.
- - parameter options: Property list reading options, **default:** `NSPropertyListReadOptions()`
- - returns: An instance of `Observable<AnyData>`
- */
- public func responsePropertyList(options: PropertyListSerialization.ReadOptions = PropertyListSerialization.ReadOptions()) -> Observable<(HTTPURLResponse, Any)> {
- return responseResult(responseSerializer: Base.propertyListResponseSerializer(options: options))
- }
- public func propertyList(options: PropertyListSerialization.ReadOptions = PropertyListSerialization.ReadOptions()) -> Observable<Any> {
- return result(responseSerializer: Base.propertyListResponseSerializer(options: options))
- }
- }
- extension Reactive where Base: Request {
- // MARK: Request - Upload and download progress
- /**
- Returns an `Observable` for the current progress status.
- Parameters on observed tuple:
- 1. bytes written so far.
- 1. total bytes to write.
- - returns: An instance of `Observable<RxProgress>`
- */
- public func progress() -> Observable<RxProgress> {
- return Observable.create { observer in
- let handler: Request.ProgressHandler = { progress in
- let rxProgress = RxProgress(bytesWritten: progress.completedUnitCount,
- totalBytes: progress.totalUnitCount)
- observer.on(.next(rxProgress))
- if rxProgress.bytesWritten >= rxProgress.totalBytes {
- observer.on(.completed)
- }
- }
- // Try in following order:
- // - UploadRequest (Inherits from DataRequest, so we test the discrete case first)
- // - DownloadRequest
- // - DataRequest
- if let uploadReq = self.base as? UploadRequest {
- uploadReq.uploadProgress(closure: handler)
- } else if let downloadReq = self.base as? DownloadRequest {
- downloadReq.downloadProgress(closure: handler)
- } else if let dataReq = self.base as? DataRequest {
- dataReq.downloadProgress(closure: handler)
- }
- return Disposables.create()
- }
- // warm up a bit :)
- .startWith(RxProgress(bytesWritten: 0, totalBytes: 0))
- }
- }
- // MARK: RxProgress
- public struct RxProgress {
- public let bytesWritten: Int64
- public let totalBytes: Int64
- public init(bytesWritten: Int64, totalBytes: Int64) {
- self.bytesWritten = bytesWritten
- self.totalBytes = totalBytes
- }
- }
- extension RxProgress {
- public var bytesRemaining: Int64 {
- return totalBytes - bytesWritten
- }
- public var completed: Float {
- if totalBytes > 0 {
- return Float(bytesWritten) / Float(totalBytes)
- }
- else {
- return 0
- }
- }
- }
- extension RxProgress: Equatable {}
- public func ==(lhs: RxProgress, rhs: RxProgress) -> Bool {
- return lhs.bytesWritten == rhs.bytesWritten &&
- lhs.totalBytes == rhs.totalBytes
- }
|