-
Notifications
You must be signed in to change notification settings - Fork 3
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #57 from RedMadRobot/feature/AND-147-add-eventqueue
AND-147: add EventQueue and rename to ViewModelEvents
- Loading branch information
Showing
18 changed files
with
475 additions
and
84 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
52 changes: 0 additions & 52 deletions
52
ktx/lifecycle-livedata-ktx/src/test/kotlin/EventQueueTest.kt
This file was deleted.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,7 @@ | ||
## Unreleased | ||
|
||
*No changes* | ||
|
||
## 1.0.0 - 2024.05.14 | ||
|
||
- Public release viewModelEvents libraries |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,194 @@ | ||
# ViewModelEvents <GitHub path="RedMadRobot/gears-android/tree/main/ktx/viewmodel-events-ktx"/> | ||
[][mavenCentral] | ||
[][license] | ||
|
||
The entity to handle one-time viewModel events. | ||
|
||
--- | ||
<!-- START doctoc generated TOC please keep comment here to allow auto update --> | ||
<!-- DON'T EDIT THIS SECTION, INSTEAD RE-RUN doctoc TO UPDATE --> | ||
|
||
- [Installation](#installation) | ||
- [Usage](#usage) | ||
- [Flow implementation](#flow-implementation) | ||
- [LiveData implementation](#livedata-implementation) | ||
- [Best Practices](#best-practices) | ||
- [`EventsDispatcher` interface](#eventsdispatcher-interface) | ||
- [Shortcuts to send common events](#shortcuts-to-send-common-events) | ||
- [Contributing](#contributing) | ||
|
||
<!-- END doctoc generated TOC please keep comment here to allow auto update --> | ||
|
||
## Installation | ||
|
||
Add the dependency: | ||
|
||
```groovy | ||
repositories { | ||
mavenCentral() | ||
google() | ||
} | ||
dependencies { | ||
implementation("com.redmadrobot.viewmodelevents:viewmodelevents-flow:<version>") | ||
// or | ||
implementation("com.redmadrobot.viewmodelevents:viewmodelevents-livedata:<version>") | ||
// Compose extensions | ||
implementation("com.redmadrobot.viewmodelevents:viewmodelevents-compose:<version>") | ||
} | ||
``` | ||
|
||
## Usage | ||
|
||
One-time events (or single events) are a common pattern to display messages or errors in UI. | ||
`ViewModelEvents` addresses the challenge of buffering and consuming one-time events: | ||
|
||
- **Buffering:** When there are no subscribers to `ViewModelEvents`, emitted events are stored in a buffer. | ||
All buffered events are then delivered sequentially as soon as you subscribe to the ViewModelEvents | ||
- **Consumption:** Each event is emitted only once. | ||
Thus, if you re-subscribe to the ViewModelEvents, you will not receive any events that have already been consumed. | ||
|
||
There are two implementations: via flow (recommended for use) and via livedata (deprecated). | ||
|
||
### Flow implementation | ||
|
||
This implementation utilizes `StateFlow` under the hood and provides the `flow` field to observe events. | ||
|
||
```kotlin | ||
data class MessageEvent(val message: String) : Event | ||
|
||
val viewModelEvents = ViewModelEvents() | ||
|
||
viewModelEvents.offerEvent(MessageEvent("A")) | ||
viewModelEvents.offerEvent(MessageEvent("B")) | ||
|
||
// Observe | ||
events.flow | ||
.onEach { onEvent(it) } | ||
.launchIn(scope) | ||
|
||
viewModelEvents.offerEvent(MessageEvent("C")) | ||
``` | ||
|
||
```kotlin | ||
MessageEvent(message="A") | ||
MessageEvent(message="B") | ||
MessageEvent(message="C") | ||
``` | ||
|
||
`ViewModelEvents` can also be used with Jetpack Compose: | ||
|
||
```kotlin | ||
// Remember to add viewmodelevents-compose dependency | ||
@Composable | ||
public fun Screen() { | ||
// We assume, ViewModel implementation has `events` field providing ViewModelEvents instance | ||
val viewModel = viewModel<FeatureViewModel>() | ||
|
||
ViewModelEventsEffect(viewModel.events) { event: Event -> | ||
when(event) { | ||
is MessageEvent -> println("Message: $event") | ||
is ErrorMessageEvent -> println("Error: $event") | ||
} | ||
} | ||
} | ||
``` | ||
|
||
That subscription emits values from `ViewModelEvents` when the lifecycle is at least at `minActiveState` (`Lifecycle.State.STARTED` by default). | ||
Emission stops when the lifecycle state falls below the `minActiveState` state. | ||
|
||
### LiveData implementation | ||
|
||
This implementation utilizes `LiveData` under the hood and provides methods to observe events on the given lifecycle. | ||
|
||
```kotlin | ||
data class MessageEvent(val message: String) : Event | ||
|
||
val viewModelEvents = ViewModelEvents() | ||
|
||
viewModelEvents.offerEvent(MessageEvent("A")) | ||
viewModelEvents.offerEvent(MessageEvent("B")) | ||
viewModelEvents.observeForever { println(it) } | ||
viewModelEvents.offerEvent(MessageEvent("C")) | ||
``` | ||
|
||
```bash | ||
MessageEvent(message=A) | ||
MessageEvent(message=B) | ||
MessageEvent(message=C) | ||
``` | ||
|
||
| Extension | Description | | ||
|-----------------------------------------------------------------------------|-------------| | ||
| `Fragment.observe(liveData: ViewModelEvents, onEvent: (Event) -> Unit)` | Shorter way to observe `LiveData` in a fragment | | ||
| `ComponentActivity.observe(liveData: ViewModelEvents, onEvent: (Event) -> Unit)` | Shorter way to observe `LiveData` in an activity | | ||
|
||
### Best Practices | ||
|
||
Here you can find the patterns that we found useful to simplify usage of `ViewModelEvents`. | ||
|
||
#### `EventsDispatcher` interface | ||
|
||
To simplify events sending, it is useful to declare the following interface: | ||
|
||
```kotlin | ||
/** Interface for ViewModel events dispatching. */ | ||
public interface EventsDispatcher { | ||
public val events: ViewModelEvents | ||
|
||
/** Offers the given [event] to be added to the ViewModel events. */ | ||
public fun offerEvent(event: Event) { | ||
events.offerEvent(event) | ||
} | ||
} | ||
``` | ||
|
||
That's it! | ||
You can add this interface to any class you want to be able to send events. | ||
For example, to your base `ViewModel`: | ||
|
||
```kotlin | ||
abstract class BaseViewModel : ViewModel(), EventsDispatcher { | ||
override val events = ViewModelEvents() | ||
} | ||
``` | ||
|
||
Thus, all `ViewModel`s will be able to use `offerEvent(Event)` to send events: | ||
|
||
```kotlin | ||
class FeatureViewModel : BaseViewModel() { | ||
fun onError(message: String) = offerEvent(ErrorMessageEvent(message)) | ||
} | ||
``` | ||
|
||
#### Shortcuts to send common events | ||
|
||
It is useful to create extension-functions to send common events. | ||
Let's imagine you have the following events: | ||
|
||
```kotlin | ||
data class MessageEvent(val message: String) : Event | ||
data class ErrorMessageEvent(val message: String) : Event | ||
``` | ||
|
||
To simplify sending of these events, you can create shortcuts. | ||
It works best with the [`EventsDispatcher` interface](#eventsdispatcher-interface). | ||
|
||
```kotlin | ||
fun EventsDispatcher.showMessage(message: String) { | ||
offerEvent(MessageEvent(message)) | ||
} | ||
|
||
fun EventsDispatcher.showError(message: String) { | ||
offerEvent(ErrorMessageEvent(message)) | ||
} | ||
``` | ||
|
||
## Contributing | ||
|
||
Merge requests are welcome. | ||
For major changes, please open an issue first to discuss what you would like to change. | ||
|
||
[mavenCentral]: https://search.maven.org/artifact/com.redmadrobot.eventqueue/eventqueue-livedata | ||
[license]: ../LICENSE |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,3 @@ | ||
// For some reason gradle.properties in this project doesn't affect its subprojects | ||
val viewModelEventsGroup = group | ||
subprojects { group = viewModelEventsGroup } |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
group=com.redmadrobot.viewmodelevents |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,9 @@ | ||
plugins { | ||
convention.library.kotlin | ||
} | ||
|
||
description = "ViewModelEvents common" | ||
|
||
dependencies { | ||
api(kotlin("stdlib")) | ||
} |
4 changes: 4 additions & 0 deletions
4
...delevents/viewmodelevents-common/src/main/kotlin/com/redmadrobot/viewmodelevents/Event.kt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,4 @@ | ||
package com.redmadrobot.viewmodelevents | ||
|
||
/** Marker interface for entities that can be put to the [ViewModelEvents]. */ | ||
public interface Event |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,25 @@ | ||
plugins { | ||
convention.library.android | ||
} | ||
|
||
description = "ViewModelEvents extensions for compose" | ||
|
||
android { | ||
namespace = "$group.compose" | ||
|
||
buildFeatures { | ||
compose = true | ||
} | ||
|
||
composeOptions { | ||
kotlinCompilerExtensionVersion = androidx.versions.compose.compiler.get() | ||
} | ||
} | ||
|
||
dependencies { | ||
api(kotlin("stdlib")) | ||
api(androidx.lifecycle.runtime) | ||
api(androidx.compose.ui) | ||
api(androidx.compose.runtime) | ||
api(project(":viewmodelevents:viewmodelevents-flow")) | ||
} |
Oops, something went wrong.