Consume Activity result directly in the ViewModel
Tags: android, kotlin
Published: 2020-03-31 09:35:24

---
title: Consume Activity result directly in the ViewModel
published: true
description: Transfer responsibility of parsing result using delegation pattern and receive activity result directly in the View.  
tags: android, kotlin
---
Most common implementation of Delegation Pattern is when the responsibility of the class is delegated to a real object of a concrete implementation of the same interface. 
```java
class HomeAcrobat implements Acrobat {
  private final Acrobat mProAcrobat = new CircusAcrobat();
  @Override
  public void doAmazingStuff(){
     mProAcrobat.doAmazingStuff();
  } 
}

```

Kotlin gives amazing and neat way to implement Delegation pattern using the keyword `by`. This gives impression of having multiple inheritance, it is still a polymorphisms in a sense. 

In `activity-ktx:1.3.+`, requesting and receiving results where taken to a new level with introduction of `ActivityResultContract<I, O>` and `ActivityResultCallback`. `ActivityResultContract<I, O>` is an interface that performs how and who will get the request for result, and `ActivityResultCallback` is how the consumer will consume the result.

But Google left us with `ActivityResultContract` abstract class which is the only obstacle we have to jump. 

Consider we are advocates of separation of concerns and we want to take take the parsing of the result in another class.

```kotlin
@FragmentScope
class ChooseFilterContractor @Inject constructor(private val context: Context) : ActivityResultContractor<Unit, String> {
    override fun createIntent(input: Unit): Intent {
        return Intent(context, ChooseFilterActivity::class.java)
    }

    override fun parseResult(resultCode: Int, intent: Intent?): String {
        return if (resultCode == Activity.RESULT_OK) {
            intent?.getStringExtra(KEY_FILTER)?: error("Dispatched RESULT_OK, but  no payload)
        } else {
            ""
        }
    }
}
```

You may notice the interface `ActivityResultContractor<I, O>`, this is extracted interface from `ActivityResultContract`.


With a minor tweak to the `prepareCall()` function, we can wrap our contractor to work as library provided `ActivityResultContract`.

```kotlin
fun <I, O> prepareCall(
        activityResultContractor: ActivityResultContractor<I, O>,
        activityResultCallback: ActivityResultCallback<O>
    ): ActivityResultLauncher<I> {

        return prepareCall(object : ActivityResultContract<I, O>() {
            override fun createIntent(input: I): Intent = activityResultContractor.createIntent(input)

            override fun parseResult(resultCode: Int, intent: Intent?): O =
                activityResultContractor.parseResult(resultCode, intent)
        }, activityResultCallback)
    }

```
This function will be called from the `Fragment` or `Activity`, since they are only able to launch another `Activity`. 

Our goal is to make the ViewModel be able to parse and receive the result. You may notice that now the `ProductViewModel` also acts like a contractor but the parsing of the result is delegated to the `activityResultContractor` using this syntax  `ActivityResultContractor<Unit, String> **by** activityResultContractor`.

```kotlin
class ProductsViewModel @Inject constructor(
    private val activityResultContractor: ActivityResultContractor<Nothing, String>,
    private val coroutineProvider: CoroutineProvider,
    private val productInteractor: ProductInteractor
) : ViewModel(), ActivityResultCallback<String>,
 ActivityResultContractor<Unit, String> by activityResultContractor{

     override fun onActivityResult(result: String?) {
        // do something with the result
    }

}
```

Final call to the tweaked `prepareCall()` would be:

```kotlin
      chooseProductFilter.setOnClickListener {
          prepareCall(productViewModel, productViewModel).launch(Unit)
      }
```


Another example of calling `launch`, when the input type is not `Unit`:

```kotlin
      textLiveData.observe(viewLifecycle){
          prepareCall(productViewModel, productViewModel).launch(it)
      }
```