{"id":13609844,"url":"https://github.com/hadiyarajesh/flower","last_synced_at":"2025-04-12T22:32:02.730Z","repository":{"id":38361376,"uuid":"271244717","full_name":"hadiyarajesh/flower","owner":"hadiyarajesh","description":"Flower simplifies networking and database caching on Android/Multiplatform.","archived":false,"fork":false,"pushed_at":"2023-12-17T12:17:38.000Z","size":666,"stargazers_count":297,"open_issues_count":2,"forks_count":29,"subscribers_count":5,"default_branch":"master","last_synced_at":"2024-11-07T16:41:08.535Z","etag":null,"topics":["android","kotlin","multiplatform"],"latest_commit_sha":null,"homepage":"","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/hadiyarajesh.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":".github/FUNDING.yml","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},"funding":{"github":["hadiyarajesh","DatL4g"]}},"created_at":"2020-06-10T10:15:31.000Z","updated_at":"2024-10-03T07:05:40.000Z","dependencies_parsed_at":"2023-12-17T12:43:17.645Z","dependency_job_id":null,"html_url":"https://github.com/hadiyarajesh/flower","commit_stats":null,"previous_names":[],"tags_count":8,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/hadiyarajesh%2Fflower","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/hadiyarajesh%2Fflower/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/hadiyarajesh%2Fflower/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/hadiyarajesh%2Fflower/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/hadiyarajesh","download_url":"https://codeload.github.com/hadiyarajesh/flower/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":248640782,"owners_count":21138093,"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","kotlin","multiplatform"],"created_at":"2024-08-01T19:01:38.630Z","updated_at":"2025-04-12T22:32:01.971Z","avatar_url":"https://github.com/hadiyarajesh.png","language":"Kotlin","funding_links":["https://github.com/sponsors/hadiyarajesh","https://github.com/sponsors/DatL4g"],"categories":["Libraries","Kotlin"],"sub_categories":["📦 Storage"],"readme":"\u003cp align=\"center\"\u003e\n    \u003cpicture\u003e\n      \u003csource media=\"(prefers-color-scheme: dark)\" srcset=\"asset/flower-logo_white.png\"\u003e\n      \u003cimg width=\"300\" height=\"300\" src=\"asset/flower-logo_black.png\"\u003e\n    \u003c/picture\u003e\n\u003c/p\u003e\n\nFlower is a Kotlin multi-platform (originally, Android) library that makes networking and database caching easy. It enables developers to\nfetch network resources and use them as is OR combine them with local database at single place with\nfault-tolerant architecture.\n\n![release](https://img.shields.io/github/v/release/hadiyarajesh/flower)\n![contributors](https://img.shields.io/github/contributors/hadiyarajesh/flower)\n\n## Special thanks\nA special thanks to [JetBrains](https://www.jetbrains.com/community/opensource) for sponsoring the [tools](https://www.jetbrains.com/lp/intellij-frameworks) required to develop this project.\n\u003cp \u003e\n    \u003cpicture\u003e\n      \u003csource media=\"(prefers-color-scheme: dark)\" srcset=\"asset/flower-logo_white.png\"\u003e\n      \u003cimg width=\"100\" height=\"100\" src=\"https://resources.jetbrains.com/storage/products/company/brand/logos/jb_beam.png\"\u003e\n    \u003c/picture\u003e\n\u003c/p\u003e\n\n\n## Why Flower?\n\n- It helps you to handle different states (`Loading`, `Success`, `EmptySuccess`, `Error`) of\n  resources efficiently.\n- It helps you to use local data in case of network unavailability.\n- It provides a fluid app experience by not blocking the `main thread` when accessing\n  network/database resources.\n\nFlower is recognised by [Google Dev Library](https://devlibrary.withgoogle.com/products/android/repos/hadiyarajesh-flower), a showcase of open-source projects.\n\n## Installation\n\nFlower is primarily available in two modules, one for **Ktorfit** and the other for **Retrofit**.\n\nIf you want to handle networking yourself, you can also use the **core** module.\n\n`$flowerVersion=3.1.0`\n\u003cbr\u003e\n`$ktorFitVersion=1.0.0-beta16`\n\u003cbr\u003e\n`$retrofitVersion=2.9.0`\n\n### Ktorfit\nThis is a multiplatform module. It is suitable for use in Kotlin multiplatform projects, Android (Apps/Libraries), the JVM in general, Kotlin-JS, and so on...\n\nIt uses and provides [Ktorfit](https://github.com/Foso/Ktorfit) and you must use KSP in your project.\n\nApply the KSP Plugin to your project:\n```gradle\nplugins {\n  id(\"com.google.devtools.ksp\") version \"1.7.10-1.0.6\"\n}\n```\n\n**Multiplatform example**\n```gradle\ndependencies {\n    implementation(\"io.github.hadiyarajesh.flower-ktorfit:flower-ktorfit:$flowerVersion\")\n\n    add(\"kspCommonMainMetadata\", \"de.jensklingenberg.ktorfit:ktorfit-ksp:$ktorFitVersion\")\n    add(\"kspJvm\", \"de.jensklingenberg.ktorfit:ktorfit-ksp:$ktorFitVersion\")\n    add(\"kspAndroid\", \"de.jensklingenberg.ktorfit:ktorfit-ksp:$ktorFitVersion\")\n    add(\"kspIosX64\", \"de.jensklingenberg.ktorfit:ktorfit-ksp:$ktorFitVersion\")\n    add(\"kspJs\", \"de.jensklingenberg.ktorfit:ktorfit-ksp:$ktorFitVersion\")\n    add(\"kspIosSimulatorArm64\", \"de.jensklingenberg.ktorfit:ktorfit-ksp:$ktorFitVersion\")\n}\n```\n\n**Android example**\n```gradle\ndependencies {\n    implementation(\"io.github.hadiyarajesh.flower-ktorfit:flower-ktorfit:$flowerVersion\")\n    //Ktorfit library\n    implementation(\"de.jensklingenberg.ktorfit:ktorfit-lib:$ktorFitVersion\")\n    ksp(\"de.jensklingenberg.ktorfit:ktorfit-ksp:$ktorFitVersion\")\n}\n```\n\n### Retrofit\nThis is an Android-only module, so it can only be used in Android Apps/Libs.\n\n```gradle\ndependencies {\n    implementation(\"io.github.hadiyarajesh.flower-retrofit:flower-retrofit:$flowerVersion\")\n    // Retrofit library\n    implementation(\"com.squareup.retrofit2:retrofit:$retrofitVersion\")\n}\n```\n\n### Core\nThis module only contains the _core code_ and allows you to handle the networking yourself.\n\n**We Highly recommend you to use either Ktorfit or Retrofit module. Only use this if you don't want\nto rely on Ktorfit or Retrofit.**\n\n```gradle\ndependencies {\n    implementation(\"io.github.hadiyarajesh:flower-core:$flowerVersion\")\n}\n```\n\n## Usage\n\nAssume you have a model class called `MyModel` that you are retrieving from the network.\n\n```kotlin\ndata class MyModel(\n    val id: Long,\n    val data: String\n)\n```\n\n### Prerequisite\n- If you want to save network response in a local database, your database caching system function must return a value of type `Flow\u003cMyModel\u003e`.\n\n**Android Room example:**\n\n```kotlin\n@Dao\ninterface MyDao {\n    @Query(\"SELECT * FROM Data\")\n    fun getLocalData(): Flow\u003cMyModel\u003e\n}\n```\n\n- Return type of networking api function must be `ApiResponse\u003cMyModel\u003e` (\n  or `Flow\u003cApiResponse\u003cMyModel\u003e\u003e` if you're retrieving a flow of data from server)\n\n**Ktorfit/Retrofit example:**\n```kotlin\ninterface MyApi {\n    @GET(\"data\")\n    suspend fun getRemoteData(): ApiResponse\u003cMyModel\u003e\n    // OR\n    @GET(\"data\")\n    fun getRemoteData(): Flow\u003cApiResponse\u003cMyModel\u003e\u003e\n}\n```\n\n\u003cbr\u003e\u003c/br\u003e\n#### 1. Add CallAdapterFactory/ResponseConverter in networking client\n\n**Ktorfit**\nAdd `FlowerResponseConverter` as *ResponseConverter* in Ktorfit builder.\n\n```kotlin\nKtorfit.Builder()\n  .baseUrl(BASE_URL)\n  .httpClient(ktorClient)\n  .responseConverter(FlowerResponseConverter())\n  .build()\n```\n\n**Retrofit**\nAdd `FlowerCallAdapterFactory` as *CallAdapterFactory* in Retrofit builder\n\n```kotlin\nRetrofit.Builder()\n    .baseUrl(BASE_URL)\n    .client(okHttpClient)\n    .addCallAdapterFactory(FlowerCallAdapterFactory.create())\n    .build()\n```\n\n\u003cbr\u003e\u003c/br\u003e\n#### 2. Make network request (and save data) in Repository\n\n**2.1** If you want to fetch network resources and save into local database, \nuse `dbBoundResource()` higher order function \n(or `dbBoundResourceFlow()` function if you're retrieving a flow of data from server). \nIt takes following functions as parameters.\n\n- *fetchFromLocal* -  A function to retrieve data from local database\n- *shouldMakeNetworkRequest* - Decides whether or not to make network request\n- *makeNetworkRequest* - A function to make network request\n- *processNetworkResponse* - A function to process network response (e.g., saving response headers before saving actual data)\n- *saveResponseData* - A function to save network response (`MyModel`) to local database\n- *onNetworkRequestFailed* - An action to perform when a network request fails\n\n```kotlin\nfun getMyData(): Flow\u003cResource\u003cMyModel\u003e\u003e {\n    return dbBoundResources(\n        fetchFromLocal = { myDao.getLocalData() },\n        shouldMakeNetworkRequest = { localData -\u003e localData == null },\n        makeNetworkRequest = { myApi.getRemoteData() },\n        processNetworkResponse = { },\n        saveResponseData = { myDao.saveMyData(it) },\n        onNetworkRequestFailed { errorMessage, statusCode -\u003e }\n    ).flowOn(Dispatchers.IO)\n}\n```\n\n**OR**\n\n**2.2** If you only want to fetch network resources without saving it in local database, \nuse `networkResource()` higher order function.\n(or `networkResourceFlow()` function if you're retrieving a flow of data from server)\n\n```kotlin\nfun getMyData(): Flow\u003cResource\u003cMyModel\u003e\u003e {\n    return networkResource(\n        makeNetworkRequest = { myApi.getRemoteData() },\n        onNetworkRequestFailed { errorMessage, statusCode -\u003e }\n    ).flowOn(Dispatchers.IO)\n}\n```\n\n\u003cbr\u003e\u003c/br\u003e\n#### 3. Collect `Flow` to observe different state of resources (`Loading`, `Success`, `Error`) In ViewModel\n\n```kotlin\n// A model class to re-present UI state\nsealed class UiState\u003cout T\u003e {\n    object Empty : UiState\u003cNothing\u003e()\n    data class Loading(val data: T?) : UiState\u003cout T\u003e()\n    data class Success\u003cout T\u003e(val data: T \u0026 Any) : UiState\u003cT \u0026 Any\u003e()\n    data class Error(val msg: String?) : UiState\u003cNothing\u003e()\n}\n```\n\n```kotlin\nprivate val _myData: MutableStateFlow\u003cUiState\u003cMyModel\u003e\u003e = MutableStateFlow(UiState.Empty)\nval myData: StateFlow\u003cUiState\u003cMyModel\u003e\u003e = _myData.asStateFlow()\n\ninit {\n    viewModelScope.launch {\n        getMyData()\n    }\n}\n\nsuspend fun getMyData() = repository.getMyData().collect { response -\u003e\n    when (response.status) {\n        is Resource.Status.Loading -\u003e {\n            val status = response.status as Resource.Status.Loading\n            _myData.value = UiState.Loading(status.data)\n        }\n      \n        is Resource.Status.Success -\u003e {\n            val status = response.status as Resource.Status.Success\n            _myData.value = UiState.Success(status.data)\n        }\n      \n        // EmptySuccess is for potentially body-less successful HTTP responses like 201, 204\n        is Resource.Status.EmptySuccess -\u003e {\n            _myData.value = UiState.Empty\n        }\n      \n        is Resource.Status.Error -\u003e {\n            val status = response.status as Resource.Status.Error\n            _myData.value = UiState.Error(status.message)\n        }\n    }\n}\n```\n\n\u003cbr\u003e\u003c/br\u003e\n#### 4. Observe data in Activity/Fragment/Composable function to drive UI changes\n\n```kotlin\nlifecycleScope.launchWhenStarted {\n    viewModel.myData.collect { data -\u003e\n        when (data) {\n            is UiState.Loading -\u003e {\n                // Show loading\n            }\n            is UiState.Success -\u003e {\n                // Show success\n            }\n            is UiState.Error -\u003e {\n                // Show error\n            }\n            else -\u003e {}\n        }\n    }\n}\n```\n\n## Sample\n\nTwo sample apps are provided in this repository\n1. [XML View based app](https://github.com/hadiyarajesh/flower/tree/master/app) - It fetch random quote from remote api and save it to local database in order to display it on UI.\n2. [Jetpack Compose based app](https://github.com/hadiyarajesh/flower/tree/master/compose-app) - It fetches unsplash images from [Picsum](https://picsum.photos) and display it on UI.\n\n\n## License\n\n[Apache License 2.0](https://github.com/hadiyarajesh/flower/blob/master/LICENSE)\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fhadiyarajesh%2Fflower","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fhadiyarajesh%2Fflower","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fhadiyarajesh%2Fflower/lists"}