Android Presentation Patterns: MVP
Tags: android, kotlin, programming, mvp
Published: 2024-02-24 17:48:44

## But MVC does the job!

When examining the **MVC pattern** [as described here](https://asvid.github.io/2024-01-27-MVC), it's evident that the direct relationship between the `View` and `Controller` is problematic. Tight coupling results in challenging testing, where changes in one class necessitate updates in another, and it doesn't scale well. In complex views, there can be multiple conditions for setting values in UI elements, depending on the current state of the UI and responses from the `Model`. Dealing with complex logic when testing becomes challenging... **leads to anarchy**.

This issue was apparent to developers, prompting some to propose better ways to implement MVC, as discussed [here](https://medium.com/upday-devs/android-architecture-patterns-part-1-model-view-controller-3baecef5f2b6).
In this approach, the `Controller` is a separate class from the `Fragment` or `Activity`. Since it's an independent
object from Android Framework instances, interfaces can be employed to mock components and facilitate testing.

## The Presenter is born

The evolution of **MVP**, as known in the context of Android development, can be traced back to Google's Web Toolkit (GWT). In the mid-2000s, GWT adopted MVP as a response to the challenges faced in building modular and testable web
applications.

Let's have an interface for the `View` and the new `Controller` type, but let's call it `Presenter` since it's
responsible for presenting model data to the `View`. A simple diagram may look like this:


![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/7q25fo9vvkxtj1s2x4qi.png)

Please note that `View` and `Presenter` are interfaces, and while they still keep a reference to each other, it's only
by the interface, not the concrete implementation. The interfaces are providing only methods relevant for particular UI interactions.

### Let there be Contract!

Okay, so now there is a set of interfaces, implemented by a concrete `View` and `Presenter`, to serve for a particular
UI screen. It would be nice to keep them close, so it's clear which `View` talks to which `Presenter`, there is always a
1-to-1 relation.


![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/wj4cqlx04ivcemi9l6q0.png)

It looks nicer in Kotlin code:

```kotlin
interface ItemListContract {
    interface View {
        fun displayError(errorMessage: String)
        fun hideError()
        fun displayItems(items: List<Int>)
        fun showLoading()
        fun hideLoading()
    }

    interface Presenter {
        fun addItem()
        fun removeItem()
    }
}
```

The Activity being the `View` will look like this:

```kotlin
class MainActivity : AppCompatActivity(), ItemListContract.View {
    // creating instance of the presenter, can be also injected by DI
    private val presenter: ItemListContract.Presenter = MainViewPresenter()

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        // giving Presenter instance of the View
        // this should happen when the View is actually ready to perform its tasks
        // because Presenter may want to display something right after getting reference to the View
        presenter.attachView(this)
        ...
```

The `Presenter`:

```kotlin
class MainViewPresenter : ItemListContract.Presenter {
    private lateinit var view: ItemListContract.View

    fun attachView(view: ItemListContract.View) {
        this.view = view
    }
}
```

No magic here, simple passing a reference to `this` from `View` to `Presenter`, and from now on, the `Presenter` should be safe to call `View` methods. This is why it should happen in the `Activity` or `Fragment` `onCreate` and not in the `Presenter` constructor. Otherwise, you would have to call some extra `onViewReady()` on the `Presenter` when you have your view prepared, with all elements assigned to fields with `findViewById()` etc.

The `View` and the `Presenter` know only each other through the `Contract`, so it should be nice to write `Presenter`
tests, with mocking of the `View` and verifying which methods were called and passed arguments.

## The boilerplate

You probably already identified the ugly part now. Each presenter has a method `attachView` or similar, but it's not
really part of any common interface for `Presenter`. It would be really great to have some sort of framework, taking
care of all the boilerplate methods for `View` and `Presenter`, so `Contract` can focus only on relevant methods for a particular UI scenario.

The `View` and `Presenter` couple should also be detached to avoid memory leaks when the view is destroyed.

All `Presenter` work is done on the UI thread. It would be great to perform tasks on other threads, and cancel them when
the View is destroyed since the result it's not relevant anymore.

This can be achieved by introducing an abstract `BasePresenter`, which is also a `CoroutineScope`. The diagram starts to be really complex, for a simple presentation layer architecture:

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/3r7qreeqnszmg835p6rh.png)

### BasePresenter

It looks more complex than it is. Each `ConcretePresenter` implements `Contract.Presenter` for its domain capabilities
and `BasePresenter` for common methods like `attachView()` or `detachView()`.
The `BasePresenter` provides `CoroutineContext`, so running methods on the Model won't happen on the UI thread, and jobs
will be canceled when the view is detached.

Kotlin implementation:

```kotlin
abstract class BasePresenter<VIEW : View> : Presenter<VIEW>, CoroutineScope {
    internal lateinit var view: VIEW

    private var job: Job = SupervisorJob()

    override val coroutineContext: CoroutineContext
        get() = Dispatchers.IO + job

    override fun attachView(view: VIEW) {
        this.view = view
        onViewAttached()
    }

    override fun detachView() {
        job.cancel()
        onViewDetached()
    }

    abstract fun onViewAttached()
    abstract fun onViewDetached()
}
```

Because the jobs are performed outside the UI thread, and only the UI thread can modify UI elements, the `View` methods
implementation has to use something like this:

```kotlin
override fun displayLoading() {
    runOnUiThread {
        progressBar.visibility = View.VISIBLE
    }
}
```

### View and Presenter interfaces

There are also new interfaces for generic `View` and `Presenter`.

```kotlin
interface Presenter<VIEW : View> {
    fun attachView(view: VIEW)
    fun detachView()
}

interface View

// Contract
interface Contract {
    interface ConcreteView : View

    interface ConcretePresenter : Presenter<ConcreteView>
}
```

Using generics guarantees everything is connected in the right way. The `MainViewPresenter` now has to inherit from both the contract presenter interface and the `BasePresenter`. It's not ideal but allows keeping behavior relevant to the contract in a separate place from behavior relevant to the presenter base functionality.

```kotlin
class MainViewPresenter : Contract.ConcretePresenter, BasePresenter<Contract.ConcreteView>()
```

The `ConcretePresenter` and `BasePresenter<Contract.ConcreteView>` have the same `ConcreteView` view, so it all works nicely. This way, the `View` doesn't have to know about the `BasePresenter` interface to use `attachView()` and `detachView()`. Those methods are part of the `Presenter` interface that `ConcretePresenter` inherits. `BasePresenter` also inherits this interface, and it provides the actual implementation for assigning the `View` reference and handling job cancellation. Still, it gives `ConcretePresenter` implementation a way to perform some actions when the view is attached with the

```kotlin
abstract fun onViewAttached()
abstract fun onViewDetached()
```

So with a relatively small cost of remembering to always inherit from `BasePresenter`, we get nice separation of
behaviors, and our `Contract` can focus on UI handling, rather than technical details of threading etc.

There were no changes for the `View` implementation; the `ConcretePresenter` interface has all the relevant methods.

### Presenter implementation

Let's look into `MainViewPresenter` code:

```kotlin
override fun removeItem(item: Int) {
    launch {
        view.displayLoading()
        Model.removeItem(item)
            .onFailure { error ->
                view.hideLoading()
                displayErrorMessage(error)
            }
            .onSuccess {
                view.hideLoading()
            }
    }
}
```

Every method from `ConcretePresenter` looks similar. It's wrapped in a `launch` block because the `BasePresenter` is a
`CoroutineScope`, and it sets the context in its implementation:

```kotlin
private var job: Job = SupervisorJob()
override val coroutineContext: CoroutineContext
get() = Dispatchers.IO + job
```

The Job is canceled when the view is detached. In my implementation, the `Model` returns a Kotlin `Result`, so I
can handle errors nicely with the `onFailure()` extension method.
The `removeItem()` method tells the `View` to display some loading. Some, because it's up to the `View` implementation
on how to do this. The `Presenter` has no idea if it's a `LinearProgressIndicator` or `CircularProgressIndicator`, or maybe a Dialog with text "please wait". Thanks to this, the whole UI can even get redesigned with very little impact on
the `Presenter`.
The `Controller` typically had to know much more about `View` details.
The same goes for `displayErrorMessage()`. We can define what data is passed to `View` in the `Contract`, and we don't
have to rely on any Android Framework limitations here.

After an item is successfully removed, the `Presenter` is just hiding the loading. What about refreshing data? Well, it
happens automatically:

```kotlin
override fun onViewAttached() {
    launch {
        Model.itemsFlow.collect { items ->
            view.displayItems(items)
        }
    }
}
```

The `Presenter` is observing the items' flow from the Model and updating the `View` with each new change. The
old-fashioned way of achieving this would be to get new data from the Model after removing an item and passing it to the `View`.

## Common problems of MVP

While Model-View-Presenter (MVP) offers several advantages over MVC, it's important to be aware of some common issues
associated with this architectural pattern.

1. **Boilerplate Code:**
    - Each component (Model, View, and Presenter) requires its own set of interfaces and implementations, leading to verbose code.
2. **Complexity for Simple UIs:**
    - For simple user interfaces, the introduction of MVP may seem like an overhead. In cases where the UI logic is straightforward, the additional layers introduced by MVP might be considered unnecessary.
3. **Learning Curve:**
    - Developers who are new to MVP may find it challenging to understand the separation of concerns and the proper       communication flow between the components. This can result in a steeper learning curve compared to simpler patterns.
4. **Presenter Responsibilities:**
    - Determining what should go into the Presenter and what should stay in the View or Model is not always straightforward. Deciding on the proper distribution of responsibilities can lead to ambiguity and inconsistencies across implementations.
5. **Tight Coupling:**
    - While MVP aims to reduce coupling compared to MVC, there's still a potential for tight coupling between the View and Presenter, especially if not implemented carefully. This can make it harder to replace or modify one component without affecting the other.
6. **Difficulty in Lifecycle Management:**
    - Managing the lifecycle of components in MVP, especially in Android where the activity and fragment lifecycles are critical, can be challenging. Improper management may result in memory leaks or unexpected behavior.
7. **No standardization:**
    - Unlike some other patterns, there is no strict standardization for MVP, leading to variations in implementations. This lack of a standard can make it challenging for developers to move between projects seamlessly.

It's important to note that the issues mentioned above are not inherent flaws in MVP but rather considerations and
trade-offs. The appropriateness of MVP depends on the specific needs of the project and the **preferences and experience of the development team**. Additionally, some of these issues can be mitigated with proper design practices and libraries that support MVP, such as dependency injection frameworks.

## Conclusion

MVP is a step in the right direction from MVC. It's also not that hard to actually refactor from MVC to MVP. Nowadays,
we can **benefit from using coroutines, flows, or `Result` class** to avoid callback hell and easy threading. The
pattern provides clear(er) separation of concerns. It's finally easy to test code between Model and the View.

The MVP pattern version I showed in this post requires a bit of boilerplate, even for low complexity UI. But at the same
time, a lot of technical issues are hidden, and developers may focus solely on the `Contract`.

Even now when I look at the extended MVP diagram, it looks a bit scary. There are a lot of interfaces, and it may look
like an overkill. If only there was a way to achieve similar separation of concerns, testability, and ease of use... oh
wait, there is **MVVM** :)