Skip to content

Commit 3669c78

Browse files
authored
docs-seo (#126)
2 parents 80407ba + 1904404 commit 3669c78

File tree

19 files changed

+671
-237
lines changed

19 files changed

+671
-237
lines changed

.cursorignore

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
# Add directories or file patterns to ignore during indexing (e.g. foo/ or *.csv)
2+
**/.kotlin/
3+
**/.gradle/
4+
**/.idea/
5+
**/build
6+
.idea
7+
.kotlin
8+
.build
9+
.gradle

.github/workflows/docs.yml

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -55,7 +55,9 @@ jobs:
5555
cache-dependency-path: './docs/package-lock.json'
5656

5757
- name: Update docs/README.md
58-
run: cp ./README.md ./docs/docs/README.md
58+
run: |
59+
chmod -R +x ./scripts
60+
./scripts/update_readme.sh
5961
6062
- name: Install docs deps
6163
run: cd docs && npm ci

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -162,6 +162,7 @@ hs_err_pid*
162162
/.idea/other.xml
163163

164164
### Custom rules
165+
.kotlin
165166
.firebase-service-account.json
166167
/.idea/artifacts/**
167168
!gradle/gradle-wrapper.jar

README.md

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
![](https://opensource.respawn.pro/FlowMVI/banner.png)
1+
![FlowMVI Framework Banner](https://opensource.respawn.pro/FlowMVI/banner.webp)
22

33
[![CI](https://github.com/respawn-app/FlowMVI/actions/workflows/ci.yml/badge.svg)](https://github.com/respawn-app/FlowMVI/actions/workflows/ci.yml)
44
![License](https://img.shields.io/github/license/respawn-app/flowMVI)
@@ -384,7 +384,13 @@ timerPlugin(timer).test(Loading) {
384384
IDE plugin generates code and lets you debug and control your app remotely:
385385
[![Plugin](https://img.shields.io/jetbrains/plugin/v/25766?style=flat)](https://plugins.jetbrains.com/plugin/25766-flowmvi)
386386

387-
https://github.com/user-attachments/assets/05f8efdb-d125-4c4a-9bda-79875f22578f
387+
<video
388+
src='https://github.com/user-attachments/assets/05f8efdb-d125-4c4a-9bda-79875f22578f'
389+
controls
390+
width="100%"
391+
alt="FlowMVI IDE Plugin Demo">
392+
Your browser does not support the video element. You can view the demo at our website.
393+
</video>
388394

389395
## People love the library:
390396

docs/docs/README.md

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,13 @@
1+
---
2+
title: FlowMVI
3+
title_meta: FlowMVI - Kotlin Architecture Framework
4+
sidebar_label: Home
5+
sidebar_position: 0
6+
hide_title: true
7+
description: Architecture Framework for Kotlin. Reuse every line of code. Handle all errors automatically. No boilerplate. Analytics, metrics, debugging in 3 lines. 50+ features.
8+
slug: /
9+
---
10+
111
# FlowMVI
212

313
### Stub readme file, do not edit!

docs/docs/integrations/_category_.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
{
2-
"position": 3,
2+
"position": 4,
33
"label": "Integrations",
44
"collapsible": true,
55
"collapsed": false

docs/docs/misc/CONTRIBUTING.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
---
2-
sidebar_position: 2
2+
sidebar_position: 3
33
sidebar_label: Contribution guide
44
---
55

docs/docs/misc/FAQ.md

Lines changed: 18 additions & 98 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,10 @@
1+
---
2+
sidebar_position: 2
3+
---
4+
15
# FAQ
26

3-
### "Cannot inline bytecode" error
7+
### How to fix "Cannot inline bytecode" error?
48

59
The library's minimum JVM target is set to 11 (sadly still not the default in Gradle).
610
If you encounter an error:
@@ -20,8 +24,6 @@ kotlin {
2024
}
2125
}
2226
}
23-
24-
2527
```
2628

2729
And in your android gradle files, set:
@@ -38,36 +40,9 @@ android {
3840
If you support Android API \<26, you will also need to
3941
enable [desugaring](https://developer.android.com/studio/write/java8-support).
4042

43+
### How to name Intents, States, Actions?
4144

42-
### Tips:
43-
44-
* Avoid using `sealed class`es and use `sealed interface`s whenever possible. Not only this reduces object allocations,
45-
but also prevents developers from putting excessive logic into their states and/or making private/protected
46-
properties. State is a simple typed data holder, so if you want to use protected properties or override functions,
47-
it is likely that something is wrong with your architecture.
48-
* Use nested class imports and import aliases to clean up your code, as contract class names can be long sometimes.
49-
* Use value classes to reduce object allocations if your Intents are being sent frequently, such as for text field
50-
value changes or scroll events.
51-
* You can use the `updateStateImmediate` function to optimize the
52-
performance of the store by bypassing all checks and plugins.
53-
* Overall, there are cases when changes are so frequent that you'll want to just leave some logic on the UI layer to
54-
avoid polluting the heap with garbage collected objects and keep the UI performant.
55-
* Avoid subscribing to a bunch of flows in your Store. The best way to implement a reactive UI pattern is to
56-
use `combine(vararg flows...)` and merge all of your data streams into one flow, and then just use the `transform`
57-
block to handle the changes.
58-
* With this, you can be sure that your state is consistent even if you have 20 parallel data streams from different
59-
sources e.g. database cache, network, websockets and other objects.
60-
* Avoid using platform-level imports and code in your Store/Container/ViewModel whenever possible. This is optional, but
61-
if you follow this rule, your **Business logic can be multiplatform**! This is also very good for the architecture.
62-
* There is an ongoing discussion about whether to name your intents starting with the verb or with the noun.
63-
* Case 1: `ClickedCounter`
64-
* Case 2: `CounterClicked`
65-
In general, this is up to your personal preference, just make sure you use a single style across all of your
66-
Contracts. I personally like to name intents starting with the verb (Case 1) for easier autosuggestions from the
67-
IDE.
68-
69-
### Opinionated naming design
70-
45+
There is an ongoing discussion on how to name Intents/States/Actions.
7146
Here's an example of rules we use at [Respawn](https://respawn.pro) to name our Contract classes:
7247

7348
* `MVIIntent` naming should be `<TypeOfActionInPastTense><Target>`.
@@ -77,8 +52,6 @@ Here's an example of rules we use at [Respawn](https://respawn.pro) to name our
7752
Do not include `Screen` postfix. `GoToHome`~~Screen~~.
7853
* `MVIState`s should be named using verbs in present tense using a gerund. Examples: `EditingGame`, `DisplayingSignIn`.
7954

80-
## FAQ
81-
8255
### My intents are not reduced! When I click buttons, nothing happens, the app just hangs.
8356

8457
* Did you call `Store.start(scope: CoroutineScope)`?
@@ -95,27 +68,15 @@ Here's an example of rules we use at [Respawn](https://respawn.pro) to name our
9568
subscribe to actions.
9669
4. Try to use an `onUndeliveredIntent` handler of a plugin or install a logging plugin to debug missed events.
9770

98-
### Why does `updateState` and `withState` not return the resulting state? Why is there no `state` property I can access?
99-
100-
FlowMVI is a framework that enables you to build highly parallel, multi-threaded systems. In such systems, multiple
101-
threads may modify the state of the `Store` in parallel, leading to data races, thread races, live locks and other
102-
nasty problems. To prevent that, FlowMVI implements a strategy called "transaction serialization" which only allows
103-
**one** client at a time to read or modify the state. Because of that, you can be sure that your state won't change
104-
unexpectedly while you're working with it. However, any state that you pass outside of the scope of `withState` or
105-
`updateState` should be **considered invalid** immediately. You can read more about serializable state transactions in
106-
the [article](https://proandroiddev.com/how-to-safely-update-state-in-your-kotlin-apps-bf51ccebe2ef).
107-
Difficulties that you are facing because of this likely have an easy solution that requires a bit more thinking.
108-
As you continue working with FlowMVI, updating states safely will come naturally to you.
109-
11071
### In what order are intents, plugins and actions processed?
11172

112-
* Intents: FIFO or undefined based on the configuration parameter `parallelIntents`.
113-
* Actions: FIFO.
114-
* States: FIFO.
115-
* Plugins: FIFO (Chain of Responsibility) based on installation order.
116-
* Decorators: FIFO, but after all of the regular plugins.
73+
* Intents: FIFO or undefined based on the configuration parameter `parallelIntents`
74+
* Actions: FIFO
75+
* States: FIFO
76+
* Plugins: FIFO (Chain of Responsibility) based on installation order
77+
* Decorators: FIFO, but after all of the regular plugins
11778

118-
### When I consume an Action, the other actions are delayed or do not come.
79+
### When I consume an Action, the other actions are delayed or do not come
11980

12081
Since actions are processed sequentially, make sure you launch a coroutine to not prevent other actions from coming and
12182
suspending the scope. This is particularly obvious with things like snackbars that suspend in compose.
@@ -126,13 +87,12 @@ You shouldn't. Use an Intent / Action to follow the contract, unless you are usi
12687
In that case, expose the parent `ImmutableContainer` / `ImmutableStore` type to hide the `intent` function from
12788
subscribers.
12889

129-
### How to use paging?
90+
### How to use androidx.paging?
13091

13192
Well, this is a tricky one. `androidx.paging` breaks the architecture by invading all layers of your app with UI
13293
logic. The best solution we could come up with is just passing a PagingFlow as a property in the state.
13394
This is not good, because the state becomes mutable and non-stable, but there's nothing better we could come up with,
13495
but it does its job, as long as you are careful not to recreate the flow and pass it around between states.
135-
If you have an idea or a working Paging setup, let us know and we can add it to the library!
13696

13797
The Paging library also relies on the `cachedIn` operator which is tricky to use in `whileSubscribed`, because that
13898
block is rerun on every subscription, recreating and re-caching the flow.
@@ -145,23 +105,13 @@ val pagingFlow by cache {
145105
}
146106
```
147107

148-
### I have like a half-dozen various flows or coroutines and I want to make my state from those data streams. Do I subscribe to all of those flows in my store?
108+
### I have a lot of data streams. Do I subscribe to all of the flows in my store?
149109

150110
It's preferable to create a single flow using `combine(vararg flows...)` and produce your state based on that.
151111
This will ensure that your state is consistent and that there are no unnecessary races in your logic.
152112
As flows add up, it will become harder and harder to keep track of things if you use `updateState` and `collect`.
153113

154-
### How do I handle errors?
155-
156-
There are two ways to do this.
157-
158-
1. First one is using one of the Result wrappers, like [ApiResult](https://github.com/respawn-app/apiresult), a monad
159-
from Arrow.io or, as the last resort, a `kotlin.Result`.
160-
2. Second one involves using a provided `recover` plugin that will be run when an exception is
161-
caught in plugins or child coroutines, but the plugin will be run **after** the job was already cancelled, so you
162-
cannot continue the job execution anymore.
163-
164-
### But that other library allows me to define 9000 handlers, actors, processors and whatnot - and I can reuse reducers. Why not do the same?
114+
### But that other library has 9000 handlers, reducers and whatnot. Why not do the same?
165115

166116
In general, a little boilerplate when duplicating intents is worth it to keep the consistency of actions and intents
167117
of screens intact.
@@ -188,43 +138,13 @@ fun <S : MVIState, I : MVIIntent, A : MVIAction> StoreBuilder<S, I, A>.reduce(
188138

189139
### How to avoid class explosion?
190140

191-
1. Modularize your app. The library allows you to do that easily.
192-
2. Use nested classes. For example, you can define an `object ScreenContract` and nest your state, intents, and actions
141+
1. Modularize the app. The library allows to do that easily.
142+
2. Use nested classes. For example, define an `object ScreenContract` and nest your state, intents, and actions
193143
inside to make autocompletion easier.
194144
3. Use `LambdaIntent`s. They don't require subclassing `MVIIntent`.
195145
4. Disallow Actions for your store. Side effects are sometimes considered an anti-pattern, and you may want to disable
196146
them if you care about the architecture this much.
197147

198-
### What if I have sub-states or multiple Loading states for different parts of the screen?
199-
200-
Create nested classes and host them in your parent state.
201-
Example:
202-
203-
```kotlin
204-
sealed interface NewsState : MVIState {
205-
data object Loading : NewsState
206-
data class DisplayingNews(
207-
val suggestionsState: SuggestionsState,
208-
val feedState: FeedState,
209-
) : NewsState {
210-
sealed interface SuggestionsState {
211-
data object Loading : SuggestionsState
212-
data class DisplayingSuggestions(val suggestions: List<Suggestion>) : SuggestionsState
213-
}
214-
215-
sealed interface FeedState {
216-
data object Loading : FeedState
217-
data class DisplayingFeed(val news: List<News>) : FeedState
218-
}
219-
}
220-
}
221-
```
222-
223-
* Use `T.withType<Type>(block: Type.() -> Unit)` to cast your sub-states easier as
224-
the `(this as? State)?.let { } ?: this` code can look ugly.
225-
* Use `T.typed<Type>()` to perform a safe cast to the given state to clean up the code.
226-
* You don't have to have a top-level sealed interface. If it's simpler, you can just use a data class on the top level.
227-
228148
### I want to use a resource or a framework dependency in my store. How can I do that?
229149

230150
The best solution would be to avoid using platform dependencies such as string resources.

docs/docs/plugins/debugging.md renamed to docs/docs/misc/debugging.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
---
2-
sidebar_position: 5
2+
sidebar_position: 1
33
---
44

55

docs/docs/plugins/custom.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
---
22
sidebar_position: 2
3+
sidebar_label: Making Custom Plugins
34
---
45

56
# Creating custom plugins

0 commit comments

Comments
 (0)