Ecosyste.ms: Awesome
An open API service indexing awesome lists of open source software.
https://github.com/sczerwinski/android-lifecycle
Extensions for Jetpack Lifecycle components
https://github.com/sczerwinski/android-lifecycle
android jetpack jetpack-android jetpack-lifecycle-components lifecycle
Last synced: about 2 months ago
JSON representation
Extensions for Jetpack Lifecycle components
- Host: GitHub
- URL: https://github.com/sczerwinski/android-lifecycle
- Owner: sczerwinski
- License: apache-2.0
- Created: 2020-12-11T17:54:33.000Z (about 4 years ago)
- Default Branch: main
- Last Pushed: 2023-07-24T07:51:26.000Z (over 1 year ago)
- Last Synced: 2024-05-02T03:15:47.456Z (8 months ago)
- Topics: android, jetpack, jetpack-android, jetpack-lifecycle-components, lifecycle
- Language: Kotlin
- Homepage: https://czerwinski.it/projects/android-lifecycle/
- Size: 328 KB
- Stars: 4
- Watchers: 3
- Forks: 1
- Open Issues: 6
-
Metadata Files:
- Readme: README.md
- Changelog: CHANGELOG.md
- Contributing: .github/CONTRIBUTING.md
- License: LICENSE
Awesome Lists containing this project
README
[![Build](https://github.com/sczerwinski/android-lifecycle/workflows/Build/badge.svg)][ci-build]
# Extensions for Jetpack Lifecycle
## LiveData Extensions
[![Maven Central](https://img.shields.io/maven-central/v/it.czerwinski.android.lifecycle/lifecycle-livedata)][lifecycle-livedata-release]
[![Sonatype Nexus (Snapshots)](https://img.shields.io/nexus/s/it.czerwinski.android.lifecycle/lifecycle-livedata?server=https%3A%2F%2Foss.sonatype.org)][lifecycle-livedata-snapshot]Kotlin
```kotlin
dependencies {
implementation("it.czerwinski.android.lifecycle:lifecycle-livedata:[VERSION]")
}
```Groovy
```groovy
dependencies {
implementation 'it.czerwinski.android.lifecycle:lifecycle-livedata:[VERSION]'
}
```### Types
#### `ConstantLiveData`
[LiveData] that always emits a single constant value.
#### `GroupedLiveData`
[MediatorLiveData] subclass which provides a separate [LiveData] per each result returned by `keySelector` function
executed on subsequent values emitted by the source LiveData.### LiveData Factory Methods
#### `intervalLiveData`
Returns a [LiveData] emitting a sequence of integer values, spaced by a given `timeInMillis`.
```kotlin
val fixedIntervalLiveData: LiveData = intervalLiveData(timeInMillis = 1000L)
val varyingIntervalLiveData: LiveData = intervalLiveData { index -> (index + 1) * 1000L }
```### LiveData Transformations
#### `mapNotNull`
Returns a [LiveData] emitting only the non-null results of applying the given `transform` function to each value
emitted by this LiveData.```kotlin
val userOptionLiveData: LiveData> = // ...
val userLiveData: LiveData = userOptionLiveData.mapNotNull { user -> user.getOrNull() }
```#### `filter`
Returns a [LiveData] emitting only values from this LiveData matching the given `predicate`.
```kotlin
val resultLiveData: LiveData> = // ...
val successLiveData: LiveData> = resultLiveData.filter { it.isSuccess }
```#### `filterNotNull`
Returns a [LiveData] emitting only non-null values from this LiveData.
```kotlin
val userLiveData: LiveData = // ...
val nonNullUserLiveData: LiveData = userLiveData.filterNotNull()
```#### `filterIsInstance`
Returns a [LiveData] emitting only values of the given type from this LiveData.
```kotlin
val resultLiveData: LiveData> = // ...
val failureLiveData: LiveData = resultLiveData.filterIsInstance()
```#### `reduce`
Returns a [LiveData] emitting accumulated value starting with the first value emitted by this LiveData and applying
`operation` from left to right to current accumulator value and each value emitted by this.```kotlin
val newOperationsCountLiveData: LiveData = // ...
val operationsCountLiveData: LiveData =
newOperationsCountLiveData.reduce { acc, next -> if (next == null) null else acc + next }
```#### `reduceNotNull`
Returns a [LiveData] emitting non-null accumulated value starting with the first non-null value emitted by this
LiveData and applying `operation` from left to right to current accumulator value and each subsequent non-null value
emitted by this LiveData.```kotlin
val newOperationsCountLiveData: LiveData = // ...
val operationsCountLiveData: LiveData =
newOperationsCountLiveData.reduceNotNull { acc, next -> acc + next }
```#### `throttleWithTimeout`
Returns a [LiveData] emitting values from this LiveData, after dropping values followed by newer values before
`timeInMillis` expires.```kotlin
val isLoadingLiveData: LiveData = // ...
val isLoadingThrottledLiveData: LiveData = isLoadingLiveData.throttleWithTimeout(
timeInMillis = 1000L,
context = viewModelScope.coroutineContext
)
```#### `delayStart`
Returns a [LiveData] emitting values from this LiveData, after dropping values followed by newer values before
`timeInMillis` expires since the result LiveData has been created.```kotlin
val resultLiveData: LiveData = // ...
val delayedResultLiveData: LiveData = resultLiveData.delayStart(
timeInMillis = 1000L,
context = viewModelScope.coroutineContext
)
```#### `merge`
Returns a [LiveData] emitting each value emitted by any of the given LiveData.
```kotlin
val serverError: LiveData = // ...
val databaseError: LiveData = // ...
val error: LiveData = serverError merge databaseError
``````kotlin
val serverError: LiveData = // ...
val databaseError: LiveData = // ...
val fileError: LiveData = // ...
val error: LiveData = merge(serverError, databaseError, fileError)
```#### `combineLatest`
Returns a [LiveData] emitting pairs, triples or lists of latest values emitted by the given LiveData.
```kotlin
val userLiveData: LiveData = // ...
val avatarUrlLiveData: LiveData = // ...
val userWithAvatar: LiveData> = combineLatest(userLiveData, avatarUrlLiveData)
``````kotlin
val userLiveData: LiveData = // ...
val avatarUrlLiveData: LiveData = // ...
val userWithAvatar: LiveData =
combineLatest(userLiveData, avatarUrlLiveData) { user, avatarUrl ->
UserWithAvatar(user, avatarUrl)
}
```#### `switch`
Converts [LiveData] that emits other LiveData into a single LiveData that emits the items emitted by the most
recently emitted LiveData.```kotlin
val sourcesLiveData: LiveData> = // ...
val resultLiveData: LiveData = sourcesLiveData.switch()
```#### `groupBy`
Returns a `GroupedLiveData` providing a set of [LiveData], each emitting a different subset of values from this
LiveData, based on the result of the given `keySelector` function.```kotlin
val userLiveData: LiveData = // ...
val userByStatusLiveData: GroupedLiveData = errorLiveData.groupBy { user -> user.status }
val activeUserLiveData: LiveData = userByStatusLiveData[UserStatus.ACTIVE]
```#### `defaultIfEmpty`
Returns a [LiveData] that emits the values emitted by this LiveData or a specified default value if this LiveData has
not yet emitted any values at the time of observing.```kotlin
val errorLiveData: LiveData = // ...
val statusLiveData: LiveData = errorLiveData.defaultIfEmpty("No errors")
```### MediatorLiveData Extensions
#### `addDirectSource`
Starts to listen the given source LiveData.
Whenever source value is changed, it is set as a new value of this [MediatorLiveData].```kotlin
mediatorLiveData.addDirectSource(liveData)
```
is equivalent to:
```kotlin
mediatorLiveData.addSource(liveData) { x -> mediatorLiveData.value = x }
```## Common LivaData Testing Utilities
[![Maven Central](https://img.shields.io/maven-central/v/it.czerwinski.android.lifecycle/lifecycle-livedata-test-common)][lifecycle-livedata-test-common-release]
[![Sonatype Nexus (Snapshots)](https://img.shields.io/nexus/s/it.czerwinski.android.lifecycle/lifecycle-livedata-test-common?server=https%3A%2F%2Foss.sonatype.org)][lifecycle-livedata-test-common-snapshot]This package is included in both `lifecycle-livedata-test-junit4` and `lifecycle-livedata-test-junit5`.
Kotlin
```kotlin
dependencies {
testImplementation("junit:junit:4.13.1")
testImplementation("it.czerwinski.android.lifecycle:lifecycle-livedata-test-common:[VERSION]")
}
```Groovy
```groovy
dependencies {
testImplementation 'junit:junit:4.13.1'
testImplementation 'it.czerwinski.android.lifecycle:lifecycle-livedata-test-common:[VERSION]'
}
```### Testing Observed Values
#### `TestObserver`
A callback testing values emitted by [LiveData].
```kotlin
class MyTestClass {@Test
fun testMethod1() {
val liveData = MutableLiveData()val observer = liveData.test()
observer.assertNoValues()
}@Test
fun testMethod2() {
val liveData = MutableLiveData()val observer = liveData.test()
liveData.postValue(1)
liveData.postValue(2)
liveData.postValue(3)observer.assertValues(1, 2, 3)
}
}
```## LivaData Testing Utilities For JUnit4
[![Maven Central](https://img.shields.io/maven-central/v/it.czerwinski.android.lifecycle/lifecycle-livedata-test-junit4)][lifecycle-livedata-test-junit4-release]
[![Sonatype Nexus (Snapshots)](https://img.shields.io/nexus/s/it.czerwinski.android.lifecycle/lifecycle-livedata-test-junit4?server=https%3A%2F%2Foss.sonatype.org)][lifecycle-livedata-test-junit4-snapshot]Kotlin
```kotlin
dependencies {
testImplementation("junit:junit:4.13.1")
testImplementation("it.czerwinski.android.lifecycle:lifecycle-livedata-test-junit4:[VERSION]")
}
```Groovy
```groovy
dependencies {
testImplementation 'junit:junit:4.13.1'
testImplementation 'it.czerwinski.android.lifecycle:lifecycle-livedata-test-junit4:[VERSION]'
}
```### JUnit4 Rules
#### `TestCoroutineDispatcherRule`
JUnit4 test rule that swaps main coroutine dispatcher with [UnconfinedTestDispatcher].
```kotlin
class MyTestClass {@Rule
@JvmField
val testCoroutineDispatcherRule = TestCoroutineDispatcherRule()val testCoroutineScheduler get() = testCoroutineDispatcherRule.scheduler
// ...
}
```## LivaData Testing Utilities For JUnit5
[![Maven Central](https://img.shields.io/maven-central/v/it.czerwinski.android.lifecycle/lifecycle-livedata-test-junit5)][lifecycle-livedata-test-junit5-release]
[![Sonatype Nexus (Snapshots)](https://img.shields.io/nexus/s/it.czerwinski.android.lifecycle/lifecycle-livedata-test-junit5?server=https%3A%2F%2Foss.sonatype.org)][lifecycle-livedata-test-junit5-snapshot]Kotlin
```kotlin
dependencies {
testImplementation("org.junit.jupiter:junit-jupiter-api:5.7.0")
testRuntimeOnly("org.junit.jupiter:junit-jupiter-engine:5.7.0")
testImplementation("it.czerwinski.android.lifecycle:lifecycle-livedata-test-junit5:[VERSION]")
}
```Groovy
```groovy
dependencies {
testImplementation 'org.junit.jupiter:junit-jupiter-api:5.7.0'
testRuntimeOnly 'org.junit.jupiter:junit-jupiter-engine:5.7.0'
testImplementation 'it.czerwinski.android.lifecycle:lifecycle-livedata-test-junit5:[VERSION]'
}
```### JUnit5 Extensions
#### `InstantTaskExecutorExtension`
JUnit5 extension that swaps the background executor used by the Architecture Components with a different one which
executes each task synchronously.This extension is analogous to [InstantTaskExecutorRule] for JUnit4.
```kotlin
@ExtendWith(InstantTaskExecutorExtension::class)
class MyTestClass {
// ...
}
```#### `TestCoroutineDispatcherExtension`
JUnit5 extension that swaps main coroutine dispatcher with [UnconfinedTestDispatcher].
Any test method parameter of type [TestCoroutineScheduler] will be resolved as the scheduler of the [TestCoroutineDispatcher]:
```kotlin
@ExtendWith(TestCoroutineDispatcherExtension::class)
class MyTestClass {@Test
fun someTest(scheduler: TestCoroutineScheduler) {
// ...
scheduler.advanceTimeBy(delayTimeMillis = 1000L)
scheduler.runCurrent()
// ...
}
}
```In case of parameterized tests, the scheduler can be passed as a parameter of a before method:
```kotlin
@ExtendWith(TestCoroutineDispatcherExtension::class)
class MyTestClass {private lateinit var testCoroutineScheduler: TestCoroutineScheduler
@BeforeEach
fun setScheduler(scheduler: TestCoroutineScheduler) {
testCoroutineScheduler = scheduler
}@ParameterizedTest
@MethodSource("testData")
fun someParameterizedTest(input: Int) {
// ...
}
}
```[ci-build]: https://github.com/sczerwinski/android-lifecycle/actions?query=workflow%3ABuild
[lifecycle-livedata-release]: https://repo1.maven.org/maven2/it/czerwinski/android/lifecycle/lifecycle-livedata/
[lifecycle-livedata-test-common-release]: https://repo1.maven.org/maven2/it/czerwinski/android/lifecycle/lifecycle-livedata-test-common/
[lifecycle-livedata-test-junit4-release]: https://repo1.maven.org/maven2/it/czerwinski/android/lifecycle/lifecycle-livedata-test-junit4/
[lifecycle-livedata-test-junit5-release]: https://repo1.maven.org/maven2/it/czerwinski/android/lifecycle/lifecycle-livedata-test-junit5/
[lifecycle-livedata-snapshot]: https://oss.sonatype.org/content/repositories/snapshots/it/czerwinski/android/lifecycle/lifecycle-livedata/
[lifecycle-livedata-test-common-snapshot]: https://oss.sonatype.org/content/repositories/snapshots/it/czerwinski/android/lifecycle/lifecycle-livedata-test-common/
[lifecycle-livedata-test-junit4-snapshot]: https://oss.sonatype.org/content/repositories/snapshots/it/czerwinski/android/lifecycle/lifecycle-livedata-test-junit4/
[lifecycle-livedata-test-junit5-snapshot]: https://oss.sonatype.org/content/repositories/snapshots/it/czerwinski/android/lifecycle/lifecycle-livedata-test-junit5/[LiveData]: https://developer.android.com/reference/androidx/lifecycle/LiveData
[MediatorLiveData]: https://developer.android.com/reference/androidx/lifecycle/MediatorLiveData
[InstantTaskExecutorRule]: https://developer.android.com/reference/androidx/arch/core/executor/testing/InstantTaskExecutorRule
[TestCoroutineDispatcher]: https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-test/kotlinx.coroutines.test/-test-coroutine-dispatcher/
[UnconfinedTestDispatcher]: https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-test/kotlinx.coroutines.test/-unconfined-test-dispatcher.html
[TestCoroutineScheduler]: https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-test/kotlinx.coroutines.test/-test-coroutine-scheduler/index.html