Error handling in a Clean Architecture

Today, you’ll learn a lot about how to handle errors in a Clean Architecture. As you’ll see from a conversation I had with Łukasz, there’re both complexities and architectural considerations.


Hi Raymond,

My name is Łukasz, I’m the iOS developer from Poland.

First of all, I want to thank you for all the work you’ve put into creating the Clean Swift architecture. After dealing with MVC and MVVM, this looks like a very compelling alternative.

Currently I’m trying to apply this architecture to my own project, and if it works out, I’ll very likely use it in the upcoming project that I’ll be developing in the company that I work for.

However, after studying the Clean Store example for a while, a question about error handling for all those cases when the Worker, and hence the Interactor can fail due to for example no Internet connection while trying to fetch a JSON, arose.

In the CreateOrder scene, in the Display Logic method displayCreatedOrder(viewModel:) you do the following check:

You basically just check if the viewModel has the order property set, and if not, you treat it as an error case and show the user the failure message.

In my opinion there are two things that are not quite right with this approach:

The first one is that you only know about the fact of the failure, and not about any details, like, for example, the error code or message received from the server. I believe it is due to the simplicity of the example, and I could simply include, along the order object, the error object, which would consist of things like error code and message and do the check in the view controller which of these two is not nil and act accordingly. But this does not solve the second issue, which, in my opinion, is the violation of the Single Responsibility Principle. The method displayCreatedOrder() advertise itself as responsible for displaying the successfully created order, but in fact, it does three things:

  1. It checks if creating the order succeed
  2. If yes, it actually displays the order
  3. It display the failure message otherwise. I thought about writing two methods in Presenter and Interactor, namely, in this example, displayCreatedOrder() and something like displayCreateOrderError(), but that would mean the overhead in writing methods for every single action that could potentially fail.

My question is: do you agree with my concerns, and if yes, what do you think about my attempt to solve it, and what is your usual way of handling errors in your real-life projects?

Looking forward to hearing from you, I’m sure you’ll be able to help solve my problem.

Łukasz,


Hi Łukasz,

Thank you for your kind words. Feedback like this keeps me going. I like that you have critical eyes and pay a lot of attention to details.

As you’ve already noted, this is a simple demo project to illustrate the concepts in Clean Swift. The focus is on the VIP cycle. My goal was to show enough details so it’s easy to read and understand, while not obfuscate with too many nuances to distract.

Below are some thoughts to ponder about.

Multiple methods for success and failure cases

The one-method-to-one-method relationship between the view controller, interactor, and presenter is easy to read and understand. In some situations, this kind of simplicity is good. In other situations, it’s better to split it up into two success and failure cases.

I’ve tried to do both and it worked well. If your result-handling code is more complex, it’s usually better to split it up. For example, if your requirements call for an entirely different UI for each case, your presenter will have a lot more work to do to format an appropriate result back to the view controller which will display a very different view to the user.

Detailed error handling

If it’s appropriate in your app, you can add a nested FormattedOrder struct to contain the order details, error, and message in the view model for the CreateOrder use case, as in:

From personal experience, this technique is very useful in a large app, because you can standardize the errors and error handling across multiple scenes. In a large app, it’s very likely that you need to perform the same or similar use case in multiple scenes.

Refactor the displayCreatedOrder(viewModel:) method

The above approaches can be a bit over engineering when your app lies somewhere between simple and complex. If so, you can also consider breaking up the displayCreatedOrder(viewModel:) method into multiple methods with one-liners:

Giving these methods descriptive names and having them do as little as possible makes them more reusable. For example, in the same scene, you could have a button to refresh when tapped, pull to refresh, as well as a search with filters. They all can be fetching orders in different ways but hit the same API.

This should alleviate your concern about SRP. You can also read what I wrote about SRP some time ago at the class and method levels.

Did I answer your questions?

What specific struggles you experienced with MVC and MVVM that led you to Clean Swift? I’m interested in learning from your background to see how I may serve this audience better.

Ray,


Hi Raymond

Thank you for your detailed answer. Options number 1 and 2 are the most compelling to me and I’ll try to choose one of them (or combine them?) in my personal project as a test and see how will they work.

In my case the MVC pattern, when I was just starting to learn iOS development, was a classic case of Massive View Controller – fortunately the project I was working on was pretty small and easy to refactor later.

But after applying the MVVM pattern on a different project I had a feeling that all the unnecessary responsibilities from the View Controller was simply extracted to the new class – View Model – and that was it, leaving me with a kinda-massive View Model. Hence my quest to find and apply a better architecture for an upcoming project.

Łukasz,


Hi Łukasz,

Which option(s) you choose depend on your situation. I would just do the simplest thing first to avoid premature optimization at this point. You never fully know how your app will evolve.

I’ve certainly seen a lot of massive view models in MVVM, and massive presenters in VIPER. In Clean Swift, features are equally distributed among the view controller, interactor, and presenter. Complex business logic is delegated to workers, and you can easily have multiple workers each doing just one thing.

Ray,


If you have questions too, email me.

Raymond
I've been developing in iOS since the iPhone debuted, jumped on Swift when it was announced. Writing well-tested apps with a clean architecture has been my goal.

2 Comments

  1. Hi Raymond,

    I’m trying to apply this architecture to an project and I have a question. How do you handle “loading states”?

    I’m using the traditional MVC and I have an method that looks like this:

    Using the VIP cycle, what is the best way to interact with the view controller twice, once before the loading starts (so, I should call the startLoading()) and after when the asynchronous call is completed in the completion handler (So I should “do stuff” and stop the loading)?

    I was thinking that maybe one interactor method should call two different presenter methods (beforeFecth and afterFetch), but I don’t know if it’s the best way to do it.

Leave a Comment

Your email address will not be published. Required fields are marked *