0
# Snapshot System
1
2
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.
3
4
## Capabilities
5
6
### Snapshot Classes
7
8
Core classes for managing snapshots of mutable state.
9
10
```kotlin { .api }
11
/**
12
* Abstract base class for snapshots representing a consistent view of mutable state
13
*/
14
abstract class Snapshot {
15
/** Unique identifier for this snapshot */
16
abstract val id: Int
17
18
/** Whether this snapshot is invalid due to conflicts */
19
abstract val invalid: Boolean
20
21
/** Set of objects that were read during this snapshot */
22
abstract val readSet: Set<Any>
23
24
/** Set of objects that were modified during this snapshot */
25
abstract val writeSet: Set<Any>
26
27
/** Disposes this snapshot and releases resources */
28
abstract fun dispose()
29
30
companion object {
31
/** The global snapshot that all state changes are applied to */
32
val global: MutableSnapshot
33
34
/** The current snapshot for the current thread */
35
val current: Snapshot
36
37
/**
38
* Takes an immutable snapshot of the current state
39
* @param readObserver Optional observer for state reads
40
* @return Immutable snapshot
41
*/
42
fun takeSnapshot(readObserver: ((Any) -> Unit)? = null): Snapshot
43
44
/**
45
* Takes a mutable snapshot for transactional state changes
46
* @param readObserver Optional observer for state reads
47
* @param writeObserver Optional observer for state writes
48
* @return Mutable snapshot for making changes
49
*/
50
fun takeMutableSnapshot(
51
readObserver: ((Any) -> Unit)? = null,
52
writeObserver: ((Any) -> Unit)? = null
53
): MutableSnapshot
54
55
/**
56
* Runs a block with the given snapshot as current
57
* @param snapshot Snapshot to make current
58
* @param block Code to execute with the snapshot
59
* @return Result of the block
60
*/
61
fun <T> withSnapshot(snapshot: Snapshot, block: () -> T): T
62
63
/**
64
* Runs a block with a mutable snapshot
65
* @param readObserver Optional observer for state reads
66
* @param writeObserver Optional observer for state writes
67
* @param block Code to execute in the snapshot
68
* @return Result of the block
69
*/
70
fun <T> withMutableSnapshot(
71
readObserver: ((Any) -> Unit)? = null,
72
writeObserver: ((Any) -> Unit)? = null,
73
block: () -> T
74
): T
75
76
/**
77
* Sends and applies all pending snapshot changes
78
*/
79
fun sendApplyNotifications()
80
}
81
}
82
83
/**
84
* Mutable snapshot that can be modified and applied
85
*/
86
abstract class MutableSnapshot : Snapshot() {
87
/** Whether changes have been made to this snapshot */
88
abstract val modified: Boolean
89
90
/**
91
* Applies this snapshot's changes to the global state
92
* @return Result indicating success or failure
93
*/
94
abstract fun apply(): SnapshotApplyResult
95
96
/**
97
* Creates a nested mutable snapshot
98
* @param readObserver Optional observer for state reads
99
* @param writeObserver Optional observer for state writes
100
* @return Nested mutable snapshot
101
*/
102
abstract fun takeNestedMutableSnapshot(
103
readObserver: ((Any) -> Unit)? = null,
104
writeObserver: ((Any) -> Unit)? = null
105
): MutableSnapshot
106
107
/**
108
* Creates a nested immutable snapshot
109
* @param readObserver Optional observer for state reads
110
* @return Nested immutable snapshot
111
*/
112
abstract fun takeNestedSnapshot(readObserver: ((Any) -> Unit)? = null): Snapshot
113
}
114
```
115
116
### Snapshot Apply Results
117
118
Results of applying snapshot changes.
119
120
```kotlin { .api }
121
/**
122
* Result of applying a snapshot
123
*/
124
sealed class SnapshotApplyResult {
125
/** Application succeeded */
126
object Success : SnapshotApplyResult()
127
128
/** Application failed due to conflicts */
129
class Failure(val snapshot: Snapshot) : SnapshotApplyResult()
130
}
131
```
132
133
**Usage Examples:**
134
135
```kotlin
136
// Basic snapshot usage
137
fun snapshotExample() {
138
val snapshot = Snapshot.takeMutableSnapshot()
139
140
val result = snapshot.apply {
141
// Make changes to state within the snapshot
142
someState.value = "new value"
143
otherState.value = "other new value"
144
145
// Apply all changes atomically
146
apply()
147
}
148
149
when (result) {
150
is SnapshotApplyResult.Success -> {
151
println("Changes applied successfully")
152
}
153
is SnapshotApplyResult.Failure -> {
154
println("Changes failed to apply due to conflicts")
155
}
156
}
157
158
snapshot.dispose()
159
}
160
```
161
162
### Global Snapshot Operations
163
164
Functions for working with the global snapshot state.
165
166
```kotlin { .api }
167
/**
168
* Runs block with global state write observation
169
* @param writeObserver Function called when global state is written
170
* @param block Code to execute with observation
171
* @return Result of the block
172
*/
173
fun <T> Snapshot.Companion.withoutReadObservation(block: () -> T): T
174
175
/**
176
* Registers a global state write observer
177
* @param observer Function called when any global state changes
178
* @return Handle to remove the observer
179
*/
180
fun Snapshot.Companion.registerGlobalWriteObserver(
181
observer: (Any) -> Unit
182
): ObserverHandle
183
184
/**
185
* Registers a global apply observer
186
* @param observer Function called when snapshots are applied
187
* @return Handle to remove the observer
188
*/
189
fun Snapshot.Companion.registerApplyObserver(
190
observer: (Set<Any>, Snapshot) -> Unit
191
): ObserverHandle
192
193
/**
194
* Handle for removing observers
195
*/
196
interface ObserverHandle {
197
/** Removes the observer */
198
fun dispose()
199
}
200
```
201
202
### Snapshot Flow Integration
203
204
Convert state reads into coroutine Flows that emit when dependencies change.
205
206
```kotlin { .api }
207
/**
208
* Creates a Flow that emits the result of block whenever observed state changes
209
* The flow emits immediately with the current value, then emits again when any
210
* state accessed within block changes
211
* @param block Function that reads state and produces values
212
* @return Flow that emits when any observed state changes
213
*/
214
fun <T> snapshotFlow(block: () -> T): Flow<T>
215
```
216
217
**Usage Examples:**
218
219
```kotlin
220
@Composable
221
fun SnapshotFlowExample() {
222
var name by remember { mutableStateOf("") }
223
var age by remember { mutableStateOf(0) }
224
225
// Flow that combines multiple state reads
226
val userSummary = remember {
227
snapshotFlow {
228
if (name.isNotEmpty() && age > 0) {
229
"User: $name, Age: $age"
230
} else {
231
"Incomplete user data"
232
}
233
}
234
}
235
236
LaunchedEffect(Unit) {
237
userSummary.collect { summary ->
238
logger.log("User summary updated: $summary")
239
}
240
}
241
242
// Create a debounced search flow
243
val debouncedSearch = remember {
244
snapshotFlow { name }
245
.debounce(300)
246
.filter { it.length >= 2 }
247
}
248
249
LaunchedEffect(Unit) {
250
debouncedSearch.collect { query ->
251
performSearch(query)
252
}
253
}
254
}
255
```
256
257
## Advanced Snapshot Operations
258
259
### Nested Snapshots
260
261
```kotlin
262
fun nestedSnapshotExample() {
263
val outerSnapshot = Snapshot.takeMutableSnapshot()
264
265
outerSnapshot.apply {
266
someState.value = "outer change"
267
268
// Create nested snapshot
269
val innerSnapshot = takeNestedMutableSnapshot()
270
271
innerSnapshot.apply {
272
someState.value = "inner change"
273
otherState.value = "another change"
274
275
// Apply inner changes first
276
apply()
277
}
278
279
// Apply outer changes (includes inner changes)
280
apply()
281
}
282
283
outerSnapshot.dispose()
284
}
285
```
286
287
### Conflict Resolution
288
289
```kotlin
290
fun conflictResolutionExample() {
291
val state = mutableStateOf("initial")
292
293
// First snapshot
294
val snapshot1 = Snapshot.takeMutableSnapshot()
295
snapshot1.apply {
296
state.value = "change 1"
297
}
298
299
// Second snapshot (concurrent modification)
300
val snapshot2 = Snapshot.takeMutableSnapshot()
301
snapshot2.apply {
302
state.value = "change 2"
303
}
304
305
// Apply first snapshot
306
val result1 = snapshot1.apply()
307
308
// Apply second snapshot (may conflict)
309
val result2 = snapshot2.apply()
310
311
when (result2) {
312
is SnapshotApplyResult.Success -> {
313
println("Second change applied successfully")
314
}
315
is SnapshotApplyResult.Failure -> {
316
println("Conflict detected, manual resolution required")
317
// Handle conflict resolution
318
}
319
}
320
321
snapshot1.dispose()
322
snapshot2.dispose()
323
}
324
```
325
326
### State Observation
327
328
```kotlin
329
fun stateObservationExample() {
330
val readObserver: (Any) -> Unit = { state ->
331
println("State read: $state")
332
}
333
334
val writeObserver: (Any) -> Unit = { state ->
335
println("State written: $state")
336
}
337
338
val snapshot = Snapshot.takeMutableSnapshot(readObserver, writeObserver)
339
340
snapshot.apply {
341
// These operations will trigger observers
342
val value = someState.value // Triggers readObserver
343
someState.value = "new value" // Triggers writeObserver
344
345
apply()
346
}
347
348
snapshot.dispose()
349
}
350
```
351
352
### Global State Monitoring
353
354
```kotlin
355
fun globalStateMonitoringExample() {
356
// Monitor all global state writes
357
val writeHandle = Snapshot.registerGlobalWriteObserver { state ->
358
println("Global state changed: $state")
359
}
360
361
// Monitor snapshot applications
362
val applyHandle = Snapshot.registerApplyObserver { changedObjects, snapshot ->
363
println("Snapshot applied with ${changedObjects.size} changes")
364
}
365
366
// Perform some state changes
367
someGlobalState.value = "changed"
368
369
// Clean up observers
370
writeHandle.dispose()
371
applyHandle.dispose()
372
}
373
```
374
375
## iOS Platform Integration
376
377
### Thread Safety
378
379
```kotlin
380
// iOS-specific snapshot handling
381
fun iosSnapshotExample() {
382
// Ensure snapshot operations happen on the main thread
383
DispatchQueue.main.async {
384
val snapshot = Snapshot.takeMutableSnapshot()
385
386
snapshot.apply {
387
// Safe to modify state on main thread
388
uiState.value = newUIState
389
apply()
390
}
391
392
snapshot.dispose()
393
}
394
}
395
```
396
397
### Integration with UIKit State
398
399
```kotlin
400
@Composable
401
fun UIKitStateIntegration() {
402
var composeState by remember { mutableStateOf("") }
403
404
// Create flow that watches Compose state
405
val stateFlow = remember {
406
snapshotFlow { composeState }
407
}
408
409
LaunchedEffect(Unit) {
410
stateFlow.collect { value ->
411
// Update UIKit component when Compose state changes
412
DispatchQueue.main.async {
413
someUIKitView.text = value
414
}
415
}
416
}
417
}
418
```
419
420
## Performance Optimization
421
422
### Batch State Updates
423
424
```kotlin
425
fun batchStateUpdatesExample() {
426
// Batch multiple state changes in a single snapshot
427
val snapshot = Snapshot.takeMutableSnapshot()
428
429
snapshot.apply {
430
// All these changes will be applied atomically
431
state1.value = "value1"
432
state2.value = "value2"
433
state3.value = "value3"
434
435
apply() // Single recomposition for all changes
436
}
437
438
snapshot.dispose()
439
}
440
```
441
442
### Snapshot Recycling
443
444
```kotlin
445
class SnapshotManager {
446
private val snapshotPool = mutableListOf<MutableSnapshot>()
447
448
fun withPooledSnapshot(block: MutableSnapshot.() -> Unit) {
449
val snapshot = snapshotPool.removeFirstOrNull()
450
?: Snapshot.takeMutableSnapshot()
451
452
try {
453
snapshot.block()
454
} finally {
455
if (!snapshot.invalid) {
456
snapshotPool.add(snapshot)
457
} else {
458
snapshot.dispose()
459
}
460
}
461
}
462
}
463
```
464
465
## Error Handling
466
467
### Snapshot Exceptions
468
469
```kotlin
470
fun snapshotErrorHandlingExample() {
471
val snapshot = Snapshot.takeMutableSnapshot()
472
473
try {
474
snapshot.apply {
475
// Operations that might fail
476
riskyStateOperation()
477
apply()
478
}
479
} catch (e: Exception) {
480
// Handle errors - snapshot is automatically invalidated
481
println("Snapshot operation failed: ${e.message}")
482
} finally {
483
snapshot.dispose()
484
}
485
}
486
```
487
488
## Performance Considerations
489
490
- **Snapshot Disposal**: Always dispose snapshots to prevent memory leaks
491
- **Batch Updates**: Use snapshots to batch multiple state changes and reduce recomposition
492
- **Read Observation**: Minimize expensive read observers that are called frequently
493
- **Nested Snapshots**: Use nested snapshots sparingly as they add overhead
494
- **Conflict Resolution**: Design state updates to minimize conflicts between concurrent snapshots
495
- **Flow Creation**: Cache snapshotFlow instances to avoid recreating flows unnecessarily