The app follows the MVVM architecture pattern and is built with testability in mind.
These are the architectural layers of the app:
- View (Fragment)
- ViewModel
- Domain (Interactor)
- Repository
- Data sources
- local, using Room
- remote, using Retrofit
The data is delivered to the Fragment via LiveData instances. The architecture relies heavily on coroutine constructs including Flow and it's variants: StateFlow and SharedFlow. For dependency injection I used Hilt to minimalize boilerplate code.
Maintain a cache of the results and reuse it when the app is opened. It should be invalid after 1 minute from the last update.
My goal was to provide the best user experience while following to these instructions. I interpreted "invalid" cache as a cache that needs an update, but it still stores the data. This means when you kill the app and reopen it more than 1 minute after the last update, it will perform an automatic refresh on startup. The user can initiate further updates by swiping down. I decided to use the swipe refresh indicator as an indicator for automatic updates too.
There's two levels of "cache" storage: Room persistence, and the state stored in a LiveData instance. The latter is retained in memory across configuration changes, so no IO operations are needed.
The app stores all the weather data it receives from the webapi, including future predictions. When there's a refresh, the app selects the weather data according to the current date, and displays it with appending the suffix "(forecast)", indicating that it's not an up-to-date measurement.
The data age time indicator label increments every second. It shows hours, minutes and seconds. It can be helpful for testing the cache's correct behavior.