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

  1. State Diagram
  2. States
    1. Idle
    2. Loading
    3. Loaded<Value>
    4. LoadFailure
    5. Saving<Value>
    6. Saved<Value>
    7. SaveFailure<Value>
  3. State Checks
    1. isIdle()
    2. isLoading()
    3. isLoaded()
    4. isLoadFailure()
    5. isSaving()
    6. isSaved()
    7. isSaveFailure()
  4. Reactive Callbacks
    1. onIdle
    2. onLoading
    3. onLoaded
    4. onLoadFailure
    5. onSaving
    6. onSaved
    7. onSaveFailure
  5. Value Access
    1. activeValue()
    2. attemptedValue()
  6. Exception Access
    1. exceptionOrNull()
  7. Transformation
    1. fold

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"

This site uses Just the Docs, a documentation theme for Jekyll.