Like, Follow, Subscribe, Favorite - How-to

Marcin Adamczewski
Appunite Labs
Published in
5 min readJun 19, 2017

There are plenty of apps with such sort of actions. It means that this is quite a common thing to do for a developer. In this article, I’d like to tell you about common mistakes and propose a way of implementing such a feature.

To visualize the problem imagine two Activities:
1. PostsList Activity with each cell containing the title and a star icon on the right, indicating if this post is favorite or not,
2. PostDetails Activity with details about the post and button with a star icon indicating if this post is favorite or not and allowing to make post favorite.

At the beginning I’d like to tell you about the common mistakes that I’ve seen people do:

a) Relying on the current view state e.g. something like this:

The problem here is that the button state is set based on the previous view state only. This is not good because request may fail which will result in a wrong view state. For example, after the user have clicked a fav button you’ve set the view state as not favorite and made an unfavorite request which unfortunately failed. Now the button icon claims that post is not favorite, but in fact, it is. Basically you should always invalidate view state depending on API response.
Another thing that is a little bit risky is deciding whether you should make favorite or unfavorite request based on view state -favoriteButton.isFavorite(). Basically everything is fine as long as you can ensure that a view has always correct state, but it may require some additional work if your code logic is not too robust. For example, you would need to save view state across activity configuration change.
Another case is that the view should only display some state and not decide about business logic. A view should not be the source of truth and should not provide information on whether a post is favorite or not.

b) The whole app is not aware of state change.
People often change some state in one Activity, but other Activities on the back stack that also display this state are not aware of that. For example on Post Details screen user has just added a post to favorites. Then he goes back to the list of posts where the favorite icon has the wrong state. I’ve seen it many times in different apps, even in the current version of the YouTube app. So when you add something to favorites you should inform the rest of interested activities about the result so they can also change the state of its views. Of course, you could pass the value back in onActivityResult() method but it is a painful and not scalable solution. My suggestion is to create a singleton class that manages all favorites (more about it later).

c) Letting users spam the API by fast, repeatedly clicking the button.
Our QA team's favorite play is very fast clicking on every button of an app. When clicking a button triggers some API request, it may be also a lot of fun for our API and mobile app. That’s why it is good to prevent such behavior. You can do it very simply using RxJava throttleFirst operator (more about it in example).

Now I’d like to propose a way to prevent the above mistakes. Before I’ll paste some code I’m going to write down the flow:

  1. The user opens the app.
  2. Request for all favorites is made in the background and the response is saved in a singleton class called FavoritesManager. FavoritesManager is a class that always has the current state of all favorites and provides Observables/Listeners informing every interested Activity about specific post state. In other words, it informs if a specific post is favorite or not.
  3. Each interested Activity, Fragment, or ViewHolder is ‘subscribed’ to this Observable/Listener so it can react on each favorite state change.
  4. Whenever the user clicks favorite button following steps are made:
    a) Toggle favorite state of a specific post in FavoritesManager. This will result in notifying views through Observable/Listener about state change,
    b)
    Based on previous value from FavoritesManager, the adequate request is made, e.g. if the previous value of favorite state was TRUE then we make an unfavorite request otherwise a favorite request,
    c) If we receive an error response from API we should change back the value in FavoritesManager.

It’s time to look at some code now.

Firstly I’ll show you how little code you have to add to your views. In every Activity, Fragment or ViewHolder in order to set correct favorite icon state you just have to do the following:

To handle button clicks you just have to do this:

And that’s all. Very simple, isn’t it?

Let’s now take a look at the most important part which is FavoritesManager (or FavoritesDao).
This class is responsible for three things:

  • fetching all favorites from API and saving them in special Cache
  • managing Cache: updating single entry or invalidating the whole cache
  • providing Observable/Listener of favorite state of each post

Why is this cache so special? Let’s look at its definition:

This cache is simply a Map with post id as a key and BehaviorSubject<Boolean> as a value. This cache takes CacheProvider argument which simply creates default Map value when you try to access an entry that doesn’t exist. This is very similar to LoadingCache class from Guava.

If you are not familiar with RxJava you can think of BehaviorSubject as of a class that is able to store a value, update it and notify about this value change everyone that is subscribed to it. I think Android LiveData class is very similar to BehaviorSubject.

As we said before on the app start we want to fill up the Cache with favorites values. We do it with the following code in our Application class:

So firstly we make an API request and then fill up the cache. What is important here is that updating single cache entry notifies all subscribers about value changes through BehaviourSubject. Let’s take a look at this:

As you can see to update a single cache value and notify everyone about that, we simply need to get a value from the cache and call .onNext(favorite) on returned BehaviourSubject. Just a single line of code and so much happens.

Now let’s take a look at the addOrRemoveFromFavs() method that is called when the user clicks on fav/like button.

In the beginning, we fetch the current post state from the cache. Then, in order to quickly change UI state (e.g. star icon) we call updateFavorite() method with negated current value. So if the post was favorite we set cache value as not favorite. Then we make an API request, calling addOrRemoveFromFavs() method. If the request fails we have to change back cache value by calling updateFavorite() method with the previous value.

What is worth mentioning is that we often have to display the number of all favorites or likes, e.g. in profile view. With such an implementation it is very easy to manage that count value.

Basically that’s all. If you want to check out the working example and take a closer look at some methods please go here -> https://github.com/marcin-adamczewski/LikeFollowSubscribeFavorite.

NOTE: This solution is only an example and may not fit all cases. I’ve also focused on simplicity so you would probably like to move some code to presenters, etc.

--

--