ResourceState<Value>
A comprehensive state machine that combines loading and saving operations into a single unified type. Ideal for screens that need to fetch data and then allow edits.
Table of contents
State Diagram
Idle -> Loading -> Loaded -> Saving -> Saved
| | |
v v v
LoadFailure SaveFailure Saving (again)
States
Idle
Represents the idle state, before any operation has been initiated.
val idleState = ResourceState.Idle
Loading
Represents the loading state, indicating that data is currently being fetched.
val loadingState = ResourceState.Loading
Loaded<Value>
Represents the successful state of loading data.
val loadedState = ResourceState.Loaded("value")
LoadFailure
Represents the failure state of loading data, containing an exception.
val loadFailureState = ResourceState.LoadFailure(RuntimeException())
Saving<Value>
Represents the saving state, indicating that a mutation is currently being persisted. Tracks both original and updated values.
val savingState = ResourceState.Saving(original = "old", updated = "new")
// Convenience constructor when original == updated
val savingState = ResourceState.Saving("value")
Saved<Value>
Represents the successful state of saving data.
val savedState = ResourceState.Saved(original = "old", updated = "new")
// Convenience constructor when original == updated
val savedState = ResourceState.Saved("value")
SaveFailure<Value>
Represents the failure state of saving data, containing an exception.
val saveFailureState = ResourceState.SaveFailure(
original = "old",
updated = "new",
exception = RuntimeException()
)
// Convenience constructor when original == updated
val saveFailureState = ResourceState.SaveFailure(
value = "value",
exception = RuntimeException()
)
State Checks
All state check functions use Kotlin contracts for smart casting.
isIdle()
ResourceState.Idle.isIdle() // true
isLoading()
ResourceState.Loading.isLoading() // true
isLoaded()
ResourceState.Loaded("value").isLoaded() // true
isLoadFailure()
ResourceState.LoadFailure(RuntimeException()).isLoadFailure() // true
isSaving()
ResourceState.Saving(original = "old", updated = "new").isSaving() // true
isSaved()
ResourceState.Saved(original = "old", updated = "new").isSaved() // true
isSaveFailure()
ResourceState.SaveFailure(
original = "old", updated = "new", exception = RuntimeException()
).isSaveFailure() // true
Reactive Callbacks
onIdle
ResourceState.Idle.onIdle { println("Idle") }
onLoading
ResourceState.Loading.onLoading { println("Loading...") }
onLoaded
ResourceState.Loaded("value").onLoaded { value ->
println("Loaded: $value")
}
onLoadFailure
ResourceState.LoadFailure(RuntimeException("error")).onLoadFailure { exception ->
println("Load failed: ${exception.message}")
}
onSaving
ResourceState.Saving(original = "old", updated = "new").onSaving { original, updated ->
println("Saving: $original -> $updated")
}
onSaved
ResourceState.Saved(original = "old", updated = "new").onSaved { original, updated ->
println("Saved: $original -> $updated")
}
onSaveFailure
ResourceState.SaveFailure(
original = "old", updated = "new", exception = RuntimeException("error")
).onSaveFailure { original, updated, exception ->
println("Save failed: ${exception.message}")
}
Value Access
activeValue()
Returns the current truth based on the state. Returns null for states without data.
| State | Returns |
|---|---|
Idle | null |
Loading | null |
Loaded | value |
LoadFailure | null |
Saving | original |
Saved | updated |
SaveFailure | original |
ResourceState.Idle.activeValue() // null
ResourceState.Loaded("value").activeValue() // "value"
ResourceState.Saving(original = "old", updated = "new").activeValue() // "old"
ResourceState.Saved(original = "old", updated = "new").activeValue() // "new"
ResourceState.SaveFailure(original = "old", updated = "new", exception = RuntimeException()).activeValue() // "old"
attemptedValue()
Returns what was or is being changed to. Returns null for states without data.
| State | Returns |
|---|---|
Idle | null |
Loading | null |
Loaded | value |
LoadFailure | null |
Saving | updated |
Saved | updated |
SaveFailure | updated |
ResourceState.Idle.attemptedValue() // null
ResourceState.Loaded("value").attemptedValue() // "value"
ResourceState.Saving(original = "old", updated = "new").attemptedValue() // "new"
ResourceState.Saved(original = "old", updated = "new").attemptedValue() // "new"
ResourceState.SaveFailure(original = "old", updated = "new", exception = RuntimeException()).attemptedValue() // "new"
Exception Access
exceptionOrNull()
Returns the exception from any failure state (LoadFailure or SaveFailure), or null otherwise.
ResourceState.LoadFailure(RuntimeException()).exceptionOrNull() // RuntimeException
ResourceState.SaveFailure(original = "old", updated = "new", exception = RuntimeException()).exceptionOrNull() // RuntimeException
ResourceState.Loaded("value").exceptionOrNull() // null
Transformation
fold
Folds the state into a single value by providing handlers for each state.
val state: ResourceState<String> = ResourceState.Loaded("value")
val result = state.fold(
onIdle = { "Idle" },
onLoading = { "Loading..." },
onLoaded = { value -> "Loaded: $value" },
onLoadFailure = { exception -> "Load failed: ${exception.message}" },
onSaving = { original, updated -> "Saving: $original -> $updated" },
onSaved = { original, updated -> "Saved: $original -> $updated" },
onSaveFailure = { original, updated, exception -> "Save failed: ${exception.message}" }
)
// result = "Loaded: value"