Coder Social home page Coder Social logo

kotlin-remotedata's Introduction

CI Maven Central

RemoteData

Data type that represents the possible states when fetching data from a remote source. Kotlin Native port of https://github.com/krisajenkins/remotedata

This is a sealed class that provides 4 possible states when loading data. Similar to an Enum, but each case can have associated values. So you can always correctly model the state of the request operation at any given point.

  1. NotAsked - No operation in progress
  2. Loading - Operation in progress
  3. Success - Operation finished successfully, with the data
  4. Failure - Operation finished but failed, with the error
Failure Case Success Case
Failure Success

State when no operation was requested:

 var operation: RemoteData<MyError, MyData> = RemoteData.NotAsked

State while loading:

 var operation: RemoteData<MyError, MyData> = RemoteData.Loading

State after loading successfully:

var operation: RemoteData<MyError, MyData> = RemoteData.Success(data)

State after failure loading:

 var operation: RemoteData<MyError, MyData> = RemoteData.Failure(error)

Being a sealed class means that the compiler can help us think of all valid state possibilities, providing extra data when necessary.

fun render(operation: RemoteData<MyError, MyData>) {
  when (operation) {
    is RemoteData.NotAsked -> // show initial screen
    is RemoteData.Loading -> // show loading screen 
    is RemoteData.Success -> // show data using operation.data
    is RemoteData.Failure -> // show error screen using operation.failure
  }
}

There are helper functions to make working with all possibilities easier.

// Get the value if finished loading, or use a default value. 
val item = operation.getOrElse(defaultItem)

// Map the value if it's a successful operation
operation.map { item -> /** update data */ }

// FlatMap with another RemoteData 
operation.flatMap { item -> /** bad data, return RemoteData.Failure */ } 

// Merge operations
operation.mergeWith(otherOperation) { item1, item2 -> /** combine results */ }

It's also useful in RxJava Observables to avoid keeping known possible error states out of onError, leaving that terminal case for exceptional unexpected errors.

fun loadData(): Observable<RemoteData<MyError, Data>> {
  return fetchData()
    .map { response -> 
      if (response.code == 200) {
        RemoteData.succeed(response.data)
      else {
        RemoteData.fail(response.error)
      }
    }
    .startWith(RemoteData.Loading)
}

Why

We often need to fetch data, and doing so means that we often setup state for this operation. We might want to do the following:

Show a loading indicator until the data is fetched, and then show the data.

Sounds simple enough. A naive approach is the following:

State while loading:

 var isLoading: Boolean = true
 var item: Data? = null

State after loading:

 var isLoading: Boolean = false
 var item: Data? = loadedData

But we forgot about handling errors. So we add error state to render an error screen if there is an error.

State while loading:

var isLoading: Boolean = true
var item: Data? = null
var error: Throwable? = null

State after loading successfully:

var isLoading: Boolean = false
var item: Data? = loadedData
var error: Throwable? = null

State after failure loading:

var isLoading: Boolean = false
var item: Data? = null
var error: Throwable? = error

There are issues with this approach.

  1. We have to remember to reset the loading state if we successfully fetched data and if we failed to load data. It's easy to forget about reseting the loading flag if there was an error.
  2. The state for data and the error are both now nullable, since they might not be available at all times of the operation.
  3. Chaining or merging operations can be difficult. For example, errors aren't propagated down the chain.
  4. We might have to duplicate this error prone state for other operations.
  5. This state allows for 16 different combinations, and many of those wouldn't make sense. For example, if the state says it's loading but it also has an error or data.

If you have a list of items to load, one could use an empty list instead of null. However, there is still the issue of easily mixing up if the data is truly empty or if it is just empty from loading or errors.

Documentation

Dokka

Download

Core

Gradle:

compile 'com.github.torresmi.remotedata:remotedata:2.0.0'

Maven:

<dependency>
  <groupId>com.github.torresmi.remotedata</groupId>
  <artifactId>remotedata</artifactId>
  <version>2.0.0</version>
  <type>pom</type>
</dependency>

### Coroutine Support
### Core
Gradle: 
```groovy
compile 'com.github.torresmi.remotedata:remotedata-coroutines:2.0.0'

Maven:

<dependency>
  <groupId>com.github.torresmi.remotedata</groupId>
  <artifactId>remotedata-coroutines</artifactId>
  <version>2.0.0</version>
  <type>pom</type>
</dependency>

kotlin-remotedata's People

Contributors

renovate[bot] avatar torresmi avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar

kotlin-remotedata's Issues

Pull Request Template

We can create a pull request template.

  • References open issue based on branch name
  • Description section

Publish Kdocs

Publish kdocs here and provide a link in the readme

Add CI

We can use Github Actions to run tests, linters, static analysis, etc.

Update package name

Update package naming convention to use dev.miketorres.remotedata instead of basing it off of the github url.

Coroutine Support

Explore supporting suspended functions for transformations.

flatMap is an obvious case where it can leverage coroutines to return a new RemoteData. This probably applies to other transformations as well.

Allow for nullable types

Instead of using sealed classes like Option or Maybe to represent optionality, we should be able to rely on Kotlin's nullable types. It's more idiomatic and provides the flexibility to move from nullable to non-nullable without a breaking change.

View helpers

A pattern I've been doing a lot is making helpers to easily render RemoteData values. At the core it's an interface that invoke rendering methods based on the current state. This is often wrapped in a ViewAnimator that can easily switch between initial, loading, success, and error views.

Making this an optional module can be helpful.

Add CD

Add continuous delivery for releases via github actions

Remove extension constructor helpers

Remove Any.success() and Any.failure()

These aren't necessary over the companion object alternatives and pollute the global namespace by default. They are very easy to recreate if callers want them.

Remove Ubuntu machine

Mac builds can build everything that the linux one can. We can remove the linux one for publishing and PR verification

Kotest

We can switch to Kotest and leverage property based testing

Add Kover

Jacoco doesn't work with inline functions. Klover is probably the better tool for this project.

Dependency Dashboard

This issue lists Renovate updates and detected dependencies. Read the Dependency Dashboard docs to learn more.

Rate-Limited

These updates are currently rate-limited. Click on a checkbox below to force their creation now.

  • Update actions/checkout action to v4
  • Update dependency org.jetbrains.kotlin:kotlin-reflect to v2
  • Update eskatos/gradle-command-action action to v3
  • Update kotlin to v2 (major) (org.jetbrains.kotlin.js, org.jetbrains.kotlin.multiplatform)
  • ๐Ÿ” Create all rate-limited PRs at once ๐Ÿ”

Open

These updates have all been created already. Click a checkbox below to force a retry/rebase of any.

Detected dependencies

github-actions
.github/workflows/ci.yml
  • actions/checkout v3
  • actions/cache v3.2.2
  • eskatos/gradle-command-action v2
.github/workflows/pr-formatter.yml
  • tzkhan/pr-update-action v1.1.1
.github/workflows/pr-size-labeler.yml
  • CodelyTV/pr-size-labeler v1
.github/workflows/publish.yml
  • actions/checkout v3
  • eskatos/gradle-command-action v2
gradle
gradle.properties
settings.gradle.kts
build.gradle.kts
gradle/libs.versions.toml
  • org.jetbrains.kotlinx:kotlinx-coroutines-core 1.6.4
  • org.jetbrains.kotlinx:kotlinx-coroutines-test 1.6.4
  • io.kotest:kotest-assertions-core 5.5.4
  • io.kotest:kotest-property 5.5.4
  • io.kotest:kotest-runner-junit5 5.5.4
  • org.jetbrains.kotlin:kotlin-reflect 1.7.22
  • org.jetbrains.dokka 1.7.20
  • org.jetbrains.kotlin.multiplatform 1.7.22
  • org.jetbrains.kotlin.js 1.7.22
  • com.vanniktech.maven.publish 0.13.0
remotedata/gradle.properties
remotedata/build.gradle.kts
remotedata-coroutines/gradle.properties
remotedata-coroutines/build.gradle.kts
scripts/jacoco.gradle
  • jacoco 0.8.8
test-util/build.gradle.kts
gradle-wrapper
gradle/wrapper/gradle-wrapper.properties
  • gradle 7.6

  • Check this box to trigger a request for Renovate to run again on this repository

Update Coroutine Targets

Target as many platforms as we can with coroutine support. Also add a commonTest for verification.

Remove Spek

Spek does not support Kotlin Native yet and it doesn't look like that will be ready soon.

Migrate to buildSrc

Instead of definitions in the top level build.gradle file, we can use buildSrc for this

Improve Docs

Cleanup and simplify the README. Move details into a hosted doc site. This can show dokka docs and have usage recipes.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.