Repository Pattern in KMP: Flow, Stale-While-Revalidate, Testable

Every data-driven app has the same two problems: users see a loading spinner when cached data would be perfectly fine, and they see an error screen when they should be seeing stale-but-useful content. Both problems come from the same gap — a repository that doesn't implement stale-while-revalidate correctly. This episode shows the full implementation from the MyEuroStatApp project: one cold Flow, a sealed Result‹T› with an isStale flag, and five Turbine test cases that verify every emission path. ⏱️ Chapters: 00:00 — Hook: the loading spinner of shame and the offline error wall 01:05 — The domain contract: observe() + refresh(), strict emission protocol 02:10 — Sealed Result‹T›: Loading, Success(isStale), Error — compiler-checked 03:15 — observe() implementation step by step: emit Loading → cache → network 04:20 — The revalidation branch: try/catch, upsert, stale emit → fresh emit 05:25 — Error swallowing: hadCache flag, when to silently suppress network failure 06:30 — flowOn: upstream on IO, downstream on Main — one call, not many 07:35 — DispatcherProvider: why you never reference Dispatchers.IO directly 08:40 — Turbine: awaitItem / awaitComplete / awaitError, no callbacks needed 09:45 — 5 test scenarios: fresh, stale+ok, no cache+ok, no cache+fail, stale+fail 10:50 — The subtle test: stale cache + network failure must NOT emit Error 11:55 — Fake doubles without Mockk: FakeApiService, FakeDao, FakeClock 📦 Project: github.com/RomanTsisyk/MyEuroStatApp Part of the "Kotlin Multiplatform — full course" series built on a real production app. 🔑 Key concepts covered: • Stale-while-revalidate: emit cached data before network, refresh silently in background • sealed Result‹T›: Loading / Success(data, isStale) / Error(AppError) — exhaustive when • flowOn(dispatchers.io): changes upstream context only, downstream stays on Main • DispatcherProvider injection: replaces Dispatchers.IO in tests with TestDispatcher • Turbine library: awaitItem(), awaitComplete(), awaitError() for linear Flow assertions • Error swallowing: network failure is suppressed when stale cache was already emitted • Handwritten fakes instead of Mockk: commonTest compiles for iOS Native, Mockk doesn't • FakeClock.advance(duration): simulate time without Thread.sleep #KotlinMultiplatform #KMP #KotlinCoroutines #AndroidDevelopment #KotlinAndroid #Flow #RepositoryPattern #CleanArchitecture #ComposeMultiplatform #MobileDevelopment

Zig 2026: No-AI Policy, $670K Foundation, Left GitHub & Why Zig Isn’t 1.0 - Andrew Kelley Explains
▶︎

Zig 2026: No-AI Policy, $670K Foundation, Left GitHub & Why Zig Isn’t 1.0 - Andrew Kelley Explains

7 Authentication Concepts Every Developer Should Know
▶︎

7 Authentication Concepts Every Developer Should Know

Keynote: After the AI Hype – What’s Real, and What’s Next - Richard Campbell - 2026
▶︎

Keynote: After the AI Hype – What’s Real, and What’s Next - Richard Campbell - 2026

JSON-stat 2.0 Parsing in Kotlin Multiplatform: Flat Arrays to Types
▶︎

JSON-stat 2.0 Parsing in Kotlin Multiplatform: Flat Arrays to Types

SQLDelight: The Multiplatform Cache That Compiles Your SQL | KMP
▶︎

SQLDelight: The Multiplatform Cache That Compiles Your SQL | KMP

Android 17 sucks. So I put Linux on a phone.
▶︎

Android 17 sucks. So I put Linux on a phone.

The 'Do-Not-Hire List' Is Real. Here's What Actually Gets You On It.
▶︎

The 'Do-Not-Hire List' Is Real. Here's What Actually Gets You On It.

Harvesting Many Big Fish From Inside The Pond | Goes To Market For Sell - Farm Harvest Journey
▶︎

Harvesting Many Big Fish From Inside The Pond | Goes To Market For Sell - Farm Harvest Journey

CHOSEN ONE! U HAVE UNKNOWN FRIENDS IN HIGH PLACES & WHAT THEY WILL DO TO PROTECT U IS UNBELIEVABLE
▶︎

CHOSEN ONE! U HAVE UNKNOWN FRIENDS IN HIGH PLACES & WHAT THEY WILL DO TO PROTECT U IS UNBELIEVABLE

TIENES que Aprender a CREER EN TI | Yokoi Kenji
▶︎

TIENES que Aprender a CREER EN TI | Yokoi Kenji

Mr.Bean Making Celebrities Cry With Laughter NONSTOP!
▶︎

Mr.Bean Making Celebrities Cry With Laughter NONSTOP!

No Boss, No Money: The Raw Reality of China’s Gen-Z Freelancers
▶︎

No Boss, No Money: The Raw Reality of China’s Gen-Z Freelancers

The Riskiest Moment of the AI Bubble
▶︎

The Riskiest Moment of the AI Bubble

Scott Ritter: Russland gewinnt den Krieg – und das eindeutig
▶︎

Scott Ritter: Russland gewinnt den Krieg – und das eindeutig

Pawel Czerwinski | Abstract Art Slideshow for your TV | 1hour of 4K HD Paintings
▶︎

Pawel Czerwinski | Abstract Art Slideshow for your TV | 1hour of 4K HD Paintings

Don't Hang Up On AI Scammers. Do THIS Instead.
▶︎

Don't Hang Up On AI Scammers. Do THIS Instead.

Real-time metaprogramming, zero dependencies // Michael Labbé (Handmade Network Expo 2026)
▶︎

Real-time metaprogramming, zero dependencies // Michael Labbé (Handmade Network Expo 2026)

Full Walkthrough: Workflow for AI Coding — Matt Pocock
▶︎

Full Walkthrough: Workflow for AI Coding — Matt Pocock

Why Ancient Humans Went From Black to White?
▶︎

Why Ancient Humans Went From Black to White?

System Design Course – APIs, Databases, Caching, CDNs, Load Balancing & Production Infra
▶︎

System Design Course – APIs, Databases, Caching, CDNs, Load Balancing & Production Infra