Inspiration
In the Netherlands you pay a deposit (statiegeld) on plastic bottles, cans and glass — but getting it back means finding a machine that takes what you're carrying, is open right now, and actually works. That information is scattered across supermarket websites, OpenStreetMap and word of mouth, and the most common way to find out a machine is broken is to walk there with a bag of empties.
I wanted one place that answers "where can I return this, right now?" — and that gets more accurate the more people use it.
What it does
- Map and list of return points near you or any address or postcode, with clustering and filters: open now, glass, cans, plastic, crates, favourites and supermarket chain.
- Location details: opening hours, what the machine accepts, directions and sharing.
- Barcode scanner that tells you whether a product carries a deposit.
- Community reports: confirm a machine works, report one that's broken or full, and suggest corrections. Reports queue offline and sync when you're back online.
- Push alerts for followed locations — the one paid feature, a yearly subscription through RevenueCat. Follow the machine you use and hear when it's out of order before you make the trip. Everything else stays free.
How I built it
Deposit is a Kotlin Multiplatform + Compose Multiplatform app: one shared UI and one codebase for Android and iOS. On iOS, a thin SwiftUI shell simply hosts the Compose UI.
- Architecture: Jetpack ViewModel (KMP), Navigation 3 with typed routes, Metro for compiler-plugin dependency injection, Ktor, SQLDelight and kotlinx.serialization.
- Map: MapLibre through
maplibre-compose, on OpenStreetMap data, with custom clustering and adaptive layouts for phones, tablets, foldables and landscape. - Monetization: RevenueCat's
purchases-kmpbehind my own purchases module, so the paywall is shared code on both platforms. The offer sits behind a remote-config flag, so it can be withdrawn without shipping a release. - Backend: a Kotlin/Ktor service on Google Cloud Run with Firestore. It merges location data from several sources into one clean model and stores the community reports.
- Quality: Roborazzi screenshot tests render every screen in six variants (second language, dark mode, tablet, landscape, large font), and the module graph is enforced in the build. Much of the day-to-day development runs through AI coding agents working from GitHub issues, with me reviewing and deciding.
Challenges I ran into
- Sources that disagree. Supermarket data and OpenStreetMap often describe the same machine differently, so the backend has to merge them without duplicates or lost detail.
- A fast map with thousands of points on both platforms from one Compose codebase.
- Choosing what to charge for. I wanted exactly one paid feature: one people would clearly value and that costs real money to run, without locking away anything that was already free. Push alerts were the answer: they save you a wasted trip.
Accomplishments that I'm proud of
- One Compose Multiplatform codebase that looks and behaves natively on Android and iOS, from phones to foldables.
- A monetization model that respects users: the core app is free, and the subscription pays for the feature that actually has running costs.
- Shipping frequent releases as a solo developer, with automated screenshot testing across languages, themes and screen sizes.
What I learned
Compose Multiplatform is production-ready for a real map-heavy app. RevenueCat made cross-platform subscriptions straightforward. And the hardest problems in a "simple" finder app are data quality and trust, not UI.
What's next
- The iOS App Store release, with push alerts on iOS.
- Giving back to OpenStreetMap: community corrections flowing back into OSM, so everyone's maps improve.
- Going worldwide: deposit return systems are spreading across Europe and beyond. The backend already serves OpenStreetMap machines outside the Netherlands, and the next step is to take Deposit to every country with a deposit system.
Built With
- android
- compose-multiplatform
- firebase
- firestore
- google-cloud-run
- ios
- jetpack-compose
- kotlin
- kotlin-multiplatform
- kotlinx-serialization
- ktor
- maplibre
- metro
- openstreetmap
- revenuecat
- roborazzi
- sqldelight

Log in or sign up for Devpost to join the conversation.