Consuming Activity Results using coroutines: Part 2
Published: 2021-12-07 11:21:00

This is the second post of a 2 parts article on how we can convert and consume Activity Results using coroutines.
In the [first part](https://dev.to/hichamboushaba/consuming-activity-results-using-coroutines-part-1-2j57), we were able to create a [component](https://gist.github.com/hichamboushaba/440939bf85300b1fe634580d19bf018d#file-permissionmanager-kt) that allowed us to convert the Activity Result's contract [RequestMultiplePermissions](https://developer.android.com/reference/androidx/activity/result/contract/ActivityResultContracts.RequestMultiplePermissions) to a Coroutine, while still able to handle Activity Recreation and process-death scenarios.

## Generalize our code for all Activity Results
When reviewing the code we had for our PermissionManager class, the only specific parts related to permissions were:
1. The instantiation of `RequestMultiplePermissions`.
2. The mapping of the result to our defined sealed class.
3. Saving the permissions' list to the saved state, and using it as the flag for deciding if there is any pending operation.

So for designing our component that can be used to consume all Activity Results, we just need to generalize or remove the above points.
The first two points are straight forward, as we are aiming for a generic component here, we'll just remove them, so we'll pass the instance of our `ActivityResultContract` to our function, and we'll make it return the same type as defined by the contract. which means having a signature for our main function as this:
```
suspend fun <I, O, C : ActivityResultContract<I, O>> requestResult(
        contract: C,
        input: I
    ): O?
```
For the third point, we have many ways for handling it:
1. Using a simple boolean to tell us if there is any pending operation.
This would work for most cases, but it has one downside, if a process-death occurs, and in the ViewModel's side we don't handle it, our component will still think that there is a pending operation, which means if we request a different type of results, it'll get stuck.
2. By saving the contract's class name, and comparing against it, this is a simple way for doing it, and would work for the majority of cases, although it may still break on a very specific case: if the ViewModel doesn't handle process-death, and we request another result using the same type of Contract, but using a different input, we'll probably return the wrong result.
3. By saving both the contract's class name and the input, and while this the ultimate solution, the comparison of the input against a saved value won't be straight forward, since it's a generic type, and we don't know if it has a valid `equals` implementation or not.

I opted for the option two, since the failure scenario is a very far corner case, and when using the SavedStateHandle correctly to save state, it won't occur at all, let me know what you think about this choice in the comments.

With all of this, we can have our component now:
{% gist https://gist.github.com/hichamboushaba/018639d44339955b2fe9d48064b39ae5 %} 

## Meet the library SuspendActivityResult
After starting the article, I decided to publish this component as a library for anyone interested, it's available in the same [repo](https://github.com/hichamboushaba/SuspendActivityResult).
The Activity Result can be requested either using the low level API:
```kotlin
val uri = ActivityResultManager.getInstance().requestResult(
    contract = GetContent(),
    input = "image/*"
)
```
or using the extensions for the built-in [ActivityResultContracts](https://developer.android.com/reference/kotlin/androidx/activity/result/contract/ActivityResultContracts)
```kotlin
val uri = ActivityResultManager.getInstance().getContent("image/*")
```

The [example](https://github.com/hichamboushaba/SuspendActivityResult/tree/master/app) app has samples for using it for permissions and  other activity results, and it has a sample for using it for a custom `ActivityResultContract`.

## Conclusion
If you have any remarks or questions regarding the code or the API, please drop them in the comments section, I hope the library can be useful to some people.