or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

Files

docs

composition-lifecycle.mdcomposition-local.mdeffects.mdindex.mdios-integration.mdsnapshot-system.mdstate-management.md

snapshot-system.mddocs/

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