Chapters

Hide chapters

RxSwift: Reactive Programming with Swift

Fourth Edition · iOS 13 · Swift 5.1 · Xcode 11

23. RxAlamofire
Written by Florent Pillet

Heads up... You're reading this book for free, with parts of this chapter shown beyond this point as scrambled text.

One of the basic needs of modern mobile applications is the ability to query remote resources. You’ve seen several examples of this throughout this book, using the basic extensions to NSURLSession included with RxSwift.

Many developers like to use wrappers around OS-provided query mechanisms. The most popular, undoubtedly, is Alamofire https://github.com/Alamofire/Alamofire, a networking library with roots in Objective-C as it itself stems from AFNetworking https://github.com/AFNetworking/AFNetworking.

RxAlamofire (https://github.com/RxSwiftCommunity/RxAlamofire) is a project living under the RxSwiftCommunity (https://github.com/RxSwiftCommunity) organization. It adds an idiomatic Rx layer to Alamofire, making it straightforward to integrate into your observable workflow.

Most of the RxAlamofire API revolves around extending SessionManager.

Basic requests

It’s straightforward to perform requests using the default SessionManager session. If you don’t need to reuse a customized session, this can be your go-to request mechanism to retrieve a request result as raw text:

RxAlamofire.string(.get, stringURL)
  .subscribe(onNext: { print($0) })
  .disposed(by: disposeBag)

Most of the time you’ll want to deal with and decode JSON, as simply as this:

RxAlamofire.json(.get, stringURL)
  .subscribe(onNext: { print($0) })
  .disposed(by: disposeBag)

The resulting observable emits the result as a decoded JSON object. Since the element type is Any, you’ll need to further map for observable chaining, or cast it in the subscription.

You can also obtain raw Data:

RxAlamofire.data(.get, stringURL)
  .subscribe(onNext: { print($0) })
  .disposed(by: disposeBag)

RxAlamofire defines variants of these convenience functions prefixed with request (requestString, requestJSON, requestData), taking the same input parameters but returning an observable of a tuple of the HTTPURLResponse object along with the decoded body.

Note: RxAlamofire requests are well-behaved observables. If you dispose() a subscription before the request has completed, the ongoing request is canceled. This is an important behavior of the framework, particularly when performing large uploads or downloads.

All of the above are convenience functions using the default SessionManager. Under the hood, they call the actual implementation defined as reactive extensions to SessionManager:

let session = SessionManager.default
session.rx.json(.get, stringURL)
  .subscribe(onNext: { print($0) })
  .disposed(by: disposeBag)

Note: The SessionManager reactive extensions returning observables of tuple are prefixed with response, not with request. For example, you’ll use session.rx.responseJSON(.get, stringURL) to obtain an Observable<(HTTPURLResponse, Any)>.

Request customization

The examples above didn’t modify the default values for customized parameters, URL encoding and HTTP headers. But that’s easy as well:

// get current weather in london
RxAlamofire.json(.get,
     "http://api.openweathermap.org/data/2.5/weather",
     parameters: ["q": "London", "APPID": "{APIKEY}"])
  .subscribe(onNext: { print($0) })
  .disposed(by: disposeBag)

Response validation

The request and session.rx.request APIs let you perform further validation and manipulation by processing the underlying DataRequest. RxAlamofire provides convenience extensions to do this easily:

let response = request(.get, stringURL)
  .validate(statusCode: 200 ..< 300)
  .validate(contentType: ["text/json"])
  .json()

Downloading files

You can download files to a destination determined by AlamoFire’s DownloadDestination closure type:

let destination: DownloadRequest.DownloadFileDestination = { _, response in
  let docsURL = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask)[0]
  let filename = response.suggestedFilename ?? "image.png"
  let fileURL = docsURL.appendingPathComponent(filename)
  return (fileURL, [.removePreviousFile, .createIntermediateDirectories])
}

RxAlamofire.download(URLRequest(url: someURL), to: destination)
  .subscribe(onCompleted: { print("Download complete") })
  .disposed(by: disposeBag)

Upload tasks

Uploading is equally simple. You can upload in-memory Data, files or provide an InputStream as the data source:

RxAlamofire.upload(someData, to: URLRequest(url: someURL))
  .subscribe(onCompleted: { print("Upload complete") })
  .disposed(by: disposeBag)

Tracking progress

Track upload and download progress by extracting an Observable<RxProgress> from the AlamoFire.Request object emitted by request, download and upload APIs:

RxAlamofire.upload(localFileURL,
                   to: URLRequest(url: remoteURL))
  .validate() // check acceptable status codes
  .progress()
  .subscribe (
    onNext: { progress in
      let percent = Int(100.0 * progress.completed)
      print("Upload progress: \(percent)%")
    },
    onCompleted: { print("Upload complete") }
  )
  .disposed(by: disposeBag)
Have a technical question? Want to report a bug? You can ask questions and report bugs to the book authors in our official book forum here.
© 2024 Kodeco Inc.

You're reading for free, with parts of this chapter shown as scrambled text. Unlock this book, and our entire catalogue of books and videos, with a Kodeco Personal Plan.

Unlock now