Best practice: Error management in Combine Framework when there are different Error types in the stream

Viewed 131

I am a beginner in iOS development, and trying to follow a best practice for error management in Combine framework for some use case when working with MVVM pattern in your project.

So let’s imagine a use case.
We have repository that has several methods, each try different sources to find some data.
So, we have some data that can be found in different sources, let's say sourceA, sourceB and sourceC, etc... and each source can produce its own Error type errorA, errorB, errorC, etc...

Repository.methodX

  • try sourceA -> can produce errorA
  • try sourceB -> can produce errorB
  • try sourceC -> can produce errorC

Repository.methodY

  • try sourceA -> can produce errorA
  • try sourceD -> can produce errorD
  • try sourceC -> can produce errorC

Repository.methodZ

  • ... and so on

So in the repository .. we can at the end of the stream make mapError to some kind of general error type.
So this kind of general error type, what it should be? a one big error type for all methods in the repository? .. or an small error type for each method?

How to think about it .. I am asking so I can learn how to accomplish error management in Combine in a clean way.

NOTE: Picture included with the style I follow in MVVM pattern https://developer.android.com/topic/libraries/architecture/images/final-architecture.png

1 Answers

Generally speaking, errorA makes sense for the types of things that sourceA lets YOU accomplish. errorB, similarly, makes sense for the types of things that sourceB lets YOU accomplish. In each case the developer of the source has crafted a part of their interface to tailor the error reporting for tasks that you need to get done.

You are creating software that lets someone else to use your code to complete a task. You should catch the errors from other services that you use as implementation details and translate them into errors that are meaningful for the task that your USERS are trying to get done.

For example, suppose you are implementing a feature to download and scale an image. sourceA downloads information from the network. sourceB scales an image. sourceA can send an error if the network is down. sourceB can throw an error if scaling the image takes too much memory. You should catch those errors and translate them into errors that are meaningful for your user.

enum ImageScaleServiceErrors {
    case couldNotRetrieveImage(Error)
    case couldNotScaleImage(Error)
}

(I typed that into StackOverflow so it might not compile).

In both of the cases for my image scaling service I have re-cast the errors into the language of my scaling service and the task the user is trying to accomplish with my code. I can include the lower level errors in the associated values for my error cases if I think the person receiving my errors can benefit from knowing the details, but in general, my code should be returning errors that make sense for the operations my code lets my users perform.

In terms of Combine, the mapError operator can help a lot here.

Related