Search code examples
androidandroid-databindingsealed-class

Resource wrapper sealed class, error with databinding


I'm stuck on a little issue using Resource wrapping around my data, I don't know how I can use it in my databinding.

Sealed class:

sealed class Resource<out T: Any> {
    data class Success<out T: Any>(val data: T): Resource<T>()
    data class Error(val exception: Throwable): Resource<Nothing>()
    object Loading: Resource<Nothing>()
}

I've got this val product: LiveData<Resource<NetworkProductDetails>>

<layout xmlns:android="http://schemas.android.com/apk/res/android">
    <data>
        <variable
            name="viewModel"
            type="my.package.ProductDetailsViewModel" />
    </data>

    <TextView
        android:id="@+id/product_name"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="@{viewModel.product.productName}"
        android:textAppearance="?attr/textAppearanceBody1"
        android:gravity="center"/>

    ...
</layout>

I've got an issue because viewModel.product is not a NetworkProductDetails but Resource<NetworkProductDetails> and my XML/Databinding doesn't know how to process it.


I've found a way to make work but I was wondering if there was a more elegant way.

First Solution:

<layout xmlns:android="http://schemas.android.com/apk/res/android">
    <data>
        <variable
            name="viewModel"
            type="my.package.ProductDetailsViewModel" />
        <variable
            name="product"
            type="my.package.NetworkProductDetails" />
    </data>

    <TextView
        android:id="@+id/product_name"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="@{product.productName}"
        android:textAppearance="?attr/textAppearanceBody1"
        android:gravity="center"/>

    ...
</layout>
viewModel.product.observe(viewLifecycleOwner, Observer { it ->
    when(it) {
        is Resource.Success -> {
            binding.product = it.data
        }
    }
}

Second Solution:

In the comment I made.


Solution

  • After looking around, there is 2 solutions I've found, the first is in my original post and the second is the one below:

    Creating a function in my sealed class to expose the data if it is Success and then be able to use it in my xml

    Pros:

    • No need to create a special BindingAdapter that could hide some of the logic
    • No need to add more code in every Fragment like in Solution 1

    Cons:

    • I feel that it breaks the point of having a wrapper
    sealed class Resource<out T: Any> {
        data class Success<out T: Any>(val data: T): Resource<T>()
        data class Error(val exception: Throwable): Resource<Nothing>()
        object Loading: Resource<Nothing>()
    
        fun toData(): T? = if(this is Success) this.data else null
    }
    
    <TextView
        android:id="@+id/product_name"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text='@{viewModel.product.toData().productName}'
        android:textAppearance="?attr/textAppearanceBody1"
        android:gravity="center"/>