Shared KMP Persistence

One storage API in commonMain — the same document code runs unchanged on Android and iOS, reading and writing through the same MMKV-backed store on both platforms.

// commonMain
@Serializable
data class OfflineQueueItem(
    val id: String,
    val endpoint: String,
    val payloadJson: String,
    val attempts: Int = 0,
)

@Serializable
data class QueueState(
    val items: List<OfflineQueueItem> = emptyList(),
)

class PendingRequestQueue {
    private val doc = Documents.document<QueueState>("pending-requests")

    fun enqueue(item: OfflineQueueItem) {
        doc.update { current -> current.copy(items = current.items + item) }
    }

    fun observe(): Flow<List<OfflineQueueItem>> =
        doc.flow().map { it?.items ?: emptyList() }
}

Note the queue is wrapped in a QueueState class rather than opened as Documents.document<List<OfflineQueueItem>>("pending-requests") directly — a top-level List is not a supported document root type today (field decomposition keys off a class's declared properties, which a bare list doesn't have); a list works correctly only as a field inside a @Serializable class, as shown here. See Opening Documents for the full constraint.

PendingRequestQueue is written once, in commonMain, and used identically from an Android ViewModel and an iOS view model — there is no platform-specific persistence code to write or keep in sync, because the entire public API and its logic live in commonMain. Only the underlying Storage implementation differs per platform, and that's an internal detail the library owns. See Storage SPI and Platform Support for how each target is bound.