This time I want to show you how you can write network request with RxSwift. The biggest change with network request with RxSwift is that we don’t have to use completion blocks, delegates or other techniques to receive the asynchronous response. Since with RxSwift everything is an observable, the caller simply starts to listen the events after starting the request.

I assume that you are familiar with Observable and the events that it can emit so I won’t explain them in detail in this post. In case you want to recap, I suggest that you read my previous post How to use RxSwift with MVVM to get the detailed explanation. This post concentrates strictly to network request with RxSwift.

I’ll use the Friends projects AppServerClient class to go through the idea. In case you are not familiar, Friends project is a project to teach you how to use Model-View-ViewModel pattern. Friends application is an iOS app that you can store and maintain a list of friends. I wrote the project some time ago and now I wanted to see how the code changes when I refactor it to use RxSwift. Of course the backend for the project is written in Swift using Vapor.

You can get the codes from GitHub, just remember to checkout the RxSwift branch, but you can also follow this tutorial without checking out the codes. But now let’s get down to business :).

Network request with RxSwift

As said, Friends app uses AppServerClient to handle all the networking. It is not a complete example of networking layer you can use in your app, it for example uses default http-headers which you probably want to set up yourself. However, it is sufficient for the sake of this example for simple networking. Just please don’t use this code straight in production code.

Network layer should always return the fetched value in an asynchronous way. For that, we could for example use delegation or completion blocks. When we making a network request with RxSwift, we’ll use an Observable. So every time a network request is created we’ll return an Observable back to the caller. I chose to use Observable since it is the most familiar type and fits for all my networking cases. You might also want to check Single and Completable types if they better suite your requests needs.

Here is the code for fetching the friends:

get friends from the server // MARK: - GetFriends enum GetFriendsFailureReason: Int, Error { case unAuthorized = 401 case notFound = 404 } func getFriends() -> Observable<[Friend]> { return Observable.create { observer -> Disposable in Alamofire.request("http://friendservice.herokuapp.com/listFriends") .validate() .responseJSON { response in switch response.result { case .success: guard let data = response.data else { // if no error provided by alamofire return .notFound error instead. // .notFound should never happen here? observer.onError(response.error ?? GetFriendsFailureReason.notFound) return } do { let friends = try JSONDecoder().decode([Friend].self, from: data) observer.onNext(friends) } catch { observer.onError(error) } case .failure(let error): if let statusCode = response.response?.statusCode, let reason = GetFriendsFailureReason(rawValue: statusCode) { observer.onError(reason) } observer.onError(error) } } return Disposables.create() } } 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 // MARK: - GetFriends enum GetFriendsFailureReason : Int , Error { case unAuthorized = 401 case notFound = 404 } func getFriends ( ) -> Observable < [ Friend ] > { return Observable . create { observer -> Disposable in Alamofire . request ( "http://friendservice.herokuapp.com/listFriends" ) . validate ( ) . responseJSON { response in switch response . result { case . success : guard let data = response . data else { // if no error provided by alamofire return .notFound error instead. // .notFound should never happen here? observer . onError ( response . error ? ? GetFriendsFailureReason . notFound ) return } do { let friends = try JSONDecoder ( ) . decode ( [ Friend ] . self , from : data ) observer . onNext ( friends ) } catch { observer . onError ( error ) } case . failure ( let error ) : if let statusCode = response . response ? . statusCode , let reason = GetFriendsFailureReason ( rawValue : statusCode ) { observer . onError ( reason ) } observer . onError ( error ) } } return Disposables . create ( ) } }

At first we’ll define an error value that can be mapped from the http error codes. Here we have defined GetFriendsFailureReason with cases .unAuthorized and .notFound. Next we’ll notice that the getFriends() functions returns an Observable<[Friend]>. When we go inside the function, the first thing we need to create is the observable that is returned.

Observable has static function called create. We can use that to create a new observable. We’ll pass a block that handles the network request as a parameter. We’ll use alamofire to fetch the data so most of the code above might look very familiar to you. We’ll chain the request, validate, responseJSON calls and then we’ll handle the response. In case you want to recap a little bit on Alamofire you can check more thorough explanation what is happening with the request in MVVM with swift article, look for “Alamofire” subtitle. Here we’ll mostly concentrate on handling the response data using RxSwift.

Emit onNext state on success case

First we’ll use switch for the response to check for .success and .failure. In success case we’ll check that the response actually contains some data. If not we’ll emit an error, provided by Alamofire. Since Alamofire returns an optional error, and the onError does not take optional as parameter we’ll use the nil coalescing operator to provide a fallback error value. This is only used when Alamofire does not have an error value. I am not sure when this happens, I think very rarely, but still the case needs to be handled.

In case we have the data, let’s convert the received JSON in to an array of Friends using Codable and emit the response to the subscriber using onNext:

Convert json to Friend objects and emitting the result to subscribers let friends = try JSONDecoder().decode([Friend].self, from: data) observer.onNext(friends) 1 2 let friends = try JSONDecoder ( ) . decode ( [ Friend ] . self , from : data ) observer . onNext ( friends )

Since the decode method might throw, we need call it inside do…catch block. In the catch block, we need to emit onError and provide the error received in the catch block as a parameter.

Emit onError on failure case

In the error case, we’ll first try to convert the error to the predefined enum values from the Alamofire status code. If that succeeds, we’ll emit the error value with the onError. In case the error is a value we have not defined, we’ll use the one provided by Alamofire directly.

The create function requires that it returns a dispose block. Since we don’t have anything to dispose here, we can use the convenient .create() function, which as the documentation says “does nothing special”. In case we’d have to dispose something here we could return a block which then calls dispose for the wanted variables.

Next let’s see how we can subscribe to the events.

Subscribe to receive getFriends

In the project this call is made in the FriendsTableViewViewModel.

Subscribe to network request using RxSwift appServerClient .getFriends() .subscribe( onNext: { [weak self] friends in // Store value }, onError: { [weak self] error in // Present error } ) .disposed(by: disposeBag) 1 2 3 4 5 6 7 8 9 10 11 appServerClient . getFriends ( ) . subscribe ( onNext : { [ weak self ] friends in // Store value } , onError : { [ weak self ] error in // Present error } ) . disposed ( by : disposeBag )

The code is pretty self-explanatory. We first call getFriends() and after that subscribe to the events. Then we handle the onNext and onError events and do what ever we need with the responses.

You might want to consider using observeOn after the getFriends call. If the code is updating the UI all changes needs to be made in the main tread. To make sure the completion block is run in the UIThread you can add this line below the getFriends call:

observe on mainthread .observeOn(MainScheduler.instance) 1 . observeOn ( MainScheduler . instance )

Conclusion

So making a network request with RxSwift is pretty simple! We just need to create an observer and inside the block that we give as parameter, we write what ever network request code we want to use. Whether it is Alamofire orURLRequest, the choice is ours. All the code is in GitHub, so you can check the code for creating, deleting and updating a friend. All other request follow this same familiar pattern so I am sure you can figure those out. If you have any trouble, question or feedback, please leave a comment or DM me on twitter.

Now, thanks for reading and have a great day my friend!