Uncategorized

Easily handle server errors by default in iOS

One of the things that sets a good app apart is that when something goes wrong, which it inevitably does, it handles the error gracefully, and, better, it makes it easy for developers to fix the issue. A reasonable first step towards this is to display an alert to the user detailing what happened, and certainly in a debug environment to do so with technical details.

My app development company wrote some code to make error handling easy. With this approach, you can handle errors on a given task in one line of code!

Let’s look at an example. Lots of folks now use URLSession directly, or with a minimal wrapper over it. You might do something like this:

let vc = UIViewController()
var cancelBag: Set<AnyCancellable> = []

let request = URLRequest(url: URL(string: "https://lithobyte.co/api/v1")!)
let session = URLSession(configuration: .default)
var taskPub = session.dataTaskPublisher(for: request).eraseToAnyPublisher()

// handle success
taskPub.sink(receiveCompletion: doNothing, 
             receiveValue: handleSuccessFunctionGoesHere)
    .store(in: &cancelBag)

In words, we have a view controller and some cancellable holder, we create a request and a session, and from those we get a taskPublisher using URLSession‘s Combine extensions, handling the data received in a success scenario. Then handling errors is as simple as adding something like this:

taskPub
  .sink(receiveCompletion: debugTaskErrorAlerter(vc: $0), 
        receiveValue: debugServerResponseErrorHandler(vc: $0))
  .store(in: &cancelBag)

That is, in words: on completion, handle any URLLoading errors (eg, no internet, request timed out, etc), and on receiving a server response, handle any errors returned by the server (eg, 404 not found, 500 server error, etc).

How does it work? Well, there’s some functional programming magic to it (which just allows easy composition of functions for flexibility), but basically it takes in a view controller and uses it to display a UIAlertController that it creates depending on the error encountered. There’s a default parameter called error map that you can pass in to provide your own server error descriptions, and we’ve supplied a custom operator << to merge dictionaries so you can take the defaults for everything except the ones you care about, eg:

debugServerResponseErrorHandler(
  vc: $0, 
  errorMap: urlResponseErrorMessages << [401: "Unauthorized. How DARE you."]
)

Which would use the default error messages except for 401, and in that case it would use what you supplied (in this case: “Unauthorized. How DARE you.”). Just like this:

There are a lot of error handling functions you can use in this library, with lots of ways to mix and match, but it’s helpful to know about custom operators and functional programming, since composing those functions will make things significantly easier.

With this library you can display errors with a simple alert, print the errors, decode any json data associated with the error (in fact, a lot of server frameworks return JSON that looks something like { “message”: “User was not authorized” }, and this library can easily decode and display that message to the user in place of the default error code message), replace the default messages with your own, and so on. But frankly, the reason it’s so useful is that all you have to do is add the cocoapod:

pod 'FunNet/ErrorHandlingCombine', git: 'https://github.com/LithoByte/funnet'

and with one line of code per request you can provide a default error handler. That, for me, is the biggest win, especially early on in an app’s development. Concentrate on the features you want, but make bug squashing down the line easy? Yes please and thank you.

1 thought on “Easily handle server errors by default in iOS”

Leave a comment