Skip to main content

Resource<T>

Sealed class wrapping async operation results into three states: Success, Error, and Loading.

Module: :core

Definition

sealed class Resource<T> {
class Success<T>(val data: T) : Resource<T>()
class Error<T>(
val exception: AppException,
val data: T? = null
) : Resource<T>()
class Loading<T>(val data: T? = null) : Resource<T>()
}

State Checks

resource.isSuccess()  // Boolean
resource.isError() // Boolean
resource.isLoading() // Boolean

Callbacks

Chain callbacks for each state:

resource
.onSuccess { data -> /* handle success */ }
.onError { exception, partialData -> /* handle error */ }
.onLoading { partialData -> /* handle loading */ }

Each callback returns this, so they can be chained.

Transformations

map

Transform the success data:

val userResource: Resource<User> = api.getUser(1)
val nameResource: Resource<String> = userResource.map { it.name }

flatMap

Chain dependent async operations:

val profileResource: Resource<Profile> = userResource.flatMap { user ->
profileApi.getProfile(user.id)
}

Data Retrieval

MethodReturnBehavior
getOrNull()T?Returns data on success, null otherwise
getOrDefault(default)TReturns data on success, default otherwise
getOrThrow()TReturns data on success, throws on error/loading

Flow Extension

Convert a plain Flow<T> into Flow<Resource<T>>:

val flow: Flow<List<User>> = userDao.getAll()
val resourceFlow: Flow<Resource<List<User>>> = flow.asResource()

This emits Resource.Loading() first, then Resource.Success(data) for each emission, and Resource.Error(exception) on failure.

Usage with BaseViewModel

Resource<T> is the fundamental return type used across the SDK:

// Repository returns Resource
interface UserRepository {
suspend fun getUsers(): Resource<List<User>>
}

// ViewModel consumes Resource
fun loadUsers() = collectResource(_users) { repository.getUsers() }

// UI renders Resource
MiruResourceView(resource = usersResource) { users ->
UserList(users)
}