Updating UI with API result handling [part 2]

Paweł S
4 min readJun 26, 2022

--

This is part two of my series, where we try to improve the user experience by better handling of API calls. It also simplifies the development process, introducing generic mechanisms.

If you haven't read my previous post, you can check it out here. In this article, we will be referring to it.

Start using the database

Currently, our app uses the database to save the data, nothing more. This is wasteful. Initially we can show users the data that was already saved, while at the same time load new data from api. If the user does not have an internet connection, we can present some data, instead of empty screen. It may be enough what they need, like list of favourite movies.

The first idea that we may have is to:

  • Return current content of DB
  • Load and save new data
  • Return updated content from DB

It will work, but it is not the best solution. We would need to coordinate a few method calls to achieve this goal. The better one is to observe the database. Most of DB ORMs support Streams. When using streams our data in the UI will always be up to date. We can imagine a scenario where we want to add a movie to our favorite list. We press the ⭐️, and the list already shows the new state, while we only called method to update a single record. Nothing more. If we used the old technique we would also need to call a method that will update the list. For more operations we can picture how our code will become bloated with update list calls. Working with streams makes our code simpler and reactive.

How our dao will look depends on the library we use, so we’ll skip that. In the repository, we need to add new methods that will map database models into view ones.

Then we create use-cases and implement the connection to the UI which will listen to those streams.

Simple change, but such a great improvement.

Updating loading data in repositories

Until now, we loaded data from API, saved it in the database, and finally returned the list to the UI. This process took quite some time, and the user had to wait a moment to see anything on the screen. Now, we are observing the data, so the results on the screen are immediate (as long as some were loaded earlier). This means that our loading method in the repository does not have to return the list of data to be shown. In the last article, we focused on handling the API result, but we did not inform the user of any of it. Today we will harvest on the results of the hard work we put in last time. As you may remember we created an abstract class Response that is returned from all of our API calls. When the call succeeds or fails we return the derived class from SuccessfulResponse or ErrorResponse respectively. Handling successful responses was already done in the previous article, we will focus on handling the errors this time.

All operations performed in the application, just like in math, end with a result. Let’s create the Result base class, which will be equivalent to the Response class used in API calls.

As we can deduce from the code snippet there are two types of Result classes: successful, and error. We also created an additional DataResult class if we would like to return some data that is not saved in the database or for another reason. Those two types are pretty simple to understand and use. When we are returning ErrorResult , we also want to return the cause of this error, so that we can inform the user what went wrong. For that, we created a ErrorCauseclass. It is created with the use of freezed so that we can use the when method to handle all different types of errors.

Now, we are in a good position to update our loading methods from the repository. Currently, they return Future<List<ContentDetailData>> , as we mentioned before we don't need to return the data to the UI, now we only care about errors, so we can return Future<Result>.

As we can see, the change was a simple one, but this time we also improved the user experience by returning the cause of the error that we can display using snackbars.

Now, our code handling the result may look like this

As we can see, we can make simple changes to our code to improve the code quality and user experience significantly. There is one more thing that we can do to build better UX. About that we will talk in the next article.

--

--

Paweł S

Passionate Android/Flutter developer that strives for perfection, which can never be achieved.