{"id":16490615,"url":"https://github.com/odaridavid/weatherapp","last_synced_at":"2025-04-04T13:08:43.919Z","repository":{"id":80953895,"uuid":"596789592","full_name":"odaridavid/WeatherApp","owner":"odaridavid","description":"☁️ ❄️ A KMP weather app built with Jetpack Compose , MVI , Unit Testing , Hilt and Location Services, Github Actions, Firebase + Material 3","archived":false,"fork":false,"pushed_at":"2024-10-24T08:06:27.000Z","size":2547,"stargazers_count":170,"open_issues_count":25,"forks_count":32,"subscribers_count":5,"default_branch":"develop","last_synced_at":"2024-10-25T04:34:48.385Z","etag":null,"topics":["android","androidpermission","bitrise","compose","design-system","firebase","github-actions","hilt","ios","jetpack","kmm","kmp","kotlin","location-services","material3-android","mvi","openweatherapi","weather"],"latest_commit_sha":null,"homepage":"https://youtu.be/O7fBOOVa3Do","language":"Kotlin","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"apache-2.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/odaridavid.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":"CONTRIBUTING.md","funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2023-02-02T23:33:29.000Z","updated_at":"2024-10-16T05:48:45.000Z","dependencies_parsed_at":"2024-05-31T15:34:04.241Z","dependency_job_id":"063d02ed-84d7-4c89-9d56-ebf2f43356f9","html_url":"https://github.com/odaridavid/WeatherApp","commit_stats":{"total_commits":106,"total_committers":4,"mean_commits":26.5,"dds":"0.037735849056603765","last_synced_commit":"a6627b1b5611e2d41e79bc14b08c600a4337c741"},"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/odaridavid%2FWeatherApp","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/odaridavid%2FWeatherApp/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/odaridavid%2FWeatherApp/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/odaridavid%2FWeatherApp/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/odaridavid","download_url":"https://codeload.github.com/odaridavid/WeatherApp/tar.gz/refs/heads/develop","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":247182390,"owners_count":20897381,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["android","androidpermission","bitrise","compose","design-system","firebase","github-actions","hilt","ios","jetpack","kmm","kmp","kotlin","location-services","material3-android","mvi","openweatherapi","weather"],"created_at":"2024-10-11T13:48:31.537Z","updated_at":"2025-04-04T13:08:43.900Z","avatar_url":"https://github.com/odaridavid.png","language":"Kotlin","funding_links":[],"categories":[],"sub_categories":[],"readme":"### Weather App\n\n[![Build Status](https://app.bitrise.io/app/80f9b4627fc90757/status.svg?token=3KnRQl0WRfDT5UTzPDiRgA\u0026branch=develop)](https://app.bitrise.io/app/80f9b4627fc90757)\n[![codecov](https://codecov.io/gh/odaridavid/WeatherApp/branch/develop/graph/badge.svg?token=eZcGjGhF83)](https://codecov.io/gh/odaridavid/WeatherApp)\n\n*Summary*\n\nA simple weather app that gets your location and displays the forecast for the current day and a few\ndays after that.\n\n*API :* [OpenWeatherMap](https://openweathermap.org/api)\n\nReason for choosing mentioned API :\n\n- 1000 free api calls per day, good for a small project.\n- Ability to specify different units in requests and receive a formatted response based on the unit\n  i.e imperial/metric etc.\n- Api response can also be modified to include the amount of needed data all with one call ,\n  contains icons for different conditions and has multilingual support if adopting for a wider\n  audience is ever needed.\n- They have a large user base and handle millions of requests, if the app were ever to scale,\n  there's confidence on the api providing high availability.\n- Has capabilities for alerts for severe weather conditions\n\nMore info on how to make an api call [here](https://openweathermap.org/api/one-call-3#multi).\n\n# Pre-requisite 📝\n\nIn your `local.properties` you will need to add your Open Weather API key and copy the urls in.\n\n```properties\nOPEN_WEATHER_API_KEY=YOUR KEY\nOPEN_WEATHER_BASE_URL=https://api.openweathermap.org\nOPEN_WEATHER_ICONS_URL=https://openweathermap.org/img/wn/\n```\n\nCheck for one under  [`Api Keys`](https://home.openweathermap.org/api_keys)\n\n*Environment*\n\n- Built on A.S Hedgehog+\n- JDK 17\n\n# Design/Architectural decisions 📐\n\nThe project makes use of common android patterns in modern android codebases.\n\n**Project Structure**\n\nThe folders are split into 4 boundaries:\n\u003cdetails\u003e\n  \u003csummary\u003eCore\u003c/summary\u003e \n Contains the models/data classes that are independent of any framework specific dependencies and represent the business logic. \n   In a Clean Arch world you can consider these as your domain classes and interfaces.\n\u003c/details\u003e\n\n\u003cdetails\u003e\n  \u003csummary\u003eData\u003c/summary\u003e\n  Contains data sources , local or remote, this is where the implementation for such is kept. All\n  data related actions and formatting happens in this layer as well.\n  It may contain framework related dependencies to orchestrate and create instances of data stores\n  like a database or shared preference etc.\n  One common pattern used in this area is the repository pattern, which mediates data sources and\n  acts as a source of truth to the consumer.\n\u003c/details\u003e\n\n\u003cdetails\u003e\n  \u003csummary\u003eDI\u003c/summary\u003e\n  This acts as the glue between the core ,data and UI.The UI relies on the core models and\n  interfaces which are implemented in data.\n\u003c/details\u003e\n\n\u003cdetails\u003e\n  \u003csummary\u003eUI\u003c/summary\u003e\n  Contains the presentation layer of the app, the screen components and viewmodels. Framework\n  specific dependencies are best suited for this layer.\n  In this layer MVI is also used, it looks similar to MVVM but the difference is the actions from a\n  screen a.k.a intents e.g ```HomeScreenIntent``` are predefined and are finite,making the\n  the screen state a bit more predictable and it's easier to scan through what actions are possible\n  from a given screen.\n\nThe screen state e.g ```HomeScreenViewState``` is also modelled as a class with immutable\nproperties and makes state management way easier by reducing the state whenever their is a new\nupdate received.\nSome design patterns that can be seen here are the Observer pattern when consuming the flow -\u003e\nstate flows in the composables and provides a reactive app.\n\u003c/details\u003e\n\n![Add flow diagram here](/docs/MVI.png)\n\n\u003cdetails\u003e\n  \u003csummary\u003eTesting\u003c/summary\u003e\n\nThe data layer is unit tested by mocking out external dependencies and the ui layer on the\nviewmodels, an integration test is written that makes use of fake,so as to mimic the real scenario\nas much as possible over using mocks, which would also turn it to a unit test.\n\u003c/details\u003e\n\n# Other Stuff 📦\n\n*Code style*\n\nFor now there is no strict adherence to a code style, but the project is formatted using the default\nandroid studio formatter.\nYou can run `./gradlew detekt` to check for any code smells and `./gradlew ktlint` to check for any\nlinting issues.\nAlternatively for ktlint\nthe [IDE plugin](https://pinterest.github.io/ktlint/latest/install/setup/#recommended-setup) might\nbe a much better option :)\nOr setting up\na [pre-commit/pre-push hook](https://pinterest.github.io/ktlint/latest/install/cli/#git-hooks) to\nrun the checks before a commit is made or pushed.\n\n*CI/CD*\n\nThe project is built on Bitrise and the workflow is maintained from its dashboard.Github actions are\nresponsible for\ndependency updates and running code quality checks ,defined in the `.github/workflows` folder.\n\n*Design System*\n\nUnder the `designsystem` package ,it follows a tiered approach to styling the app i.e\n\u003cdetails\u003e\n\u003csummary\u003eAtoms (Smallest Components)\u003c/summary\u003e\nTypography:\nDefine font styles, sizes, and weights for headers, paragraphs, and other text elements.\n\nColor Palette:\nEstablish a color palette with primary, secondary, and accent colors. Specify their usage in\ndifferent contexts.\n\nIcons:\nDesign a set of basic icons that represent common actions or concepts. Ensure consistency in style\nand sizing.\n\nButtons:\nCreate button styles with variations for primary, secondary, and tertiary actions. Include states\nlike hover and disabled.\n\nInput Fields:\nDesign consistent styles for text inputs, checkboxes, radio buttons, and other form elements.\n\u003c/details\u003e\n\u003cdetails\u003e\n\u003csummary\u003eMolecules (Simple Components)\u003c/summary\u003e\nForm Elements:\nCombine atoms to create complete form components. Ensure consistency in spacing and alignment.\n\nCards:\nCombine text, images, and buttons to create card components. Define variations for different use\ncases.\n\nBadges:\nAssemble icons and text to create badge components for notifications or status indicators.\n\nAvatars:\nDesign avatar components for user profiles, incorporating images or initials.\n\u003c/details\u003e\n\u003cdetails\u003e\n\u003csummary\u003eOrganisms (Complex Components)\u003c/summary\u003e\nNavigation Bars:\nCreate a consistent navigation bar design that includes menus, icons, and navigation elements.\n\nHeaders and Footers:\nDefine headers and footers with appropriate spacing, logos, and navigation links.\n\nLists:\nAssemble atoms and molecules to create list components, incorporating variations like simple lists,\ndetailed lists, and nested lists.\n\nModals:\nDesign modal components for overlays or pop-ups, ensuring consistency in styles and behavior.\n\u003c/details\u003e\n\u003cdetails\u003e\n\u003csummary\u003eTemplates (Page-Level Structures)\u003c/summary\u003e\nPage Layouts:\nEstablish consistent layouts for different types of pages (e.g., home page, product page, settings \npage).\n\nGrid Systems:\nDefine grid systems that ensure alignment and consistency across various screen sizes.\n\u003c/details\u003e\n\n*Performance*\n\nThe app is monitored using Firebase Performance and Crashlytics,for performance it's using the\ndefault\ntraces but can be extended as the app grows to monitor specific parts of the app that might be slow.\nLeakCanary is also used to monitor for any memory leaks that might occur in debug mode.\n\n*Build Times*\n\nThe current CI build time , factoring in the project size, the number of tests etc.\n\n| Task                                    | Avg Time |\n|-----------------------------------------|----------|\n| Build -  Bitrise                        | 4m 30s   |\n| Code Analysis    - Github Actions       | 6m 30s   |\n| Update Dependencies    - Github Actions | 8m 30s   |\n\n# Technologies 🔨\n\n**Language :** [Kotlin](https://github.com/JetBrains/kotlin)\n\n**Libraries :**\n\u003cdetails\u003e\n  \u003csummary\u003eUI\u003c/summary\u003e \n  \u003ca href=\"https://developer.android.com/jetpack/compose\"\u003eCompose\u003c/a\u003e\u003cbr\u003e\n  \u003ca href=\"https://coil-kt.github.io/coil/compose/\"\u003eCoil\u003c/a\u003e\u003cbr\u003e\n  \u003ca href=\"https://developer.android.com/guide/playcore/in-app-updates\"\u003eInAppUpdate\u003c/a\u003e\n\u003c/details\u003e\n\n\u003cdetails\u003e\n  \u003csummary\u003eData\u003c/summary\u003e \n  \u003ca href=\"https://square.github.io/retrofit/\"\u003eRetrofit\u003c/a\u003e\u003cbr\u003e\n  \u003ca href=\"https://square.github.io/okhttp/\"\u003eOkHTTP\u003c/a\u003e\u003cbr\u003e\n  \u003ca href=\"https://kotlinlang.org/docs/serialization.html\"\u003ekotlinx.serialization\u003c/a\u003e\u003cbr\u003e\n  \u003ca href=\"https://developer.android.com/topic/libraries/architecture/datastore\"\u003ePreference Data Store\u003c/a\u003e\n\u003c/details\u003e\n\n\u003cdetails\u003e\n  \u003csummary\u003eTesting\u003c/summary\u003e \n  \u003ca href=\"https://junit.org/junit4/\"\u003eJUnit\u003c/a\u003e\u003cbr\u003e\n  \u003ca href=\"https://mockk.io/\"\u003eMockk\u003c/a\u003e\u003cbr\u003e\n  \u003ca href=\"https://truth.dev/\"\u003eTruth\u003c/a\u003e\u003cbr\u003e\n  \u003ca href=\"https://github.com/cashapp/turbine\"\u003eTurbine\u003c/a\u003e\n\u003c/details\u003e\n\n\u003cdetails\u003e\n  \u003csummary\u003eTooling/Project setup\u003c/summary\u003e\n  \u003ca href=\"https://github.com/google/secrets-gradle-plugin\"\u003eGradle secrets plugin\u003c/a\u003e\u003cbr\u003e\n  \u003ca href=\"https://developer.android.com/training/dependency-injection/hilt-android\"\u003eHilt (DI)\u003c/a\u003e\u003cbr\u003e\n  \u003ca href=\"https://firebase.google.com/docs\"\u003eFirebase - Crashlytics, Performance\u003c/a\u003e\u003cbr\u003e\n  \u003ca href=\"https://www.bitrise.io/\"\u003eBitrise\u003c/a\u003e\u003cbr\u003e\n  \u003ca href=\"https://about.codecov.io/\"\u003eCodecov\u003c/a\u003e\u003cbr\u003e\n  \u003ca href=\"https://github.com/detekt/detekt\"\u003eDetekt\u003c/a\u003e\u003cbr\u003e\n  \u003ca href=\"https://ktlint.github.io/\"\u003eKtlint\u003c/a\u003e\u003cbr\u003e\n  \u003ca href=\"https://square.github.io/leakcanary/\"\u003eLeakCanary\u003c/a\u003e\u003cbr\u003e\n  \u003ca href=\"https://github.com/mikepenz/AboutLibraries\"\u003eAbout Libraries\u003c/a\u003e\u003cbr\u003e\n  \u003ca href=\"https://kotlinlang.org/docs/multiplatform.html\"\u003eKMM\u003c/a\u003e\n\u003c/details\u003e\n\n# LICENSE\n\n```\n   Copyright 2023 David Odari\n\n   Licensed under the Apache License, Version 2.0 (the \"License\");\n   you may not use this file except in compliance with the License.\n   You may obtain a copy of the License at\n\n       http://www.apache.org/licenses/LICENSE-2.0\n\n   Unless required by applicable law or agreed to in writing, software\n   distributed under the License is distributed on an \"AS IS\" BASIS,\n   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n   See the License for the specific language governing permissions and\n   limitations under the License.\n   \n```\n\n# Screenshots 📱\n\n|                               Light Theme                                |                               Dark Theme                                |\n|:------------------------------------------------------------------------:|:-----------------------------------------------------------------------:|\n|       \u003cimg src=\"/docs/screenshots/(Light)Main.png\" width=\"250px\"\u003e        |       \u003cimg src=\"/docs/screenshots/(Dark)Main.png\" width=\"250px\"\u003e        |\n|     \u003cimg src=\"/docs/screenshots/(Light)Settings.png\" width=\"250px\"\u003e      |     \u003cimg src=\"/docs/screenshots/(Dark)Settings.png\" width=\"250px\"\u003e      |\n| \u003cimg src=\"/docs/screenshots/(Light)Settings-Exclude.png\" width=\"250px\"\u003e  | \u003cimg src=\"/docs/screenshots/(Dark)Settings-Exclude.png\" width=\"250px\"\u003e  |\n|       \u003cimg src=\"/docs/screenshots/(Light)About.png\" width=\"250px\"\u003e       |       \u003cimg src=\"/docs/screenshots/(Dark)About.png\" width=\"250px\"\u003e       |\n|       \u003cimg src=\"/docs/screenshots/(Light)Error.png\" width=\"250px\"\u003e       |       \u003cimg src=\"/docs/screenshots/(Dark)Error.png\" width=\"250px\"\u003e       |\n| \u003cimg src=\"/docs/screenshots/(Light)Settings-Language.png\" width=\"250px\"\u003e | \u003cimg src=\"/docs/screenshots/(Dark)Settings-Language.png\" width=\"250px\"\u003e |\n|   \u003cimg src=\"/docs/screenshots/(Light)Settings-Time.png\" width=\"250px\"\u003e   |   \u003cimg src=\"/docs/screenshots/(Dark)Settings-Time.png\" width=\"250px\"\u003e   |\n|         \u003cimg src=\"/docs/screenshots/Excluded.png\" width=\"250px\"\u003e         |                                    -                                    |\n\n![](https://media.giphy.com/media/hWvk9iUU4uBBeyBq0k/giphy.gif)\n\n\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fodaridavid%2Fweatherapp","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fodaridavid%2Fweatherapp","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fodaridavid%2Fweatherapp/lists"}