Compose Multiplatform runtime library for iOS UIKit x64 target providing core runtime functionality for declarative UI framework integration.
—
Low-level snapshot-based state management providing efficient change tracking and transactional state updates. The snapshot system is the foundation for Compose's state management and enables optimizations like structural sharing and conflict resolution.
Core classes for managing snapshots of mutable state.
/**
* Abstract base class for snapshots representing a consistent view of mutable state
*/
abstract class Snapshot {
/** Unique identifier for this snapshot */
abstract val id: Int
/** Whether this snapshot is invalid due to conflicts */
abstract val invalid: Boolean
/** Set of objects that were read during this snapshot */
abstract val readSet: Set<Any>
/** Set of objects that were modified during this snapshot */
abstract val writeSet: Set<Any>
/** Disposes this snapshot and releases resources */
abstract fun dispose()
companion object {
/** The global snapshot that all state changes are applied to */
val global: MutableSnapshot
/** The current snapshot for the current thread */
val current: Snapshot
/**
* Takes an immutable snapshot of the current state
* @param readObserver Optional observer for state reads
* @return Immutable snapshot
*/
fun takeSnapshot(readObserver: ((Any) -> Unit)? = null): Snapshot
/**
* Takes a mutable snapshot for transactional state changes
* @param readObserver Optional observer for state reads
* @param writeObserver Optional observer for state writes
* @return Mutable snapshot for making changes
*/
fun takeMutableSnapshot(
readObserver: ((Any) -> Unit)? = null,
writeObserver: ((Any) -> Unit)? = null
): MutableSnapshot
/**
* Runs a block with the given snapshot as current
* @param snapshot Snapshot to make current
* @param block Code to execute with the snapshot
* @return Result of the block
*/
fun <T> withSnapshot(snapshot: Snapshot, block: () -> T): T
/**
* Runs a block with a mutable snapshot
* @param readObserver Optional observer for state reads
* @param writeObserver Optional observer for state writes
* @param block Code to execute in the snapshot
* @return Result of the block
*/
fun <T> withMutableSnapshot(
readObserver: ((Any) -> Unit)? = null,
writeObserver: ((Any) -> Unit)? = null,
block: () -> T
): T
/**
* Sends and applies all pending snapshot changes
*/
fun sendApplyNotifications()
}
}
/**
* Mutable snapshot that can be modified and applied
*/
abstract class MutableSnapshot : Snapshot() {
/** Whether changes have been made to this snapshot */
abstract val modified: Boolean
/**
* Applies this snapshot's changes to the global state
* @return Result indicating success or failure
*/
abstract fun apply(): SnapshotApplyResult
/**
* Creates a nested mutable snapshot
* @param readObserver Optional observer for state reads
* @param writeObserver Optional observer for state writes
* @return Nested mutable snapshot
*/
abstract fun takeNestedMutableSnapshot(
readObserver: ((Any) -> Unit)? = null,
writeObserver: ((Any) -> Unit)? = null
): MutableSnapshot
/**
* Creates a nested immutable snapshot
* @param readObserver Optional observer for state reads
* @return Nested immutable snapshot
*/
abstract fun takeNestedSnapshot(readObserver: ((Any) -> Unit)? = null): Snapshot
}Results of applying snapshot changes.
/**
* Result of applying a snapshot
*/
sealed class SnapshotApplyResult {
/** Application succeeded */
object Success : SnapshotApplyResult()
/** Application failed due to conflicts */
class Failure(val snapshot: Snapshot) : SnapshotApplyResult()
}Usage Examples:
// Basic snapshot usage
fun snapshotExample() {
val snapshot = Snapshot.takeMutableSnapshot()
val result = snapshot.apply {
// Make changes to state within the snapshot
someState.value = "new value"
otherState.value = "other new value"
// Apply all changes atomically
apply()
}
when (result) {
is SnapshotApplyResult.Success -> {
println("Changes applied successfully")
}
is SnapshotApplyResult.Failure -> {
println("Changes failed to apply due to conflicts")
}
}
snapshot.dispose()
}Functions for working with the global snapshot state.
/**
* Runs block with global state write observation
* @param writeObserver Function called when global state is written
* @param block Code to execute with observation
* @return Result of the block
*/
fun <T> Snapshot.Companion.withoutReadObservation(block: () -> T): T
/**
* Registers a global state write observer
* @param observer Function called when any global state changes
* @return Handle to remove the observer
*/
fun Snapshot.Companion.registerGlobalWriteObserver(
observer: (Any) -> Unit
): ObserverHandle
/**
* Registers a global apply observer
* @param observer Function called when snapshots are applied
* @return Handle to remove the observer
*/
fun Snapshot.Companion.registerApplyObserver(
observer: (Set<Any>, Snapshot) -> Unit
): ObserverHandle
/**
* Handle for removing observers
*/
interface ObserverHandle {
/** Removes the observer */
fun dispose()
}Convert state reads into coroutine Flows that emit when dependencies change.
/**
* Creates a Flow that emits the result of block whenever observed state changes
* The flow emits immediately with the current value, then emits again when any
* state accessed within block changes
* @param block Function that reads state and produces values
* @return Flow that emits when any observed state changes
*/
fun <T> snapshotFlow(block: () -> T): Flow<T>Usage Examples:
@Composable
fun SnapshotFlowExample() {
var name by remember { mutableStateOf("") }
var age by remember { mutableStateOf(0) }
// Flow that combines multiple state reads
val userSummary = remember {
snapshotFlow {
if (name.isNotEmpty() && age > 0) {
"User: $name, Age: $age"
} else {
"Incomplete user data"
}
}
}
LaunchedEffect(Unit) {
userSummary.collect { summary ->
logger.log("User summary updated: $summary")
}
}
// Create a debounced search flow
val debouncedSearch = remember {
snapshotFlow { name }
.debounce(300)
.filter { it.length >= 2 }
}
LaunchedEffect(Unit) {
debouncedSearch.collect { query ->
performSearch(query)
}
}
}fun nestedSnapshotExample() {
val outerSnapshot = Snapshot.takeMutableSnapshot()
outerSnapshot.apply {
someState.value = "outer change"
// Create nested snapshot
val innerSnapshot = takeNestedMutableSnapshot()
innerSnapshot.apply {
someState.value = "inner change"
otherState.value = "another change"
// Apply inner changes first
apply()
}
// Apply outer changes (includes inner changes)
apply()
}
outerSnapshot.dispose()
}fun conflictResolutionExample() {
val state = mutableStateOf("initial")
// First snapshot
val snapshot1 = Snapshot.takeMutableSnapshot()
snapshot1.apply {
state.value = "change 1"
}
// Second snapshot (concurrent modification)
val snapshot2 = Snapshot.takeMutableSnapshot()
snapshot2.apply {
state.value = "change 2"
}
// Apply first snapshot
val result1 = snapshot1.apply()
// Apply second snapshot (may conflict)
val result2 = snapshot2.apply()
when (result2) {
is SnapshotApplyResult.Success -> {
println("Second change applied successfully")
}
is SnapshotApplyResult.Failure -> {
println("Conflict detected, manual resolution required")
// Handle conflict resolution
}
}
snapshot1.dispose()
snapshot2.dispose()
}fun stateObservationExample() {
val readObserver: (Any) -> Unit = { state ->
println("State read: $state")
}
val writeObserver: (Any) -> Unit = { state ->
println("State written: $state")
}
val snapshot = Snapshot.takeMutableSnapshot(readObserver, writeObserver)
snapshot.apply {
// These operations will trigger observers
val value = someState.value // Triggers readObserver
someState.value = "new value" // Triggers writeObserver
apply()
}
snapshot.dispose()
}fun globalStateMonitoringExample() {
// Monitor all global state writes
val writeHandle = Snapshot.registerGlobalWriteObserver { state ->
println("Global state changed: $state")
}
// Monitor snapshot applications
val applyHandle = Snapshot.registerApplyObserver { changedObjects, snapshot ->
println("Snapshot applied with ${changedObjects.size} changes")
}
// Perform some state changes
someGlobalState.value = "changed"
// Clean up observers
writeHandle.dispose()
applyHandle.dispose()
}// iOS-specific snapshot handling
fun iosSnapshotExample() {
// Ensure snapshot operations happen on the main thread
DispatchQueue.main.async {
val snapshot = Snapshot.takeMutableSnapshot()
snapshot.apply {
// Safe to modify state on main thread
uiState.value = newUIState
apply()
}
snapshot.dispose()
}
}@Composable
fun UIKitStateIntegration() {
var composeState by remember { mutableStateOf("") }
// Create flow that watches Compose state
val stateFlow = remember {
snapshotFlow { composeState }
}
LaunchedEffect(Unit) {
stateFlow.collect { value ->
// Update UIKit component when Compose state changes
DispatchQueue.main.async {
someUIKitView.text = value
}
}
}
}fun batchStateUpdatesExample() {
// Batch multiple state changes in a single snapshot
val snapshot = Snapshot.takeMutableSnapshot()
snapshot.apply {
// All these changes will be applied atomically
state1.value = "value1"
state2.value = "value2"
state3.value = "value3"
apply() // Single recomposition for all changes
}
snapshot.dispose()
}class SnapshotManager {
private val snapshotPool = mutableListOf<MutableSnapshot>()
fun withPooledSnapshot(block: MutableSnapshot.() -> Unit) {
val snapshot = snapshotPool.removeFirstOrNull()
?: Snapshot.takeMutableSnapshot()
try {
snapshot.block()
} finally {
if (!snapshot.invalid) {
snapshotPool.add(snapshot)
} else {
snapshot.dispose()
}
}
}
}fun snapshotErrorHandlingExample() {
val snapshot = Snapshot.takeMutableSnapshot()
try {
snapshot.apply {
// Operations that might fail
riskyStateOperation()
apply()
}
} catch (e: Exception) {
// Handle errors - snapshot is automatically invalidated
println("Snapshot operation failed: ${e.message}")
} finally {
snapshot.dispose()
}
}Install with Tessl CLI
npx tessl i tessl/maven-org-jetbrains-compose-runtime--runtime-uikitx64