Network request with RxSwift example

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 :).

Example code for network request with RxSwift

Network request with RxSwift

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:

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:

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.

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:

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!

This article has 4 comments

  1. ning

    why two occurrences of onError in this code ?
    case .failure(let error):
    if let statusCode = response.response?.statusCode,
    let reason = GetFriendsFailureReason(rawValue: statusCode)
    {
    observer.onError(reason)
    }
    observer.onError(error)
    }

    • Jimmy

      First we try to map the received error for the ones that we have defined for our application. So if the error server sends is one of the GetFriendsFailureReason values we send those for the UI layer. This way it is easy for example to show specific localised error messages for the user. If server sends an error we haven’t specified in our code, we just forward the original error.

  2. ning

    Oh, thank you, now I understand. Maybe it’s better to add an else branch, it’s more readable, or maybe not.

    case .failure(let error):
    if let statusCode = response.response?.statusCode,
    let reason = GetFriendsFailureReason(rawValue: statusCode)
    {
    observer.onError(reason)
    } else {
    observer.onError(error)
    }

    • Jimmy

      Yep, that looks better! Nicely spotted! 🙂 I think I’d prefer even more a guard statement since it feels a bit more swifty… I’ll update the code. I have some work to do with new blog posts so this won’t happen this week.