Skip to content

Commit 8460d11

Browse files
author
Adrián García
authored
Update README.md
1 parent 83240e5 commit 8460d11

File tree

1 file changed

+115
-44
lines changed

1 file changed

+115
-44
lines changed

README.md

Lines changed: 115 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -1,42 +1,73 @@
11
# Mini
2+
[![Release](https://jitpack.io/v/bq/mini-kotlin.svg)](https://jitpack.io/#bq/mini-kotlin)
3+
24
Mini is a minimal Flux architecture written in Kotlin that also adds a mix of useful features to build UIs fast.
35

46
### Purpose
57
You should use this library if you aim to develop a reactive application with good performance (no reflection using code-gen).
68
Feature development using Mini is fast compared to traditional architectures (like CLEAN or MVP), low boilerplate and state based models make feature integration and bugfixing easy as well as removing several families of problems like concurrency or view consistency across screens.
79

8-
## How to Use
9-
### Actions
10+
### How to Use
11+
## Dispatcher
12+
The *Dispatcher* is the hub that manages all data flow in a Flux application. It is basically a holder of store callbacks: each store registers itself and provides a callback for an action.
1013

11-
🚧WIP🚧
14+
One important thing is that the dispatching is always performed in the same thread to avoid possible side-effects.
1215

13-
### Dispatcher
14-
15-
🚧WIP🚧
16+
We can dispatch actions in the following ways:
1617

1718
```kotlin
18-
//Dispatch an action on the main thread synchronously
19+
// Dispatch an action on the main thread synchronously
1920
dispatcher.dispatch(LoginAction(username = "user", password = "123"))
2021

21-
//Post an event that will dispatch the action on the UI thread and return immediately.
22+
// Post an event that will dispatch the action on the UI thread and return immediately.
2223
dispatcher.dispatchAsync(LoginAction(username = "user", password = "123"))
2324
```
2425

25-
### Store
26-
The Stores are holders for application state and state mutation logic. In order to do so they expose pure reducer functions that are later invoked by the dispatcher.
26+
## Store
27+
The *Stores* are holders for application state and state mutation logic. In order to do so they expose pure reducer functions that are later invoked by the dispatcher.
2728

28-
The state is plain object (usually a data class) that holds all information needed to display the view. State should always be inmutable. State classes should avoid using framework elements (View, Camera, Cursor...) in order to facilitate testing.
29+
The state is a plain object (usually a `data class`) that holds all information needed to display the view. States should always be inmutable. State classes should avoid using framework elements (View, Camera, Cursor...) in order to facilitate testing.
2930

3031
Stores subscribe to actions to change the application state after a dispatch. Mini generates the code that links dispatcher actions and stores using the `@Reducer` annotation over a **non-private function that receives an Action as parameter**.
3132

32-
🚧WIP🚧
33+
```kotlin
34+
data class SessionState(val loginTask: Task = taskIdle(), val loggedUser: User? = null)
35+
36+
class SessionStore @Inject constructor(val controller: SessionController) : Store<SessionState>() {
37+
@Reducer
38+
fun login(action: LoginAction): SessionState {
39+
controller.login(action.username, action.password)
40+
return state.copy(loginTask = taskRunning(), loggedUser = null)
41+
}
42+
43+
@Reducer
44+
fun loginComplete(action: LoginCompleteAction): SessionState {
45+
return state.copy(loginTask = action.loginTask, loggedUser = action.user)
46+
}
47+
}
48+
```
49+
50+
## Actions
51+
An *Action* is a simple class that usually represents a use case. It can also contain a payload that includes data to perform said action. When an action is triggered, it will be delivered via dispatcher to the stores that are going to do something with the action to change their state.
52+
53+
For example, we may want to log in to a service. We would create an action like this one:
54+
```kotlin
55+
data class LoginAction(val username: String, val password: String)
56+
```
3357

34-
### Generated code
58+
When we receive the response from the server, we'll dispatch another action with the result:
59+
```kotlin
60+
data class LoginCompleteAction(val loginTask: Task, val user: User?)
61+
```
62+
63+
Actions will usually be triggered from Views or Controllers.
64+
65+
## Generated code
3566

3667
🚧WIP🚧
3768

38-
### View changes
39-
Each ``Store`` exposes a custom `StoreCallback` though the method `observe` or a `Flowable` if you wanna make use of RxJava. Both of them emits changes produced on their states, allowing the view to listen reactive the state changes. Being able to update the UI according to the new `Store` state.
69+
## View changes
70+
Each ``Store`` exposes a custom `StoreCallback` though the method `observe` or a `Flowable` if you want to make use of RxJava. Both of them emits changes produced on their states, allowing the view to listen reactive the state changes. Being able to update the UI according to the new `Store` state.
4071

4172
```kotlin
4273
//Using RxJava
@@ -52,31 +83,10 @@ Each ``Store`` exposes a custom `StoreCallback` though the method `observe` or a
5283

5384
If you make use of the RxJava methods, you can make use of the `SubscriptionTracker` interface to keep track of the `Disposables` used on your activities and fragments.
5485

55-
### Tasks
86+
## Tasks
5687
A Task is a basic object to represent an ongoing process. They should be used in the state of our `Store` to represent ongoing processes that must be represented in the UI.
57-
Having the next code:
58-
59-
```kotlin
60-
data class LoginAction(val username: String, val password: String)
61-
data class LoginCompleteAction(val loginTask: Task, val user: User?)
62-
```
63-
```kotlin
64-
data class SessionState(val loginTask: Task = taskIdle(), val loggedUser: User? = null)
6588

66-
class SessionStore @Inject constructor(val controller: SessionController) : Store<SessionState>() {
67-
@Reducer
68-
fun login(action: LoginAction): SessionState {
69-
controller.login(action.username, action.password)
70-
return state.copy(loginTask = taskRunning(), loggedUser = null)
71-
}
72-
73-
@Reducer
74-
fun loginComplete(action: LoginCompleteAction): SessionState {
75-
return state.copy(loginTask = action.loginTask, loggedUser = action.user)
76-
}
77-
}
78-
```
79-
The workflow will be:
89+
Given the example Stores and Actions explained before, the workflow will be:
8090

8191
- View dispatch `LoginAction`.
8292
- Store changes his `LoginTask` status to running and call though his SessionController which will do all the async work to log in the given user.
@@ -85,7 +95,7 @@ The workflow will be:
8595
- The Store changes his state to the given values from `LoginCompleteAction`.
8696
- The View redirect to the HomeActivity if the task was success or shows an error if not.
8797

88-
### Rx Utils
98+
## Rx Utils
8999
Mini includes some utility extensions over RxJava 2.0 to make easier listen state changes over the `Stores`.
90100

91101
- `mapNotNull`: Will emit only not null values over the given `map` clause.
@@ -107,7 +117,7 @@ For example:
107117

108118
If we continually listen the changes of a `Task` and we navigate to a specific screen when the `Task` becomes successful. The state will stay on SUCCESS and if we navigate back to the last screen we will be redirected again.
109119

110-
### Logging
120+
## Logging
111121
Mini includes a custom `LoggerInterceptor` to log any change in your `Store` states produced from an `Action`. This will allow you to keep track of your actions, changes and side-effects more easily.
112122
To add the LoggerInterceptor to your application you just need to add a single instance of it to your `Dispatcher` after initialize it in your `Application` class or dependency injection code.
113123
```kotlin
@@ -157,11 +167,72 @@ fun login_redirects_to_home_with_success_task() {
157167
```
158168

159169
## Setting Up
160-
161170
### Import the library
162171

163-
🚧WIP🚧
172+
Add the following dependencies to your main `build.gradle`:
173+
```groovy
174+
allprojects {
175+
repositories {
176+
maven { url "https://jitpack.io" }
177+
}
178+
}
179+
```
164180

165-
### Setting up your App file
181+
Add the following dependencies to your app's `build.gradle`:
166182

167-
🚧WIP🚧
183+
```groovy
184+
dependencies {
185+
def mini_version = "1.0.8"
186+
// Minimum working dependencies
187+
implementation "com.github.bq.mini-kotlin:mini-android:$mini_version"
188+
kapt "com.github.bq.mini-kotlin:mini-processor:$mini_version"
189+
190+
// RxJava 2 helper libraries
191+
implementation "com.github.bq.mini-kotlin:mini-rx2:$mini_version"
192+
implementation "com.github.bq.mini-kotlin:mini-rx2-android:$mini_version"
193+
194+
// Kodein helper libraries
195+
implementation "com.github.bq.mini-kotlin:mini-kodein:$mini_version"
196+
implementation "com.github.bq.mini-kotlin:mini-kodein-android:$mini_version"
197+
198+
// Testing helper libraries
199+
androidTestImplementation "com.github.bq.mini-kotlin:mini-testing:$mini_version"
200+
}
201+
```
202+
203+
### [Android] Setting up your App file
204+
205+
You'll need to add the following snippet to your `Application`'s `onCreate` method. If you don't have it, then create it and reference it in your `AndroidManifest.xml` file:
206+
207+
```kotlin
208+
val stores = listOf<Store<*>>(...) // Here you'll set-up you store list, you can retrieve it using your preferred DI framework
209+
val dispatcher = MiniGen.newDispatcher() // Create a new dispatcher
210+
211+
// Initialize Mini
212+
storeSubscriptions = MiniGen.subscribe(dispatcher, stores)
213+
stores.forEach { store ->
214+
store.initialize()
215+
}
216+
217+
// Optional: add logging interceptor to log action events
218+
dispatcher.addInterceptor(LoggerInterceptor(stores, { tag, msg ->
219+
Log.d(tag, msg)
220+
}))
221+
```
222+
223+
## License
224+
```
225+
Copyright 2019 BQ
226+
227+
Licensed under the Apache License, Version 2.0 (the "License");
228+
you may not use this file except in compliance with the License.
229+
You may obtain a copy of the License at
230+
231+
http://www.apache.org/licenses/LICENSE-2.0
232+
233+
Unless required by applicable law or agreed to in writing, software
234+
distributed under the License is distributed on an "AS IS" BASIS,
235+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
236+
See the License for the specific language governing permissions and
237+
limitations under the License.
238+
```

0 commit comments

Comments
 (0)