RxAlamofire.swift 35 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041
  1. //
  2. // RxAlamofire.swift
  3. // RxAlamofire
  4. //
  5. // Created by Junior B. (@bontojr) on 23/08/15.
  6. // Developed with the kind help of Krunoslav Zaher (@KrunoslavZaher)
  7. //
  8. // Updated by Ivan Đikić for the latest version of Alamofire(3) and RxSwift(2) on 21/10/15
  9. // Updated by Krunoslav Zaher to better wrap Alamofire (3) on 1/10/15
  10. //
  11. // Copyright © 2015 Bonto.ch. All rights reserved.
  12. //
  13. import Foundation
  14. import Alamofire
  15. import RxSwift
  16. /// Default instance of unknown error
  17. public let RxAlamofireUnknownError = NSError(domain: "RxAlamofireDomain", code: -1, userInfo: nil)
  18. // MARK: Convenience functions
  19. /**
  20. Creates a NSMutableURLRequest using all necessary parameters.
  21. - parameter method: Alamofire method object
  22. - parameter url: An object adopting `URLConvertible`
  23. - parameter parameters: A dictionary containing all necessary options
  24. - parameter encoding: The kind of encoding used to process parameters
  25. - parameter header: A dictionary containing all the additional headers
  26. - returns: An instance of `NSMutableURLRequest`
  27. */
  28. public func urlRequest(_ method: Alamofire.HTTPMethod,
  29. _ url: URLConvertible,
  30. parameters: [String: Any]? = nil,
  31. encoding: ParameterEncoding = URLEncoding.default,
  32. headers: [String: String]? = nil)
  33. throws -> Foundation.URLRequest
  34. {
  35. var mutableURLRequest = Foundation.URLRequest(url: try url.asURL())
  36. mutableURLRequest.httpMethod = method.rawValue
  37. if let headers = headers {
  38. for (headerField, headerValue) in headers {
  39. mutableURLRequest.setValue(headerValue, forHTTPHeaderField: headerField)
  40. }
  41. }
  42. if let parameters = parameters {
  43. mutableURLRequest = try encoding.encode(mutableURLRequest, with: parameters)
  44. }
  45. return mutableURLRequest
  46. }
  47. // MARK: Request
  48. /**
  49. Creates an observable of the generated `Request`.
  50. - parameter method: Alamofire method object
  51. - parameter url: An object adopting `URLConvertible`
  52. - parameter parameters: A dictionary containing all necessary options
  53. - parameter encoding: The kind of encoding used to process parameters
  54. - parameter header: A dictionary containing all the additional headers
  55. - returns: An observable of a the `Request`
  56. */
  57. public func request(_ method: Alamofire.HTTPMethod,
  58. _ url: URLConvertible,
  59. parameters: [String: Any]? = nil,
  60. encoding: ParameterEncoding = URLEncoding.default,
  61. headers: [String: String]? = nil)
  62. -> Observable<DataRequest>
  63. {
  64. return SessionManager.default.rx.request(
  65. method,
  66. url,
  67. parameters: parameters,
  68. encoding: encoding,
  69. headers: headers
  70. )
  71. }
  72. /**
  73. Creates an observable of the generated `Request`.
  74. - parameter urlRequest: An object adopting `URLRequestConvertible`
  75. - returns: An observable of a the `Request`
  76. */
  77. public func request(_ urlRequest: URLRequestConvertible) -> Observable<DataRequest> {
  78. return SessionManager.default.rx.request(urlRequest: urlRequest)
  79. }
  80. // MARK: data
  81. /**
  82. Creates an observable of the `(NSHTTPURLResponse, NSData)` instance.
  83. - parameter method: Alamofire method object
  84. - parameter url: An object adopting `URLConvertible`
  85. - parameter parameters: A dictionary containing all necessary options
  86. - parameter encoding: The kind of encoding used to process parameters
  87. - parameter header: A dictionary containing all the additional headers
  88. - returns: An observable of a tuple containing `(NSHTTPURLResponse, NSData)`
  89. */
  90. public func requestData(_ method: Alamofire.HTTPMethod,
  91. _ url: URLConvertible,
  92. parameters: [String: Any]? = nil,
  93. encoding: ParameterEncoding = URLEncoding.default,
  94. headers: [String: String]? = nil)
  95. -> Observable<(HTTPURLResponse, Data)>
  96. {
  97. return SessionManager.default.rx.responseData(
  98. method,
  99. url,
  100. parameters: parameters,
  101. encoding: encoding,
  102. headers: headers
  103. )
  104. }
  105. /**
  106. Creates an observable of the `(NSHTTPURLResponse, NSData)` instance.
  107. - parameter urlRequest: An object adopting `URLRequestConvertible`
  108. - returns: An observable of a tuple containing `(NSHTTPURLResponse, NSData)`
  109. */
  110. public func requestData(_ urlRequest: URLRequestConvertible) -> Observable<(HTTPURLResponse, Data)> {
  111. return request(urlRequest).flatMap { $0.rx.responseData() }
  112. }
  113. /**
  114. Creates an observable of the returned data.
  115. - parameter method: Alamofire method object
  116. - parameter url: An object adopting `URLConvertible`
  117. - parameter parameters: A dictionary containing all necessary options
  118. - parameter encoding: The kind of encoding used to process parameters
  119. - parameter header: A dictionary containing all the additional headers
  120. - returns: An observable of `NSData`
  121. */
  122. public func data(_ method: Alamofire.HTTPMethod,
  123. _ url: URLConvertible,
  124. parameters: [String: Any]? = nil,
  125. encoding: ParameterEncoding = URLEncoding.default,
  126. headers: [String: String]? = nil)
  127. -> Observable<Data>
  128. {
  129. return SessionManager.default.rx.data(
  130. method,
  131. url,
  132. parameters: parameters,
  133. encoding: encoding,
  134. headers: headers
  135. )
  136. }
  137. // MARK: string
  138. /**
  139. Creates an observable of the returned decoded string and response.
  140. - parameter method: Alamofire method object
  141. - parameter url: An object adopting `URLConvertible`
  142. - parameter parameters: A dictionary containing all necessary options
  143. - parameter encoding: The kind of encoding used to process parameters
  144. - parameter header: A dictionary containing all the additional headers
  145. - returns: An observable of the tuple `(NSHTTPURLResponse, String)`
  146. */
  147. public func requestString(_ method: Alamofire.HTTPMethod,
  148. _ url: URLConvertible,
  149. parameters: [String: Any]? = nil,
  150. encoding: ParameterEncoding = URLEncoding.default,
  151. headers: [String: String]? = nil)
  152. -> Observable<(HTTPURLResponse, String)>
  153. {
  154. return SessionManager.default.rx.responseString(
  155. method,
  156. url,
  157. parameters: parameters,
  158. encoding: encoding,
  159. headers: headers
  160. )
  161. }
  162. /**
  163. Creates an observable of the returned decoded string and response.
  164. - parameter urlRequest: An object adopting `URLRequestConvertible`
  165. - returns: An observable of the tuple `(NSHTTPURLResponse, String)`
  166. */
  167. public func requestString(_ urlRequest: URLRequestConvertible) -> Observable<(HTTPURLResponse, String)> {
  168. return request(urlRequest).flatMap { $0.rx.responseString() }
  169. }
  170. /**
  171. Creates an observable of the returned decoded string.
  172. - parameter method: Alamofire method object
  173. - parameter url: An object adopting `URLConvertible`
  174. - parameter parameters: A dictionary containing all necessary options
  175. - parameter encoding: The kind of encoding used to process parameters
  176. - parameter header: A dictionary containing all the additional headers
  177. - returns: An observable of `String`
  178. */
  179. public func string(_ method: Alamofire.HTTPMethod,
  180. _ url: URLConvertible,
  181. parameters: [String: Any]? = nil,
  182. encoding: ParameterEncoding = URLEncoding.default,
  183. headers: [String: String]? = nil)
  184. -> Observable<String>
  185. {
  186. return SessionManager.default.rx.string(
  187. method,
  188. url,
  189. parameters: parameters,
  190. encoding: encoding,
  191. headers: headers
  192. )
  193. }
  194. // MARK: JSON
  195. /**
  196. Creates an observable of the returned decoded JSON as `AnyObject` and the response.
  197. - parameter method: Alamofire method object
  198. - parameter url: An object adopting `URLConvertible`
  199. - parameter parameters: A dictionary containing all necessary options
  200. - parameter encoding: The kind of encoding used to process parameters
  201. - parameter header: A dictionary containing all the additional headers
  202. - returns: An observable of the tuple `(NSHTTPURLResponse, AnyObject)`
  203. */
  204. public func requestJSON(_ method: Alamofire.HTTPMethod,
  205. _ url: URLConvertible,
  206. parameters: [String: Any]? = nil,
  207. encoding: ParameterEncoding = URLEncoding.default,
  208. headers: [String: String]? = nil)
  209. -> Observable<(HTTPURLResponse, Any)>
  210. {
  211. return SessionManager.default.rx.responseJSON(
  212. method,
  213. url,
  214. parameters: parameters,
  215. encoding: encoding,
  216. headers: headers
  217. )
  218. }
  219. /**
  220. Creates an observable of the returned decoded JSON as `AnyObject` and the response.
  221. - parameter urlRequest: An object adopting `URLRequestConvertible`
  222. - returns: An observable of the tuple `(NSHTTPURLResponse, AnyObject)`
  223. */
  224. public func requestJSON(_ urlRequest: URLRequestConvertible) -> Observable<(HTTPURLResponse, Any)> {
  225. return request(urlRequest).flatMap { $0.rx.responseJSON() }
  226. }
  227. /**
  228. Creates an observable of the returned decoded JSON.
  229. - parameter method: Alamofire method object
  230. - parameter url: An object adopting `URLConvertible`
  231. - parameter parameters: A dictionary containing all necessary options
  232. - parameter encoding: The kind of encoding used to process parameters
  233. - parameter header: A dictionary containing all the additional headers
  234. - returns: An observable of the decoded JSON as `Any`
  235. */
  236. public func json(_ method: Alamofire.HTTPMethod,
  237. _ url: URLConvertible,
  238. parameters: [String: Any]? = nil,
  239. encoding: ParameterEncoding = URLEncoding.default,
  240. headers: [String: String]? = nil)
  241. -> Observable<Any>
  242. {
  243. return SessionManager.default.rx.json(
  244. method,
  245. url,
  246. parameters: parameters,
  247. encoding: encoding,
  248. headers: headers
  249. )
  250. }
  251. // MARK: Upload
  252. /**
  253. Returns an observable of a request using the shared manager instance to upload a specific file to a specified URL.
  254. The request is started immediately.
  255. - parameter urlRequest: The request object to start the upload.
  256. - paramenter file: An instance of NSURL holding the information of the local file.
  257. - returns: The observable of `UploadRequest` for the created request.
  258. */
  259. public func upload(_ file: URL, urlRequest: URLRequestConvertible) -> Observable<UploadRequest> {
  260. return SessionManager.default.rx.upload(file, urlRequest: urlRequest)
  261. }
  262. /**
  263. Returns an observable of a request using the shared manager instance to upload any data to a specified URL.
  264. The request is started immediately.
  265. - parameter urlRequest: The request object to start the upload.
  266. - paramenter data: An instance of NSData holdint the data to upload.
  267. - returns: The observable of `UploadRequest` for the created request.
  268. */
  269. public func upload(_ data: Data, urlRequest: URLRequestConvertible) -> Observable<UploadRequest> {
  270. return SessionManager.default.rx.upload(data , urlRequest: urlRequest)
  271. }
  272. /**
  273. Returns an observable of a request using the shared manager instance to upload any stream to a specified URL.
  274. The request is started immediately.
  275. - parameter urlRequest: The request object to start the upload.
  276. - paramenter stream: The stream to upload.
  277. - returns: The observable of `Request` for the created upload request.
  278. */
  279. public func upload(_ stream: InputStream, urlRequest: URLRequestConvertible) -> Observable<UploadRequest> {
  280. return SessionManager.default.rx.upload(stream, urlRequest: urlRequest)
  281. }
  282. // MARK: Download
  283. /**
  284. Creates a download request using the shared manager instance for the specified URL request.
  285. - parameter urlRequest: The URL request.
  286. - parameter destination: The closure used to determine the destination of the downloaded file.
  287. - returns: The observable of `DownloadRequest` for the created download request.
  288. */
  289. public func download(_ urlRequest: URLRequestConvertible,
  290. to destination: @escaping DownloadRequest.DownloadFileDestination) -> Observable<DownloadRequest> {
  291. return SessionManager.default.rx.download(urlRequest, to: destination)
  292. }
  293. // MARK: Resume Data
  294. /**
  295. Creates a request using the shared manager instance for downloading from the resume data produced from a
  296. previous request cancellation.
  297. - parameter resumeData: The resume data. This is an opaque data blob produced by `NSURLSessionDownloadTask`
  298. when a task is cancelled. See `NSURLSession -downloadTaskWithResumeData:` for additional
  299. information.
  300. - parameter destination: The closure used to determine the destination of the downloaded file.
  301. - returns: The observable of `Request` for the created download request.
  302. */
  303. public func download(resumeData: Data,
  304. to destination: @escaping DownloadRequest.DownloadFileDestination) -> Observable<DownloadRequest> {
  305. return SessionManager.default.rx.download(resumeData: resumeData, to: destination)
  306. }
  307. // MARK: Manager - Extension of Manager
  308. extension SessionManager: ReactiveCompatible {
  309. }
  310. protocol RxAlamofireRequest {
  311. func responseWith(completionHandler: @escaping (RxAlamofireResponse) -> Void)
  312. func resume()
  313. func cancel()
  314. }
  315. protocol RxAlamofireResponse {
  316. var error: Error? {get}
  317. }
  318. extension DefaultDataResponse: RxAlamofireResponse {}
  319. extension DefaultDownloadResponse: RxAlamofireResponse {}
  320. extension DataRequest: RxAlamofireRequest {
  321. func responseWith(completionHandler: @escaping (RxAlamofireResponse) -> Void) {
  322. response { (response) in
  323. completionHandler(response)
  324. }
  325. }
  326. }
  327. extension DownloadRequest: RxAlamofireRequest {
  328. func responseWith(completionHandler: @escaping (RxAlamofireResponse) -> Void) {
  329. response { (response) in
  330. completionHandler(response)
  331. }
  332. }
  333. }
  334. extension Reactive where Base: SessionManager {
  335. // MARK: Generic request convenience
  336. /**
  337. Creates an observable of the DataRequest.
  338. - parameter createRequest: A function used to create a `Request` using a `Manager`
  339. - returns: A generic observable of created data request
  340. */
  341. func request<R: RxAlamofireRequest>(_ createRequest: @escaping (SessionManager) throws -> R) -> Observable<R> {
  342. return Observable.create { observer -> Disposable in
  343. let request: R
  344. do {
  345. request = try createRequest(self.base)
  346. observer.on(.next(request))
  347. request.responseWith(completionHandler: { (response) in
  348. if let error = response.error {
  349. observer.on(.error(error))
  350. } else {
  351. observer.on(.completed)
  352. }
  353. })
  354. if !self.base.startRequestsImmediately {
  355. request.resume()
  356. }
  357. return Disposables.create {
  358. request.cancel()
  359. }
  360. }
  361. catch let error {
  362. observer.on(.error(error))
  363. return Disposables.create()
  364. }
  365. }
  366. }
  367. /**
  368. Creates an observable of the `Request`.
  369. - parameter method: Alamofire method object
  370. - parameter url: An object adopting `URLConvertible`
  371. - parameter parameters: A dictionary containing all necessary options
  372. - parameter encoding: The kind of encoding used to process parameters
  373. - parameter header: A dictionary containing all the additional headers
  374. - returns: An observable of the `Request`
  375. */
  376. public func request(_ method: Alamofire.HTTPMethod,
  377. _ url: URLConvertible,
  378. parameters: [String: Any]? = nil,
  379. encoding: ParameterEncoding = URLEncoding.default,
  380. headers: [String: String]? = nil
  381. )
  382. -> Observable<DataRequest>
  383. {
  384. return request { manager in
  385. return manager.request(url,
  386. method: method,
  387. parameters: parameters,
  388. encoding: encoding,
  389. headers: headers)
  390. }
  391. }
  392. /**
  393. Creates an observable of the `Request`.
  394. - parameter URLRequest: An object adopting `URLRequestConvertible`
  395. - parameter parameters: A dictionary containing all necessary options
  396. - parameter encoding: The kind of encoding used to process parameters
  397. - parameter header: A dictionary containing all the additional headers
  398. - returns: An observable of the `Request`
  399. */
  400. public func request(urlRequest: URLRequestConvertible)
  401. -> Observable<DataRequest>
  402. {
  403. return request { manager in
  404. return manager.request(urlRequest)
  405. }
  406. }
  407. // MARK: data
  408. /**
  409. Creates an observable of the data.
  410. - parameter url: An object adopting `URLConvertible`
  411. - parameter parameters: A dictionary containing all necessary options
  412. - parameter encoding: The kind of encoding used to process parameters
  413. - parameter header: A dictionary containing all the additional headers
  414. - returns: An observable of the tuple `(NSHTTPURLResponse, NSData)`
  415. */
  416. public func responseData(_ method: Alamofire.HTTPMethod,
  417. _ url: URLConvertible,
  418. parameters: [String: Any]? = nil,
  419. encoding: ParameterEncoding = URLEncoding.default,
  420. headers: [String: String]? = nil
  421. )
  422. -> Observable<(HTTPURLResponse, Data)>
  423. {
  424. return request(
  425. method,
  426. url,
  427. parameters: parameters,
  428. encoding: encoding,
  429. headers: headers
  430. ).flatMap { $0.rx.responseData() }
  431. }
  432. /**
  433. Creates an observable of the data.
  434. - parameter URLRequest: An object adopting `URLRequestConvertible`
  435. - parameter parameters: A dictionary containing all necessary options
  436. - parameter encoding: The kind of encoding used to process parameters
  437. - parameter header: A dictionary containing all the additional headers
  438. - returns: An observable of `NSData`
  439. */
  440. public func data(_ method: Alamofire.HTTPMethod,
  441. _ url: URLConvertible,
  442. parameters: [String: Any]? = nil,
  443. encoding: ParameterEncoding = URLEncoding.default,
  444. headers: [String: String]? = nil
  445. )
  446. -> Observable<Data>
  447. {
  448. return request(
  449. method,
  450. url,
  451. parameters: parameters,
  452. encoding: encoding,
  453. headers: headers
  454. ).flatMap { $0.rx.data() }
  455. }
  456. // MARK: string
  457. /**
  458. Creates an observable of the tuple `(NSHTTPURLResponse, String)`.
  459. - parameter url: An object adopting `URLRequestConvertible`
  460. - parameter parameters: A dictionary containing all necessary options
  461. - parameter encoding: The kind of encoding used to process parameters
  462. - parameter header: A dictionary containing all the additional headers
  463. - returns: An observable of the tuple `(NSHTTPURLResponse, String)`
  464. */
  465. public func responseString(_ method: Alamofire.HTTPMethod,
  466. _ url: URLConvertible,
  467. parameters: [String: Any]? = nil,
  468. encoding: ParameterEncoding = URLEncoding.default,
  469. headers: [String: String]? = nil
  470. )
  471. -> Observable<(HTTPURLResponse, String)>
  472. {
  473. return request(
  474. method,
  475. url,
  476. parameters: parameters,
  477. encoding: encoding,
  478. headers: headers
  479. ).flatMap { $0.rx.responseString() }
  480. }
  481. /**
  482. Creates an observable of the data encoded as String.
  483. - parameter url: An object adopting `URLConvertible`
  484. - parameter parameters: A dictionary containing all necessary options
  485. - parameter encoding: The kind of encoding used to process parameters
  486. - parameter header: A dictionary containing all the additional headers
  487. - returns: An observable of `String`
  488. */
  489. public func string(_ method: Alamofire.HTTPMethod,
  490. _ url: URLConvertible,
  491. parameters: [String: Any]? = nil,
  492. encoding: ParameterEncoding = URLEncoding.default,
  493. headers: [String: String]? = nil
  494. )
  495. -> Observable<String>
  496. {
  497. return request(
  498. method,
  499. url,
  500. parameters: parameters,
  501. encoding: encoding,
  502. headers: headers
  503. )
  504. .flatMap { (request) -> Observable<String> in
  505. return request.rx.string()
  506. }
  507. }
  508. // MARK: JSON
  509. /**
  510. Creates an observable of the data decoded from JSON and processed as tuple `(NSHTTPURLResponse, AnyObject)`.
  511. - parameter url: An object adopting `URLRequestConvertible`
  512. - parameter parameters: A dictionary containing all necessary options
  513. - parameter encoding: The kind of encoding used to process parameters
  514. - parameter header: A dictionary containing all the additional headers
  515. - returns: An observable of the tuple `(NSHTTPURLResponse, AnyObject)`
  516. */
  517. public func responseJSON(_ method: Alamofire.HTTPMethod,
  518. _ url: URLConvertible,
  519. parameters: [String: Any]? = nil,
  520. encoding: ParameterEncoding = URLEncoding.default,
  521. headers: [String: String]? = nil
  522. )
  523. -> Observable<(HTTPURLResponse, Any)>
  524. {
  525. return request(method,
  526. url,
  527. parameters: parameters,
  528. encoding: encoding,
  529. headers: headers
  530. ).flatMap { $0.rx.responseJSON() }
  531. }
  532. /**
  533. Creates an observable of the data decoded from JSON and processed as `AnyObject`.
  534. - parameter URLRequest: An object adopting `URLRequestConvertible`
  535. - parameter parameters: A dictionary containing all necessary options
  536. - parameter encoding: The kind of encoding used to process parameters
  537. - parameter header: A dictionary containing all the additional headers
  538. - returns: An observable of `AnyObject`
  539. */
  540. public func json(_ method: Alamofire.HTTPMethod,
  541. _ url: URLConvertible,
  542. parameters: [String: Any]? = nil,
  543. encoding: ParameterEncoding = URLEncoding.default,
  544. headers: [String: String]? = nil
  545. )
  546. -> Observable<Any>
  547. {
  548. return request(
  549. method,
  550. url,
  551. parameters: parameters,
  552. encoding: encoding,
  553. headers: headers
  554. ).flatMap { $0.rx.json() }
  555. }
  556. // MARK: Upload
  557. /**
  558. Returns an observable of a request using the shared manager instance to upload a specific file to a specified URL.
  559. The request is started immediately.
  560. - parameter urlRequest: The request object to start the upload.
  561. - paramenter file: An instance of NSURL holding the information of the local file.
  562. - returns: The observable of `AnyObject` for the created request.
  563. */
  564. public func upload(_ file: URL, urlRequest: URLRequestConvertible) -> Observable<UploadRequest> {
  565. return request { manager in
  566. return manager.upload(file, with: urlRequest)
  567. }
  568. }
  569. /**
  570. Returns an observable of a request using the shared manager instance to upload any data to a specified URL.
  571. The request is started immediately.
  572. - parameter urlRequest: The request object to start the upload.
  573. - paramenter data: An instance of Data holdint the data to upload.
  574. - returns: The observable of `UploadRequest` for the created request.
  575. */
  576. public func upload(_ data: Data, urlRequest: URLRequestConvertible) -> Observable<UploadRequest> {
  577. return request { manager in
  578. return manager.upload(data, with: urlRequest)
  579. }
  580. }
  581. /**
  582. Returns an observable of a request using the shared manager instance to upload any stream to a specified URL.
  583. The request is started immediately.
  584. - parameter urlRequest: The request object to start the upload.
  585. - paramenter stream: The stream to upload.
  586. - returns: The observable of `(NSData?, RxProgress)` for the created upload request.
  587. */
  588. public func upload(_ stream: InputStream,
  589. urlRequest: URLRequestConvertible) -> Observable<UploadRequest> {
  590. return request { manager in
  591. return manager.upload(stream, with: urlRequest)
  592. }
  593. }
  594. // MARK: Download
  595. /**
  596. Creates a download request using the shared manager instance for the specified URL request.
  597. - parameter urlRequest: The URL request.
  598. - parameter destination: The closure used to determine the destination of the downloaded file.
  599. - returns: The observable of `(NSData?, RxProgress)` for the created download request.
  600. */
  601. public func download(_ urlRequest: URLRequestConvertible,
  602. to destination: @escaping DownloadRequest.DownloadFileDestination) -> Observable<DownloadRequest> {
  603. return request { manager in
  604. return manager.download(urlRequest, to: destination)
  605. }
  606. }
  607. /**
  608. Creates a request using the shared manager instance for downloading with a resume data produced from a
  609. previous request cancellation.
  610. - parameter resumeData: The resume data. This is an opaque data blob produced by `NSURLSessionDownloadTask`
  611. when a task is cancelled. See `NSURLSession -downloadTaskWithResumeData:` for additional
  612. information.
  613. - parameter destination: The closure used to determine the destination of the downloaded file.
  614. - returns: The observable of `(NSData?, RxProgress)` for the created download request.
  615. */
  616. public func download(resumeData: Data,
  617. to destination: @escaping DownloadRequest.DownloadFileDestination) -> Observable<DownloadRequest> {
  618. return request { manager in
  619. return manager.download(resumingWith: resumeData, to: destination)
  620. }
  621. }
  622. }
  623. // MARK: Request - Common Response Handlers
  624. extension ObservableType where E == DataRequest {
  625. public func responseJSON() -> Observable<DataResponse<Any>> {
  626. return flatMap { $0.rx.responseJSON() }
  627. }
  628. public func json(options: JSONSerialization.ReadingOptions = .allowFragments) -> Observable<Any> {
  629. return flatMap { $0.rx.json(options: options) }
  630. }
  631. public func responseString(encoding: String.Encoding? = nil) -> Observable<(HTTPURLResponse, String)> {
  632. return flatMap { $0.rx.responseString(encoding: encoding) }
  633. }
  634. public func string(encoding: String.Encoding? = nil) -> Observable<String> {
  635. return flatMap { $0.rx.string(encoding: encoding) }
  636. }
  637. public func responseData() -> Observable<(HTTPURLResponse, Data)> {
  638. return flatMap { $0.rx.responseData() }
  639. }
  640. public func data() -> Observable<Data> {
  641. return flatMap { $0.rx.data() }
  642. }
  643. public func responsePropertyList(options: PropertyListSerialization.ReadOptions = PropertyListSerialization.ReadOptions()) -> Observable<(HTTPURLResponse, Any)> {
  644. return flatMap { $0.rx.responsePropertyList(options: options) }
  645. }
  646. public func propertyList(options: PropertyListSerialization.ReadOptions = PropertyListSerialization.ReadOptions()) -> Observable<Any> {
  647. return flatMap { $0.rx.propertyList(options: options) }
  648. }
  649. public func progress() -> Observable<RxProgress> {
  650. return flatMap { $0.rx.progress() }
  651. }
  652. }
  653. // MARK: Request - Validation
  654. extension ObservableType where E == DataRequest {
  655. public func validate<S: Sequence>(statusCode: S) -> Observable<E> where S.Element == Int {
  656. return map { $0.validate(statusCode: statusCode) }
  657. }
  658. public func validate() -> Observable<E> {
  659. return map { $0.validate() }
  660. }
  661. public func validate<S: Sequence>(contentType acceptableContentTypes: S) -> Observable<E> where S.Iterator.Element == String {
  662. return map { $0.validate(contentType: acceptableContentTypes) }
  663. }
  664. public func validate(_ validation: @escaping DataRequest.Validation) -> Observable<E> {
  665. return map { $0.validate(validation) }
  666. }
  667. }
  668. extension Request: ReactiveCompatible {
  669. }
  670. extension Reactive where Base: DataRequest {
  671. // MARK: Defaults
  672. /// - returns: A validated request based on the status code
  673. func validateSuccessfulResponse() -> DataRequest {
  674. return self.base.validate(statusCode: 200 ..< 300)
  675. }
  676. /**
  677. Transform a request into an observable of the response and serialized object.
  678. - parameter queue: The dispatch queue to use.
  679. - parameter responseSerializer: The the serializer.
  680. - returns: The observable of `(NSHTTPURLResponse, T.SerializedObject)` for the created download request.
  681. */
  682. public func responseResult<T: DataResponseSerializerProtocol>(queue: DispatchQueue? = nil,
  683. responseSerializer: T)
  684. -> Observable<(HTTPURLResponse, T.SerializedObject)>
  685. {
  686. return Observable.create { observer in
  687. let dataRequest = self.base
  688. .response(queue: queue, responseSerializer: responseSerializer) { (packedResponse) -> Void in
  689. switch packedResponse.result {
  690. case .success(let result):
  691. if let httpResponse = packedResponse.response {
  692. observer.on(.next((httpResponse, result)))
  693. observer.on(.completed)
  694. }
  695. else {
  696. observer.on(.error(RxAlamofireUnknownError))
  697. }
  698. case .failure(let error):
  699. observer.on(.error(error as Error))
  700. }
  701. }
  702. return Disposables.create {
  703. dataRequest.cancel()
  704. }
  705. }
  706. }
  707. public func responseJSON() -> Observable<DataResponse<Any>> {
  708. return Observable.create { observer in
  709. let request = self.base
  710. request.responseJSON { response in
  711. if let error = response.result.error {
  712. observer.on(.error(error))
  713. } else {
  714. observer.on(.next(response))
  715. observer.on(.completed)
  716. }
  717. }
  718. return Disposables.create {
  719. request.cancel()
  720. }
  721. }
  722. }
  723. /**
  724. Transform a request into an observable of the serialized object.
  725. - parameter queue: The dispatch queue to use.
  726. - parameter responseSerializer: The the serializer.
  727. - returns: The observable of `T.SerializedObject` for the created download request.
  728. */
  729. public func result<T: DataResponseSerializerProtocol>(
  730. queue: DispatchQueue? = nil,
  731. responseSerializer: T)
  732. -> Observable<T.SerializedObject>
  733. {
  734. return Observable.create { observer in
  735. let dataRequest = self.validateSuccessfulResponse()
  736. .response(queue: queue, responseSerializer: responseSerializer) { (packedResponse) -> Void in
  737. switch packedResponse.result {
  738. case .success(let result):
  739. if let _ = packedResponse.response {
  740. observer.on(.next(result))
  741. observer.on(.completed)
  742. }
  743. else {
  744. observer.on(.error(RxAlamofireUnknownError))
  745. }
  746. case .failure(let error):
  747. observer.on(.error(error as Error))
  748. }
  749. }
  750. return Disposables.create {
  751. dataRequest.cancel()
  752. }
  753. }
  754. }
  755. /**
  756. Returns an `Observable` of NSData for the current request.
  757. - parameter cancelOnDispose: Indicates if the request has to be canceled when the observer is disposed, **default:** `false`
  758. - returns: An instance of `Observable<NSData>`
  759. */
  760. public func responseData() -> Observable<(HTTPURLResponse, Data)> {
  761. return responseResult(responseSerializer: DataRequest.dataResponseSerializer())
  762. }
  763. public func data() -> Observable<Data> {
  764. return result(responseSerializer: DataRequest.dataResponseSerializer())
  765. }
  766. /**
  767. Returns an `Observable` of a String for the current request
  768. - parameter encoding: Type of the string encoding, **default:** `nil`
  769. - returns: An instance of `Observable<String>`
  770. */
  771. public func responseString(encoding: String.Encoding? = nil) -> Observable<(HTTPURLResponse, String)> {
  772. return responseResult(responseSerializer: Base.stringResponseSerializer(encoding: encoding))
  773. }
  774. public func string(encoding: String.Encoding? = nil) -> Observable<String> {
  775. return result(responseSerializer: Base.stringResponseSerializer(encoding: encoding))
  776. }
  777. /**
  778. Returns an `Observable` of a serialized JSON for the current request.
  779. - parameter options: Reading options for JSON decoding process, **default:** `.AllowFragments`
  780. - returns: An instance of `Observable<AnyObject>`
  781. */
  782. public func responseJSON(options: JSONSerialization.ReadingOptions = .allowFragments) -> Observable<(HTTPURLResponse, Any)> {
  783. return responseResult(responseSerializer: Base.jsonResponseSerializer(options: options))
  784. }
  785. /**
  786. Returns an `Observable` of a serialized JSON for the current request.
  787. - parameter options: Reading options for JSON decoding process, **default:** `.AllowFragments`
  788. - returns: An instance of `Observable<AnyObject>`
  789. */
  790. public func json(options: JSONSerialization.ReadingOptions = .allowFragments) -> Observable<Any> {
  791. return result(responseSerializer: Base.jsonResponseSerializer(options: options))
  792. }
  793. /**
  794. Returns and `Observable` of a serialized property list for the current request.
  795. - parameter options: Property list reading options, **default:** `NSPropertyListReadOptions()`
  796. - returns: An instance of `Observable<AnyData>`
  797. */
  798. public func responsePropertyList(options: PropertyListSerialization.ReadOptions = PropertyListSerialization.ReadOptions()) -> Observable<(HTTPURLResponse, Any)> {
  799. return responseResult(responseSerializer: Base.propertyListResponseSerializer(options: options))
  800. }
  801. public func propertyList(options: PropertyListSerialization.ReadOptions = PropertyListSerialization.ReadOptions()) -> Observable<Any> {
  802. return result(responseSerializer: Base.propertyListResponseSerializer(options: options))
  803. }
  804. }
  805. extension Reactive where Base: Request {
  806. // MARK: Request - Upload and download progress
  807. /**
  808. Returns an `Observable` for the current progress status.
  809. Parameters on observed tuple:
  810. 1. bytes written so far.
  811. 1. total bytes to write.
  812. - returns: An instance of `Observable<RxProgress>`
  813. */
  814. public func progress() -> Observable<RxProgress> {
  815. return Observable.create { observer in
  816. let handler: Request.ProgressHandler = { progress in
  817. let rxProgress = RxProgress(bytesWritten: progress.completedUnitCount,
  818. totalBytes: progress.totalUnitCount)
  819. observer.on(.next(rxProgress))
  820. if rxProgress.bytesWritten >= rxProgress.totalBytes {
  821. observer.on(.completed)
  822. }
  823. }
  824. // Try in following order:
  825. // - UploadRequest (Inherits from DataRequest, so we test the discrete case first)
  826. // - DownloadRequest
  827. // - DataRequest
  828. if let uploadReq = self.base as? UploadRequest {
  829. uploadReq.uploadProgress(closure: handler)
  830. } else if let downloadReq = self.base as? DownloadRequest {
  831. downloadReq.downloadProgress(closure: handler)
  832. } else if let dataReq = self.base as? DataRequest {
  833. dataReq.downloadProgress(closure: handler)
  834. }
  835. return Disposables.create()
  836. }
  837. // warm up a bit :)
  838. .startWith(RxProgress(bytesWritten: 0, totalBytes: 0))
  839. }
  840. }
  841. // MARK: RxProgress
  842. public struct RxProgress {
  843. public let bytesWritten: Int64
  844. public let totalBytes: Int64
  845. public init(bytesWritten: Int64, totalBytes: Int64) {
  846. self.bytesWritten = bytesWritten
  847. self.totalBytes = totalBytes
  848. }
  849. }
  850. extension RxProgress {
  851. public var bytesRemaining: Int64 {
  852. return totalBytes - bytesWritten
  853. }
  854. public var completed: Float {
  855. if totalBytes > 0 {
  856. return Float(bytesWritten) / Float(totalBytes)
  857. }
  858. else {
  859. return 0
  860. }
  861. }
  862. }
  863. extension RxProgress: Equatable {}
  864. public func ==(lhs: RxProgress, rhs: RxProgress) -> Bool {
  865. return lhs.bytesWritten == rhs.bytesWritten &&
  866. lhs.totalBytes == rhs.totalBytes
  867. }