KMP Core Contracts: Result, AppError & DispatcherProvider Explained (Ep 3)

Three files. Under 100 lines of Kotlin combined. Every repository, use case, and Compose screen in the project depends on them. Get these wrong and the consequences propagate across all 9 feature modules. Get them right and the architecture locks in place with compiler-verified correctness. This episode reads Result‹T›, AppError, DispatcherProvider, and safeFetch line by line from the real MyEuroStatApp codebase — explaining every design decision, why kotlin.Result isn't enough, why sealed types beat open exception hierarchies, and why hardcoding Dispatchers.IO in a KMP project is a subtle bug waiting to happen on iOS. ⏱️ Chapters: 00:00 — Hook: 3 files, 100 lines, every module depends on them 01:05 — Why a custom Result‹T› and not kotlin.Result 02:10 — Reading Result‹T›: the type, covariance, and isStale 03:15 — isStale and the stale-while-revalidate emission protocol 04:20 — AppError: sealed taxonomy of 5 domain failure types 05:25 — Exhaustive when: adding a variant breaks all consumers at compile time 06:30 — DispatcherProvider: the testability lever 07:35 — DefaultDispatcherProvider as expect/actual — why iOS needs it 08:40 — TestDispatcherProvider in practice with Turbine 09:45 — safeFetch and the CancellationException re-throw 10:50 — The full contract in motion: one request through all four types 11:55 — Bottom line + next episode: Ktor networking 🛠️ Files read in this episode: • core-common/src/commonMain/kotlin/eu/eurostat/core/common/Result.kt (full file) • core-common/src/commonMain/kotlin/eu/eurostat/core/common/DispatcherProvider.kt (full file) • core-common/src/commonMain/kotlin/eu/eurostat/core/common/SafeFetch.kt (full file) • feature-population/.../ui/PopulationComponent.kt (AppError.toUserMessage when expression) • feature-population/.../data/PopulationRepositoryImplTest.kt (TestDispatcherProvider + Turbine) 📦 Project repo (note: may be private — check description for current link): https://github.com/romantsisyk/MyEuro... ✅ What you'll understand after this episode: • Why a 3-state Result beats kotlin.Result for data-layer flows • How isStale encodes the entire stale-while-revalidate caching protocol • Why sealed AppError makes UI error handling compiler-verified • How DispatcherProvider enables deterministic coroutine tests on all KMP platforms • Why re-throwing CancellationException is not optional — it's a correctness requirement #KotlinMultiplatform #KMP #KotlinCoroutines #KotlinFlow #AndroidArchitecture #CleanArchitecture #KMPTutorial #ComposeMultiplatform #KotlinNative #AndroidDevelopment