Flower - Super cool Android library to manage networking and database caching with ease

Overview

Flower

Super cool Android library to manage networking and database caching with ease. It allows developers to use remote resources on-the-fly OR Combine remote resources and local persistence data caching in single place with fault tolerant architecture. It's built on top of Retrofit and Kotlin Flow.

Why do we even need this library?

  • It helps you to handle different states of resources (LOADING, SUCCESS, FAILURE) efficiently.
  • It does not block main thread while accessing network/database resources, thus providing fluid experience for your users.
  • It leverage Kotlin idiomatic api, thus doing all hard stuffs behind a simple function call.

You can find companion medium article here

Installation

Add Gradle dependency as below

dependencies {
    //Groovy DSL
    implementation "io.github.hadiyarajesh:flower:2.0.0"
    Kotlin DSL
    implementation("io.github.hadiyarajesh:flower:2.0.0")
}

Usage

Prerequisite

  • Your Room DAO method must return Flow (If you're using database caching)
  • Your Retrofit API method must return Flow >



1. Add FlowCallAdapterFactory as CallAdapterFactory in Retrofit builder

Retrofit.Builder()
    .baseUrl(BASE_API_URL)
    .client(okHttpClient)
    .addCallAdapterFactory(FlowCallAdapterFactory.create())
    .build()



2. Inside Repository

If you want to use remote resources backed by local database caching, use networkBoundResource() function. This function takes following functions as lambda parameter

  • fetchFromLocal - It retrieve data from local database
  • shouldFetchFromRemote - It decide whether network request should be made or use local persistent data if available
  • fetchFromRemote - It retrieve data from network request
  • processRemoteResponse - It process process result of network request. (e.g., save response headers)
  • saveRemoteData - It saves result of network request to local persistent database
  • onFetchFailed - It perform provided actions when network request fails (Non HTTP 200..300 response, exceptions etc)
fun getSomething(): Flow<Resource<YourModelclass>> {
    return networkBoundResources(
        fetchFromLocal = { yourDaoclass.getFromDatabase() },
        shouldFetchFromRemote = { it == null },
        fetchFromRemote = { apiInterface.getFromRemote() },
        processRemoteResponse = { },
        saveRemoteData = { yourDaoclass.saveYourData(it) },
        onFetchFailed {_, _ -> }
    ).flowOn(Dispatchers.IO)
}

OR

If you want to use remote resources on-the-fly (without caching into local database), use networkResource() function

fun getSomething(): Flow<Resource<YourModelclass>> {
    return networkBoundResources(
        fetchFromRemote = { apiInterface.getFromRemote() },
        onFetchFailed {_, _ -> }
    ).flowOn(Dispatchers.IO)
}



3. Inside ViewModel

Collect/transform flow to get different state of resources: LOADING, SUCCESS or ERROR

val someVariable: LiveData<Resource<YourModelClass>> = repository.getSomething().map {
    when (it.status) {
        Resource.Status.LOADING -> {
            Resource.loading(null)
        }
        Resource.Status.SUCCESS -> {
            Resource.success(it.data)
        }
        Resource.Status.ERROR -> {
            Resource.error(it.message!!, null)
        }
    }
}.asLiveData(viewModelScope.coroutineContext)



4. Inside Activity/Fragment

Observe it in Activity/Fragment to make UI changes

viewModel.someVariable.observer(this, Observer {
    when (it.status) {
        Resource.Status.LOADING -> {
            //Show loading message
        }
        Resource.Status.SUCCESS -> {
            //Show success message
        }
        Resource.Status.ERROR -> {
            //Show error message
        }
    }
})

Sample

Sample app is provided in this repository. It fetch random quote from remote api and save it to local persistent database in order to display it on UI.

License

MIT License

Comments
  • Improved Resource status information

    Improved Resource status information

    This simplifies error handling and provides needed information

    • changed Resource Status to sealed class
    • only pass error message in error status
    • error message is no longer nullable and always present
    • added statusCode to error status
    opened by DATL4G 13
  • Error class without data

    Error class without data

    Currently, Error class is having access to nullable data property

    We're providing local data in case of network error response, but I believe we should remove that data field and Error class should only contains errorMsg and status code.

    Additionally, planning use errorMsg and statusCode as value class instead of primitives.

    @DATL4G what are your thougths?

    enhancement 
    opened by hadiyarajesh 4
  • Need help for return list

    Need help for return list

    as you know getMyData() from repo return Flow<Resource> but I want to return Flow<Resource<List>> mean list of PostEntity but is give me error so you have any suggestion for that?

    opened by Android1500 3
  • Multiplatform Migration

    Multiplatform Migration

    • migrated the base project to multiplatform.
    • renamed "Flower" to "flower-core"
    • Success data is a Definitely Non-Nullable Type now
    • return Empty if Success is empty
    • all resource status types are classes now
    • renamed "networkBoundResource" to "dbBoundResource"
    • renamed "shouldFetchFromRemote" to "shouldMakeNetworkRequest"
    • renamed "fetchFromRemote" to "makeNetwotkRequest"
    • renamed "processRemoteResponse" to "processRequestResponse"
    • renamed "saveRemoteData" to "saveRequestData"
    • renamed "onFetchFailed" to "onRequestFailed"
    • added Ktorfit compatibility
    • removed gradle-nexus-plugin
    • README image is now white if darkmode is enabled
    • gradlew is executable now (unix systems)
    opened by DATL4G 3
  • πŸš€ Added support for Android SDK 31 & more

    πŸš€ Added support for Android SDK 31 & more

    Summary

    πŸš€ Added support for Android SDK 31 πŸ˜„ Updated dependencies to the latest stable versions & left the kotlin-gradle-plugin to version 1.5.31, because the latest 1.6.10 causes bugs & is not yet fully supported by certain dependencies in thi project πŸ‘Œ Tested the whole project in portrait & landscape mode on an Android Studio Emulator & it works just fine

    opened by germainkevinbusiness 3
  • can demo use flower-ktorfit?

    can demo use flower-ktorfit?

    Dear Sir

    i follow "ktorfit"

    as the issus https://github.com/Foso/Ktorfit/issues/85

    you say https://github.com/Foso/Ktorfit/issues/85#issuecomment-1294224913

    so i go to here

    can demo how use flower-ktorfit ?

    THX

    help wanted 
    opened by CMingTseng 2
  • Fixed maven publishing

    Fixed maven publishing

    The publishing process of the gradle plugin changed.

    I fixed the modules. You need to publish the modules separatly like this:

    ./gradlew :flower-core:publish
    ./gradlew :flower-retrofit:publish
    ./gradlew :flower-ktorfit:publish
    

    I've already tested it (under my repository) and the packages are available like this:

    implementation("dev.datlag.flower-ktorfit:flower-core:3.0.0-beta02")
    implementation("dev.datlag.flower-ktorfit:flower-retrofit:3.0.0-beta02")
    implementation("dev.datlag.flower-ktorfit:flower-ktorfit:3.0.0-beta02")
    

    Of course you only need either the Retrofit package or the Ktorfit package. The core module is referenced and available in these.

    opened by DATL4G 2
  • build error after upgrade from 2.0.0 to 2.0.1

    build error after upgrade from 2.0.0 to 2.0.1

    `java.lang.IllegalStateException: SimpleTypeImpl should not be created for error type: ErrorScope{Error scope for class with arguments: org.jetbrains.kotlin.types.IndexedParametersSubstitution@2d4ec75b} [ERROR : Resource] at org.jetbrains.kotlin.types.SimpleTypeImpl.(KotlinTypeFactory.kt:225) at org.jetbrains.kotlin.types.KotlinTypeFactory.simpleTypeWithNonTrivialMemberScope(KotlinTypeFactory.kt:155) at org.jetbrains.kotlin.types.KotlinTypeFactory.simpleType(KotlinTypeFactory.kt:82) at org.jetbrains.kotlin.types.KotlinTypeFactory.simpleType$default(KotlinTypeFactory.kt:71) at org.jetbrains.kotlin.types.TypeSubstitutionKt.replace(TypeSubstitution.kt:167) at org.jetbrains.kotlin.types.TypeSubstitutionKt.replace(TypeSubstitution.kt:152) at org.jetbrains.kotlin.types.TypeSubstitutionKt.replace$default(TypeSubstitution.kt:140) at org.jetbrains.kotlin.kapt3.util.TypeUtilsKt.replaceAnonymousTypeWithSuperType(typeUtils.kt:53) at org.jetbrains.kotlin.kapt3.util.TypeUtilsKt.replaceAnonymousTypeWithSuperType(typeUtils.kt:50) at org.jetbrains.kotlin.kapt3.KaptAnonymousTypeTransformer.transformAnonymousType(KaptAnonymousTypeTransformer.kt:27) at org.jetbrains.kotlin.resolve.DescriptorResolver.transformAnonymousTypeIfNeeded(DescriptorResolver.java:1064) at org.jetbrains.kotlin.resolve.DescriptorResolver.lambda$inferReturnTypeFromExpressionBody$5(DescriptorResolver.java:1271) at org.jetbrains.kotlin.storage.LockBasedStorageManager$LockBasedLazyValue.invoke(LockBasedStorageManager.java:408) at org.jetbrains.kotlin.storage.LockBasedStorageManager$LockBasedNotNullLazyValue.invoke(LockBasedStorageManager.java:527) at org.jetbrains.kotlin.types.DeferredType.getDelegate(DeferredType.java:106) at org.jetbrains.kotlin.types.WrappedType.getAnnotations(KotlinType.kt:126) at org.jetbrains.kotlin.resolve.lazy.ForceResolveUtil.forceResolveAllContents(ForceResolveUtil.java:109) at org.jetbrains.kotlin.resolve.lazy.ForceResolveUtil.doForceResolveAllContents(ForceResolveUtil.java:96) at org.jetbrains.kotlin.resolve.lazy.ForceResolveUtil.forceResolveAllContents(ForceResolveUtil.java:42) at org.jetbrains.kotlin.resolve.lazy.ForceResolveUtil.forceResolveAllContents(ForceResolveUtil.java:52) at org.jetbrains.kotlin.resolve.lazy.ForceResolveUtil.forceResolveAllContents(ForceResolveUtil.java:47) at org.jetbrains.kotlin.resolve.lazy.descriptors.LazyClassDescriptor.doForceResolveAllContents(LazyClassDescriptor.java:669) at org.jetbrains.kotlin.resolve.lazy.descriptors.LazyClassDescriptor.lambda$new$4(LazyClassDescriptor.java:220) at org.jetbrains.kotlin.storage.LockBasedStorageManager$LockBasedLazyValue.invoke(LockBasedStorageManager.java:408) at org.jetbrains.kotlin.resolve.lazy.descriptors.LazyClassDescriptor.forceResolveAllContents(LazyClassDescriptor.java:657) at org.jetbrains.kotlin.resolve.lazy.ForceResolveUtil.doForceResolveAllContents(ForceResolveUtil.java:78) at org.jetbrains.kotlin.resolve.lazy.ForceResolveUtil.forceResolveAllContents(ForceResolveUtil.java:42) at org.jetbrains.kotlin.resolve.jvm.extensions.PartialAnalysisHandlerExtension$doAnalysis$1.invoke(PartialAnalysisHandlerExtension.kt:72) at org.jetbrains.kotlin.resolve.jvm.extensions.PartialAnalysisHandlerExtension$doAnalysis$1.invoke(PartialAnalysisHandlerExtension.kt:67) at org.jetbrains.kotlin.resolve.jvm.extensions.PartialAnalysisHandlerExtension.doForEachDeclaration(PartialAnalysisHandlerExtension.kt:123) at org.jetbrains.kotlin.resolve.jvm.extensions.PartialAnalysisHandlerExtension.doForEachDeclaration(PartialAnalysisHandlerExtension.kt:138) at org.jetbrains.kotlin.resolve.jvm.extensions.PartialAnalysisHandlerExtension.doAnalysis(PartialAnalysisHandlerExtension.kt:67) at org.jetbrains.kotlin.kapt3.AbstractKapt3Extension.doAnalysis(Kapt3Extension.kt:152) at org.jetbrains.kotlin.cli.jvm.compiler.TopDownAnalyzerFacadeForJVM.analyzeFilesWithJavaIntegration(TopDownAnalyzerFacadeForJVM.kt:123) at org.jetbrains.kotlin.cli.jvm.compiler.TopDownAnalyzerFacadeForJVM.analyzeFilesWithJavaIntegration$default(TopDownAnalyzerFacadeForJVM.kt:99) at org.jetbrains.kotlin.cli.jvm.compiler.KotlinToJVMBytecodeCompiler$analyze$1.invoke(KotlinToJVMBytecodeCompiler.kt:301) at org.jetbrains.kotlin.cli.jvm.compiler.KotlinToJVMBytecodeCompiler$analyze$1.invoke(KotlinToJVMBytecodeCompiler.kt:55) at org.jetbrains.kotlin.cli.common.messages.AnalyzerWithCompilerReport.analyzeAndReport(AnalyzerWithCompilerReport.kt:113) at org.jetbrains.kotlin.cli.jvm.compiler.KotlinToJVMBytecodeCompiler.analyze(KotlinToJVMBytecodeCompiler.kt:292) at org.jetbrains.kotlin.cli.jvm.compiler.KotlinToJVMBytecodeCompiler.compileModules$cli(KotlinToJVMBytecodeCompiler.kt:102) at org.jetbrains.kotlin.cli.jvm.compiler.KotlinToJVMBytecodeCompiler.compileModules$cli$default(KotlinToJVMBytecodeCompiler.kt:60) at org.jetbrains.kotlin.cli.jvm.K2JVMCompiler.doExecute(K2JVMCompiler.kt:172) at org.jetbrains.kotlin.cli.jvm.K2JVMCompiler.doExecute(K2JVMCompiler.kt:54) at org.jetbrains.kotlin.cli.common.CLICompiler.execImpl(CLICompiler.kt:91) at org.jetbrains.kotlin.cli.common.CLICompiler.execImpl(CLICompiler.kt:43) at org.jetbrains.kotlin.cli.common.CLITool.exec(CLITool.kt:93) at org.jetbrains.kotlin.incremental.IncrementalJvmCompilerRunner.runCompiler(IncrementalJvmCompilerRunner.kt:471) at org.jetbrains.kotlin.incremental.IncrementalJvmCompilerRunner.runCompiler(IncrementalJvmCompilerRunner.kt:123) at org.jetbrains.kotlin.incremental.IncrementalCompilerRunner.compileIncrementally(IncrementalCompilerRunner.kt:367) at org.jetbrains.kotlin.incremental.IncrementalCompilerRunner.compileIncrementally$default(IncrementalCompilerRunner.kt:309) at org.jetbrains.kotlin.incremental.IncrementalCompilerRunner.compileImpl$rebuild(IncrementalCompilerRunner.kt:115) at org.jetbrains.kotlin.incremental.IncrementalCompilerRunner.compileImpl(IncrementalCompilerRunner.kt:167) at org.jetbrains.kotlin.incremental.IncrementalCompilerRunner.compile(IncrementalCompilerRunner.kt:77) at org.jetbrains.kotlin.daemon.CompileServiceImplBase.execIncrementalCompiler(CompileServiceImpl.kt:623) at org.jetbrains.kotlin.daemon.CompileServiceImplBase.access$execIncrementalCompiler(CompileServiceImpl.kt:101) at org.jetbrains.kotlin.daemon.CompileServiceImpl.compile(CompileServiceImpl.kt:1718) at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method) at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62) at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) at java.base/java.lang.reflect.Method.invoke(Method.java:566) at java.rmi/sun.rmi.server.UnicastServerRef.dispatch(UnicastServerRef.java:359) at java.rmi/sun.rmi.transport.Transport$1.run(Transport.java:200) at java.rmi/sun.rmi.transport.Transport$1.run(Transport.java:197) at java.base/java.security.AccessController.doPrivileged(Native Method) at java.rmi/sun.rmi.transport.Transport.serviceCall(Transport.java:196) at java.rmi/sun.rmi.transport.tcp.TCPTransport.handleMessages(TCPTransport.java:562) at java.rmi/sun.rmi.transport.tcp.TCPTransport$ConnectionHandler.run0(TCPTransport.java:796) at java.rmi/sun.rmi.transport.tcp.TCPTransport$ConnectionHandler.lambda$run$0(TCPTransport.java:677) at java.base/java.security.AccessController.doPrivileged(Native Method) at java.rmi/sun.rmi.transport.tcp.TCPTransport$ConnectionHandler.run(TCPTransport.java:676) at java.base/java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1128) at java.base/java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:628) at java.base/java.lang.Thread.run(Thread.java:834)

    `

    opened by ageback 2
  • How to combine multi flows from different apis?

    How to combine multi flows from different apis?

    I'm developing a weather app, which fetch weather forecast data from different apis such as Daily, Hourly, AirQuality .etc. I can access each api with one flow. Now I want to combine these flows into one and return it in one callback. How can I do it with Flower lib, please?

    opened by ageback 2
  • Suspend function support and default Call ApiResponse

    Suspend function support and default Call ApiResponse

    • added possibility to call suspend functions in networkBoundResource
    • provide retrofit to root project to ensure working with same version without compatibility issues
    • removed passing type Any to FlowCallAdapter
    • fixed some parsing with requirement in FlowCallAdapterFactory when using flow
    • provide ApiResponse for default calls (useful when calling such functions without handling in DB but relying on same service)
    • fixed README typos
    opened by DATL4G 2
  • Enhance sample app

    Enhance sample app

    Updated sample app ✌️ with following features

    • Loading multiple resources (quotes) from API page-by page πŸ”.
    • Updated app to use material design πŸŽ‰ .
    • Also included circular reveal animation on quote card between page change .
    • To show loading state, progress bar ⚑ is shown till quote loads

    https://user-images.githubusercontent.com/34616118/117104706-42aaf100-ad9a-11eb-8dc9-64be5dedea3e.mp4

    opened by piyushjohnson 1
Releases(3.1.0)
  • 3.1.0(Dec 4, 2022)

    ⭐ New Features Added dbResource() to fetch resource from local db only

    ❀️ Other Updated ktorfit Updated dependencies Code cleanup and bug fixes

    Source code(tar.gz)
    Source code(zip)
  • 3.0.0(Sep 6, 2022)

    ⭐ New Features

    • Library is migrated to Kotlin multi-platform project and is available on all targets that Kotlin multi-platform supports.
    • For network requests that return flow of data, dbBoundResource() and networkResource() now contains their Flow counterpart, dbBoundResourceFlow() and networkResourceFlow() respectively.
    • Resource Status contains a new class called EmptySuccess for body-less responses like HTTP 201.
    • Resource Status Success contains Definitely non-nullable types.

    ❀️ Other

    • Updated dependencies
    • Code cleanup and bug fixes
    Source code(tar.gz)
    Source code(zip)
  • 2.0.3(Jun 15, 2022)

    ⭐ New Features

    • Resource Status is converted to sealed class #13
    • Error message is no longer nullable and always present #13
    • Added statusCode to error status #13

    ❀️ Other

    • Updated dependencies
    • Code cleanup
    Source code(tar.gz)
    Source code(zip)
  • 2.0.0(Jul 30, 2021)

    ⭐ New Features

    • Added ability to use network resources on-the-fly without caching them into local database. To use it, invoke networkResource() function instead of networkBoundResource() function.

    ❀️ Other

    • ApiEmptyResponse now returns Resource.success()
    • Repository hosting is migrated from Jitpack to Maven Central
    • Code cleanup
    Source code(tar.gz)
    Source code(zip)
Owner
Rajesh Hadiya
Software architect and developer Kotlin | Android | Spring
Rajesh Hadiya
Volley is an HTTP library that makes networking for Android apps easier and, most importantly, faster.

Volley Volley is an HTTP library that makes networking for Android apps easier and, most importantly, faster. For more information about Volley and ho

Google 3.3k Jan 1, 2023
:satellite: [Android Library] Simplified async networking in android

Android library that simplifies networking in android via an async http client. Also featured in [Awesome Android Newsletter #Issue 15 ] Built with ❀︎

Nishant Srivastava 36 May 14, 2022
πŸš€ A Complete Fast Android Networking Library that also supports HTTP/2 πŸš€

Fast Android Networking Library About Fast Android Networking Library Fast Android Networking Library is a powerful library for doing any type of netw

AMIT SHEKHAR 5.5k Dec 27, 2022
The easiest HTTP networking library for Kotlin/Android

Fuel The easiest HTTP networking library for Kotlin/Android. You are looking at the documentation for 2.x.y.. If you are looking for the documentation

Kittinun Vantasin 4.3k Jan 8, 2023
πŸš€ A Complete Fast Android Networking Library that also supports HTTP/2 πŸš€

Fast Android Networking Library About Fast Android Networking Library Fast Android Networking Library is a powerful library for doing any type of netw

AMIT SHEKHAR 5.5k Jan 3, 2023
Kotlin-echo-client - Echo client using Kotlin with Ktor networking library

Overview This repository contains an echo server implemented with Kotlin and kto

Elliot Barlas 2 Sep 1, 2022
Android Asynchronous Networking and Image Loading

Android Asynchronous Networking and Image Loading Download Maven Git Features Kotlin coroutine/suspend support Asynchronously download: Images into Im

Koushik Dutta 6.3k Dec 27, 2022
IceNet - Fast, Simple and Easy Networking for Android

IceNet FAST, SIMPLE, EASY This library is an Android networking wrapper consisting of a combination of Volley, OkHttp and Gson. For more information s

Anton Nurdin Tuhadiansyah 61 Jun 24, 2022
Latihan Networking dengan Retrofit

Latihan-Background-Process-dan-Networking-9 Latihan Networking dengan Retrofit Pada materi kali ini Anda akan belajar menggunakan Retrofit untuk menam

Ubean 0 Nov 25, 2021
NiceHttp - A small and simple OkHttp wrapper to ease scraping

NiceHttp - A small and simple OkHttp wrapper to ease scraping

LagradOst 19 Dec 17, 2022
Asynchronous socket, http(s) (client+server) and websocket library for android. Based on nio, not threads.

AndroidAsync AndroidAsync is a low level network protocol library. If you are looking for an easy to use, higher level, Android aware, http request li

Koushik Dutta 7.3k Jan 2, 2023
Android library listening network connection state and change of the WiFi signal strength with event bus

NetworkEvents Android library listening network connection state and change of the WiFi signal strength with event bus. It works with any implementati

Piotr Wittchen 452 Nov 21, 2022
SimpleApiCalls is a type-safe REST client for Android. The library provides the ability to interact with APIs and send network requests with HttpURLConnection.

SimpleApiCalls ?? SimpleApiCalls is a type-safe REST client for Android. The library provides the ability to interact with APIs and send network reque

null 4 Nov 28, 2022
Asynchronous Http and WebSocket Client library for Java

Async Http Client Follow @AsyncHttpClient on Twitter. The AsyncHttpClient (AHC) library allows Java applications to easily execute HTTP requests and a

AsyncHttpClient 6k Jan 8, 2023
HttpMocker is a simple HTTP mocking library written in Kotlin to quickly and easily handle offline modes in your apps

HttpMocker HttpMocker is a very lightweight Kotlin library that allows to mock HTTP calls relying on either OkHttp or the Ktor client libraries. It ca

David Blanc 174 Nov 28, 2022
An RPC library for Kotlin services that strives to balance developer productivity and performance.

IndieRpc An RPC library for Kotlin services that strives to balance developer productivity and performance. IndieRpc is inspired by Golang's net/rpc p

Asad Awadia 3 Nov 30, 2021
gRPC-Kotlin/JVM - An RPC library and framework

gRPC-Kotlin/JVM - An RPC library and framework A Kotlin/JVM implementation of gRPC: A high performance, open source, general RPC framework that puts m

null 2 Nov 25, 2021
Monitoring water tanker level using NodeMCU ESP8266 and HC-SR04P Ultrasonic Sensor and broadcasting it using a simple HTTP server inside NodeMCU ESP8266 and show data in an Android App

WaterLevel Preface This project aims to finding a tanker water level using NodeMCU with ESP8266 core and HC-SR04P Ultrasonic sensor and broadcasting i

YaMiN 12 Dec 20, 2022