Releases: skydoves/nowinandroid-kmp
Release list
1.0.0
This is Google's Now in Android sample moved onto Kotlin Multiplatform. The same screens, ViewModels, navigation graph and offline-first data layer now compile for Android, iOS, desktop and the browser out of a single commonMain.
馃寪 Try it without installing anything
https://skydoves.github.io/nowinandroid-kmp/
That is the wasm build of this repository, published to GitHub Pages on every push to main. Onboarding, the feed, FTS search, bookmarks and the interests list all run there.
Four apps, one source tree
| Platform | Module | Run it |
|---|---|---|
| Android | app/androidApp |
./gradlew :app:androidApp:installDebug, or the APK below |
| iOS | app/iosApp |
./scripts/generate-xcodeproj.sh, then open the generated project |
| Desktop | app/desktopApp |
./gradlew :app:desktopApp:run |
| Web | app/webApp |
./gradlew :app:webApp:wasmJsBrowserDevelopmentRun |
84% of the Kotlin under src/*Main sits in commonMain. What is left is the platform seams, and there are fewer of them than you would expect.
What the port had to replace
Upstream is Android-only in the places you would guess, so each of those grew a seam:
- Hilt became Metro, one
@DependencyGraphper platform, every binding resolved at compile time, assisted ViewModels included. - Retrofit and OkHttp became Ktor on each platform's own engine, wrapped in Sandwich so an HTTP error and a transport failure show up as two inspectable results rather than one thrown exception.
- Room 2 became Room 3, still the offline-first cache on all four targets, FTS search included.
- Proto DataStore became DataStore over okio, with a kotlinx.serialization serializer standing in for the JVM-only protobuf one.
- Coil became Landscapist.
landscapist-svgrasterises the topic icons, which are all SVG, andShimmerPluginreplaces upstream's fixed 80.dp spinner. That spinner covered half of a 180.dp news header and was clamped into a stray ring on a 32.dp icon; a shimmer takes the shape of whatever it fills. R.stringandR.drawablebecame Compose Resources, oneResper module.- WorkManager stays WorkManager on Android and becomes a mutex-guarded app-scope coroutine everywhere else.
Connectivity, time zone changes, notifications, deep links, jank tracking and opening a URL each have a small per-platform implementation behind a shared interface. The README has the whole table.
Navigation is Navigation 3 in both projects, so upstream's multi-back-stack Navigator ported over close to unchanged, list-detail scene strategy and all. compose-nav-graph extracts the graph at compile time from @NavDestination and @NavEdge, and ./gradlew navCheck fails the build if a destination or an edge changes without the baseline being updated.
Notes on the browser build
Three things there are genuinely browser-only.
The database runs in a Web Worker. androidx.sqlite:sqlite-web is the only wasm driver, and it ships the Kotlin half of a worker protocol without the worker itself, so app/webApp supplies one over the official SQLite WASM build. That build is compiled with FTS5 and no FTS4, while Room offers only @Fts3 and @Fts4, so the worker rewrites the CREATE VIRTUAL TABLE statement on the way through. The app only ever asks the FTS tables for MATCH, count(*) and inserts, and those behave the same on either version.
The database is in memory. Persisting to OPFS needs the page to be cross-origin isolated, which a plain static host cannot promise, so the browser build re-syncs on load instead. Preferences do persist, in localStorage, so the topics you follow survive a reload.
Topic icons come from the app's own origin. The demo data points them at a bucket that answers browsers without an Access-Control-Allow-Origin header, so the request is blocked before any decoder sees it. The icons are copied into the web app and the URLs rewritten. A few news header images sit behind the same restriction and fall back to the placeholder. Fixing those needs a proxy rather than a code change.
What is not here
Upstream pieces that did not come across, all of them Android-only tooling rather than app behaviour: Firebase (analytics, Crashlytics, Remote Config) and its demo/prod flavours, Roborazzi screenshot tests, the macrobenchmark and baseline profile modules, and the instrumented test suite. Analytics is still there as an interface with a logging implementation, so the call sites upstream added are intact.
Tests
commonTest runs on every target it can:
| Suite | Tests |
|---|---|
desktopTest |
158 |
iosSimulatorArm64Test |
139 |
testAndroidHostTest |
117 |
All green. CI runs the four builds, the three suites, spotlessCheck and navCheck on every push.
Built with
Kotlin 2.4.10, Compose Multiplatform 1.12.0, AGP 9.2.1, Gradle 9.5.1, JDK 17 target, Room 3.0.2, Ktor 3.5.2, Metro 1.4.2, Landscapist 2.12.1.
Credit
All of the product design, the content, the architecture guidance and the demo data come from the Android team at Google, under Apache 2.0. This repository only moves that work onto Kotlin Multiplatform, and the original stays the reference implementation. The port was made against 12f80da if you want a like-for-like diff.
