Combinest Error Handling in Swift
Error handling provides us catching an error during an application lifecycle in the mobile development. When we develop the app by written imperatively in Swift such as
try-catch, Result failure case are significant to catch any error. In Combine, differently, it pushes us to handle catching an error at the beginning of the defining a property. Specifically,
Subjects have an extra generic error parameter by default, Failure.
Depending on our requirements, we are lucky that there are many options to handle any encountered error using
tryMap, replaceError, replaceEmpty, catch. In our example that we will focus on the standard error handling for error cases and we are going to discuss what if we use other options.
Let’s look this example that we have been focusing on loading any local JSON file for mocking all network requests in our app so we will create a protocol that relates the requirement of loading. This is also useful other local purposes like fetching any plist file but the main idea here is fetching all JSON files which we give
extension and an
Element type using
Before the conformation of LocalLoader to anywhere, for more testable code purpose, we need to define one more protocol so definitely more specific than LocalLoader, named
The whole operation should run on the background queue named
operationQueue to prevent UI locking and we just need a JSONDecoder named
We can easily define a class
JSONLoader right now, so we used class because of its reference but struct is also okay. It contains two generic type that we defined V and U. V represents type of our base object that we decode and U represents also type of an object for file name of the json file but the problem here is
Why are they same purpose and why we didn’t use one generic parameter instead of two?
Because, if we use nested generic typed class like
<BaseResponse<Response>>, it is tough to access
Response here, so we seperated them.
Let’s focus on the error handling aspect. Until now, we didn’t talk about the error handling much, we only created the base. We only defined
OperationError enum to specify it.
load() scope, there is a chain operations here and we created a Just publisher because of a constant element that we need to create the subscription. After that, we rescued from its optional value using
compactMap and we transformed it to URL using
map before converting it to data. Converting data process needs
tryMap we handled by throwing
Everything looks great. However, when we called
sink() I mean subscribe, to result of load(), we can’t see anything to show an error even if catch closure throws an error in tryMap. We have two problems here, the file name or extension is not compatible with existing JSON file, the data can’t be decoded due to inconsistency between existing JSON file and the data.
To accomplish these, we can use
replaceEmpty() after compactMap to handle an empty upstream by replacing a value with same type. We used an empty string for throwing cantLoadContent.
The second one was about our local JSON format. We may decode wrong depends on the corresponding key so we can use
Fail publisher after decode() is called to specify throwing error.
That’s all for now. We handled any error using publishers for our case.
Thanks for reading!