A third BookRepository implementation, OfflineFirstBookRepository, bound in DataModule, brings in the network without touching any screen. loadBooks() asks the server, writes what arrives into Room 234 , and answers from Room:
override suspend fun loadBooks(): List<Book> {
when (val result = apiCall { api.books() }) {
is ApiResult.Success -> {
val now = System.currentTimeMillis()
dao.upsertAll(result.data.map { it.toEntity(fetchedAt = now) })
sync.updateData { SyncState(source = "network", books = result.data.size, at = now) }
online.value = true
Log.d("BookNestNet", "refreshed ${result.data.size} books from the network")
}
...
}
val cached = dao.getAll() // the cache answers either way
if (cached.isEmpty()) throw IOException("No connection and nothing saved yet")
return cached.map { it.toBook() }
}A cold start with the test server running logged the exchange shown in OkHttp Interceptors, then refreshed 6 books from the network; covers travel separately, through Coil 11,909 's own OkHttp 47,077 client (Images with Coil and Glide). The server has Salt and Saffron on sale at $21.60, and the catalog now shows that price instead of the bundled $24.00 (see the figure in Offline-First). @Upsert updates changed rows but never removes books the server has dropped; a full sync would also delete rows missing from the response, in the same @Transaction.