GithubHelp home page GithubHelp logo

swr-compose's Introduction

SWR for Compose Android

Maven Central javadoc Test License

This is a clone library of React SWR ported for Jetpack Compose.
Currently works only on Android.

The API specification of React SWR is followed as much as possible.
Options are also supported for the most part.

What's "SWR"?

According to React SWR, "SWR" refers to

The name “SWR” is derived from stale-while-revalidate, a HTTP cache invalidation strategy popularized by HTTP RFC 5861. SWR is a strategy to first return the data from cache (stale), then send the fetch request (revalidate), and finally come with the up-to-date data.

Requirements

Min SDK 23+

Install

Add the following gradle dependency exchanging *.*.* for the latest release. Maven Central

implementation("com.kazakago.swr.compose:swr-android:*.*.*")

Getting Started

As with React SWR, implement the "fetcher function" and set it with the key to useSWR().
Using Kotlin's Destructuring declarations for return values can be written in the same way as in React SWR.

private val fetcher: suspend (key: String) -> String = {
    getNameApi.execute(key)
}

@Composable
fun Profile() {
    val (data, error) = useSWR("/api/user", fetcher)

    if (error != null) {
        Text("failed to load")
    } else if (data == null) {
        Text("loading...")
    } else {
        Text("hello $data!")
    }
}

Example

Refer to the example module for details. This module works as an Android app.

Supported Features

Feature name Status Note
Options See below
Global Configuration
Data Fetching
Error Handling
Auto Revalidation
Conditional Data Fetching
Arguments
Mutation ✅️
Pagination
Prefetching Data ✅️ Available by useSWRPreload()
Suspense
Middleware

Supported Options

The following options are supported for React SWR.
https://swr.vercel.app/docs/options

Option name Status Default value
suspense
fetcher(args)
revalidateIfStale true
revalidateOnMount true if fallbackData is not set
revalidateOnFocus true
revalidateOnReconnect true
refreshInterval 0.seconds (disable)
refreshWhenHidden false
refreshWhenOffline false
shouldRetryOnError true
dedupingInterval 2.seconds
focusThrottleInterval 5.seconds
loadingTimeout 3.seconds
errorRetryInterval 5.seconds
errorRetryCount
fallback
fallbackData
keepPreviousData false
onLoadingSlow(key, config)
onSuccess(data, key, config)
onError(err, key, config)
onErrorRetry(err, key, config, revalidate, revalidateOps) Exponential backoff algorithm
compare(a, b)
isPaused() false
use

Notice for performance

Deduplication and Deep Comparison are supported, but Dependency Collection is not supported due to Kotlin's language specification.
Therefore, the number of re-rendering (re-compose) may be higher than in the original React SWR.

However, it is possible to prevent performance degradation by limiting the number of arguments passed to child Composable functions (e.g., only data).

swr-compose's People

Contributors

kazakago avatar renovate[bot] avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar

swr-compose's Issues

Dependency Dashboard

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

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/deploy.yml
  • actions/checkout v4
  • actions/setup-java v3
.github/workflows/test.yml
  • actions/checkout v4
  • actions/setup-java v3
gradle
gradle.properties
settings.gradle.kts
build.gradle.kts
  • com.android.application 8.1.4
  • com.android.library 8.1.4
  • org.jetbrains.kotlin.android 1.9.20
  • org.jetbrains.dokka 1.9.10
  • io.github.gradle-nexus.publish-plugin 1.3.0
example/build.gradle.kts
  • composeOptions 1.5.4
  • androidx.activity:activity-compose 1.8.1
  • androidx.navigation:navigation-compose 2.7.5
  • androidx.compose:compose-bom 2023.10.01
  • junit:junit 4.13.2
  • androidx.test.ext:junit 1.1.5
  • androidx.test.espresso:espresso-core 3.5.1
  • androidx.compose:compose-bom 2023.10.01
swr/build.gradle.kts
  • composeOptions 1.5.4
  • androidx.lifecycle:lifecycle-runtime-ktx 2.6.2
  • androidx.compose:compose-bom 2023.10.01
  • junit:junit 4.13.2
  • io.kotest:kotest-assertions-core 5.8.0
  • org.robolectric:robolectric 4.11.1
  • org.jetbrains.kotlinx:kotlinx-coroutines-test 1.7.3
  • androidx.test.ext:junit 1.1.5
  • androidx.test.espresso:espresso-core 3.5.1
  • androidx.compose:compose-bom 2023.10.01
gradle-wrapper
gradle/wrapper/gradle-wrapper.properties
  • gradle 8.4

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

useSWRをたくさん呼び出すと、「android.net.ConnectivityManager$TooManyRequestsException 」によってクラッシュする(If call useSWR many times, crashing by android.net.ConnectivityManager$TooManyRequestsException)

Jp:

useSWRをたくさん呼び出すと、「android.net.ConnectivityManager$TooManyRequestsException 」によってクラッシュします。

これは、revalidateOnReconnectオプションがtrueだと、UseSWROptions.RevalidateOnReconnectの、LifecycleEventObserver内にあるregisterNetworkCallbackを多量に登録していることに起因していると考えられます。

networkCallbackを一度だけ登録するようにすれば改善すると考えられますがどうでしょうか?

En:

If call useSWR many times, crashing by android.net.ConnectivityManager$TooManyRequestsException

This problem caused by calling registerNetworkCallback too many times in UseSWROptions.RevalidateOnReconnect -> LifecycleEventObserver if revalidateOnReconnect = true.

Maybe, This problem will be fixed by chaning code to register networkCallback once only.

Please someone's opinion.

Code:

UseSWROptions.RevalidateOnReconnect

 val lifecycleObserver = LifecycleEventObserver { _, event ->
                when (event) {
                    Lifecycle.Event.ON_START -> {
                        shouldNotValidateAtNextOnAvailable = connectivityManager.getNetworkCapabilities(connectivityManager.activeNetwork)?.let {
                            it.hasCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET) && it.hasCapability(NetworkCapabilities.NET_CAPABILITY_VALIDATED)
                        } ?: false
                        connectivityManager.registerNetworkCallback(request, networkCallback) <-- Here!
                    }
                    Lifecycle.Event.ON_STOP -> {
                        connectivityManager.unregisterNetworkCallback(networkCallback)
                    }
                    else -> {}
                }
            }
StackTrace:
android.net.ConnectivityManager$TooManyRequestsException
at android.net.ConnectivityManager.convertServiceException(ConnectivityManager.java:4034)
at android.net.ConnectivityManager.sendRequestForNetwork(ConnectivityManager.java:4226)
at android.net.ConnectivityManager.sendRequestForNetwork(ConnectivityManager.java:4233)
at android.net.ConnectivityManager.registerNetworkCallback(ConnectivityManager.java:4615)
at android.net.ConnectivityManager.registerNetworkCallback(ConnectivityManager.java:4585)
at com.kazakago.swr.compose.internal.UseSWROptionsKt$RevalidateOnReconnect$1.invoke$lambda$1(UseSWROptions.kt:125)
at com.kazakago.swr.compose.internal.UseSWROptionsKt$RevalidateOnReconnect$1.$r8$lambda$dDbd31SUo6_iOlvK8STGeUnEKlA(Unknown Source:0)
at com.kazakago.swr.compose.internal.UseSWROptionsKt$RevalidateOnReconnect$1$$ExternalSyntheticLambda0.onStateChanged(Unknown Source:10)
at androidx.lifecycle.LifecycleRegistry$ObserverWithState.dispatchEvent(LifecycleRegistry.java:360)
at androidx.lifecycle.LifecycleRegistry.addObserver(LifecycleRegistry.java:202)
at com.kazakago.swr.compose.internal.UseSWROptionsKt$RevalidateOnReconnect$1.invoke(UseSWROptions.kt:133)
at com.kazakago.swr.compose.internal.UseSWROptionsKt$RevalidateOnReconnect$1.invoke(UseSWROptions.kt:103)
at androidx.compose.runtime.DisposableEffectImpl.onRemembered(Effects.kt:81)
at androidx.compose.runtime.CompositionImpl$RememberEventDispatcher.dispatchRememberObservers(Composition.kt:1091)
at androidx.compose.runtime.CompositionImpl.applyChangesInLocked(Composition.kt:818)
at androidx.compose.runtime.CompositionImpl.applyChanges(Composition.kt:839)
at androidx.compose.runtime.Recomposer$runRecomposeAndApplyChanges$2$2.invoke(Recomposer.kt:585)
at androidx.compose.runtime.Recomposer$runRecomposeAndApplyChanges$2$2.invoke(Recomposer.kt:503)
at androidx.compose.ui.platform.AndroidUiFrameClock$withFrameNanos$2$callback$1.doFrame(AndroidUiFrameClock.android.kt:34)
at androidx.compose.ui.platform.AndroidUiDispatcher.performFrameDispatch(AndroidUiDispatcher.android.kt:109)
at androidx.compose.ui.platform.AndroidUiDispatcher.access$performFrameDispatch(AndroidUiDispatcher.android.kt:41)
at androidx.compose.ui.platform.AndroidUiDispatcher$dispatchCallback$1.doFrame(AndroidUiDispatcher.android.kt:69)
at android.view.Choreographer$CallbackRecord.run(Choreographer.java:1229)
at android.view.Choreographer$CallbackRecord.run(Choreographer.java:1239)
at android.view.Choreographer.doCallbacks(Choreographer.java:899)
at android.view.Choreographer.doFrame(Choreographer.java:827)
at android.view.Choreographer$FrameDisplayEventReceiver.run(Choreographer.java:1214)
at android.os.Handler.handleCallback(Handler.java:942)
at android.os.Handler.dispatchMessage(Handler.java:99)
at android.os.Looper.loopOnce(Looper.java:201)
at android.os.Looper.loop(Looper.java:288)
at android.app.ActivityThread.main(ActivityThread.java:7872)
at java.lang.reflect.Method.invoke(Native Method)
at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:548)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:936)
Suppressed: kotlinx.coroutines.DiagnosticCoroutineContextException: [androidx.compose.runtime.PausableMonotonicFrameClock@ae3b631, androidx.compose.ui.platform.MotionDurationScaleImpl@f5db116, StandaloneCoroutine{Cancelling}@3dd4a97, AndroidUiDispatcher@9bcf484]

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.