A modern real-time cryptocurrency price tracker for Android demonstrating Clean Architecture, Coroutines + Flow, and reactive UI.
Features • Architecture • Getting Started
CoinPulse follows Clean Architecture with three clearly separated layers:
Presentation → Domain ← Data
- Presentation — Jetpack Compose screens, ViewModels exposing
StateFlow<UiState> - Domain — Pure Kotlin use cases and repository interfaces. Zero Android dependencies
- Data — Retrofit + Room implementations, DTOs, mappers, and background workers
CoinGecko API (polling every 30s)
└── flow { while(true) }
└── map / combine / catch
└── StateFlow in ViewModel
└── collectAsStateWithLifecycle in Compose
└── WorkManager (background alerts every 15min)
| Layer | Technology |
|---|---|
| UI | Jetpack Compose + Material 3 |
| Async | Kotlin Coroutines + Flow |
| DI | Hilt |
| Networking | Retrofit + OkHttp |
| Local storage | Room |
| Preferences | DataStore |
| Background | WorkManager (CoroutineWorker) |
| Image loading | Coil |
| Navigation | Navigation Compose |
flow { while(true) }— manual polling stream from REST APIStateFlow+stateIn()— converting cold flows to hot, lifecycle-aware statecombine()— merging market data stream with search query in real timecatch— error handling in the flow pipeline without crashing the streamflowOn(Dispatchers.IO)— moving upstream work off the main threadCoroutineWorker— background price alert monitoring via WorkManagerviewModelScope+SharingStarted.WhileSubscribed(5_000)— lifecycle-aware sharing
- Market — Live crypto prices with 30-second polling and real-time search filtering
- Detail — Individual coin data with price and market cap
- Favorites — Persist favorite coins with Room, reactive UI via
Flow<List<Favorite>> - Alerts — Set price thresholds, monitored in background via WorkManager notifications
com.mauro.coinpulse/
├── core/
│ ├── di/ # Hilt modules (Network, Database, Repository)
│ ├── util/ # NotificationHelper, WorkManagerScheduler
│ └── ui/ # Theme, colors, typography
├── data/
│ ├── local/ # Room entities, DAOs, AppDatabase
│ ├── remote/ # Retrofit API service, DTOs
│ ├── mapper/ # DTO → Domain model converters
│ ├── repository/ # CoinRepositoryImpl
│ └── worker/ # PriceAlertWorker (CoroutineWorker)
├── domain/
│ ├── model/ # Coin, Alert, Favorite
│ ├── repository/ # CoinRepository interface
│ └── usecase/ # GetCoinsUseCase, GetCoinDetailUseCase, etc.
└── presentation/
├── market/ # MarketScreen + MarketViewModel
├── detail/ # DetailScreen + DetailViewModel
├── favorites/ # FavoritesScreen + FavoritesViewModel
├── alerts/ # AlertsScreen + AlertsViewModel
└── navigation/ # NavGraph, Screen sealed class
- Clone the repository
git clone https://github.com/maurocosentino/coinpulse.git
- Open in Android Studio Hedgehog or later
- Run on emulator or physical device (API 26+)
- No API key required — uses CoinGecko public API
This project was built to go deep on reactive programming in Android. The main takeaways:
- The difference between cold and hot flows and when to use each
- How
stateIn()withWhileSubscribedprevents unnecessary upstream work during recomposition - Why
CoroutineWorkeris required overWorkerwhen callingsuspendfunctions in background tasks - How
combine()allows merging independent data streams without manual synchronization - The value of Clean Architecture when the CoinGecko detail endpoint had a different shape than the list endpoint — only the DTO and mapper needed to change, zero impact on domain or UI
Mauro Cosentino Android Developer GitHub