Skip to content

Commit

Permalink
Merge pull request #3 from Nek-12/1.0.0-alpha01
Browse files Browse the repository at this point in the history
1.0.0-alpha01: Safe state update API.
  • Loading branch information
Nek-12 authored Nov 22, 2022
2 parents dfa24fd + b58d00e commit 5565b17
Show file tree
Hide file tree
Showing 37 changed files with 948 additions and 459 deletions.
57 changes: 29 additions & 28 deletions .editorconfig
Original file line number Diff line number Diff line change
Expand Up @@ -106,7 +106,7 @@ ij_java_for_statement_wrap = off
ij_java_generate_final_locals = false
ij_java_generate_final_parameters = false
ij_java_if_brace_force = never
ij_java_imports_layout = $android.**, $androidx.**, $com.**, $junit.**, $net.**, $org.**, $java.**, $javax.**, $*, |, android.**, |, androidx.**, |, com.**, |, junit.**, |, net.**, |, org.**, |, java.**, |, javax.**, |, *, |
ij_java_imports_layout = $android.**,$androidx.**,$com.**,$junit.**,$net.**,$org.**,$java.**,$javax.**,$*,|,android.**,|,androidx.**,|,com.**,|,junit.**,|,net.**,|,org.**,|,java.**,|,javax.**,|,*,|
ij_java_indent_case_from_switch = true
ij_java_insert_inner_class_imports = false
ij_java_insert_override_annotation = true
Expand All @@ -128,8 +128,8 @@ ij_java_label_indent_absolute = false
ij_java_label_indent_size = 0
ij_java_lambda_brace_style = end_of_line
ij_java_layout_static_imports_separately = true
ij_java_line_comment_add_space = false
ij_java_line_comment_at_first_column = true
ij_java_line_comment_add_space = true
ij_java_line_comment_at_first_column = false
ij_java_method_annotation_wrap = split_into_lines
ij_java_method_brace_style = end_of_line
ij_java_method_call_chain_wrap = off
Expand Down Expand Up @@ -244,7 +244,7 @@ ij_java_variable_annotation_wrap = off
ij_java_visibility = public
ij_java_while_brace_force = never
ij_java_while_on_new_line = false
ij_java_wrap_comments = false
ij_java_wrap_comments = true
ij_java_wrap_first_method_in_call_chain = false
ij_java_wrap_long_lines = false

Expand All @@ -259,12 +259,12 @@ ij_properties_spaces_around_key_value_delimiter = false
ij_visual_guides = none
ij_editorconfig_align_group_field_declarations = false
ij_editorconfig_space_after_colon = false
ij_editorconfig_space_after_comma = true
ij_editorconfig_space_after_comma = false
ij_editorconfig_space_before_colon = false
ij_editorconfig_space_before_comma = false
ij_editorconfig_spaces_around_assignment_operators = true

[{*.ant, *.fxml, *.jhm, *.jnlp, *.jrxml, *.rng, *.tld, *.wsdl, *.xml, *.xsd, *.xsl, *.xslt, *.xul}]
[{*.ant,*.fxml,*.jhm,*.jnlp,*.jrxml,*.rng,*.tld,*.wsdl,*.xml,*.xsd,*.xsl,*.xslt,*.xul}]
ij_continuation_indent_size = 4
ij_visual_guides = none
ij_xml_align_attributes = false
Expand All @@ -285,7 +285,7 @@ ij_xml_space_inside_empty_tag = true
ij_xml_text_wrap = normal
ij_xml_use_custom_settings = true

[{*.bash, *.sh, *.zsh}]
[{*.bash,*.sh,*.zsh}]
indent_size = 2
tab_width = 2
ij_visual_guides = none
Expand All @@ -296,7 +296,7 @@ ij_shell_redirect_followed_by_space = false
ij_shell_switch_cases_indented = false
ij_shell_use_unix_line_separator = true

[{*.c, *.c++, *.cc, *.cp, *.cpp, *.cu, *.cuh, *.cxx, *.h, *.h++, *.hh, *.hp, *.hpp, *.hxx, *.i, *.icc, *.ii, *.inl, *.ino, *.ipp, *.m, *.mm, *.pch, *.tcc, *.tpp}]
[{*.c,*.c++,*.cc,*.cp,*.cpp,*.cu,*.cuh,*.cxx,*.h,*.h++,*.hh,*.hp,*.hpp,*.hxx,*.i,*.icc,*.ii,*.inl,*.ino,*.ipp,*.m,*.mm,*.pch,*.tcc,*.tpp}]
ij_visual_guides = none
ij_c_add_brief_tag = false
ij_c_add_getter_prefix = true
Expand Down Expand Up @@ -575,7 +575,7 @@ ij_c_while_brace_force = never
ij_c_while_on_new_line = false
ij_c_wrap_property_declaration = off

[{*.cmake, CMakeLists.txt}]
[{*.cmake,CMakeLists.txt}]
ij_visual_guides = none
ij_cmake_align_multiline_parameters_in_calls = false
ij_cmake_force_commands_case = 2
Expand All @@ -591,7 +591,7 @@ ij_cmake_spaces_within_method_call_parentheses = false
ij_cmake_spaces_within_method_parentheses = false
ij_cmake_spaces_within_while_parentheses = false

[{*.gant, *.gradle, *.groovy, *.gy}]
[{*.gant,*.gradle,*.groovy,*.gy}]
ij_visual_guides = none
ij_groovy_align_group_field_declarations = false
ij_groovy_align_multiline_array_initializer_expression = false
Expand Down Expand Up @@ -648,7 +648,7 @@ ij_groovy_for_statement_right_paren_on_new_line = false
ij_groovy_for_statement_wrap = off
ij_groovy_if_brace_force = never
ij_groovy_import_annotation_wrap = 2
ij_groovy_imports_layout = *, |, javax.**, java.**, |, $*
ij_groovy_imports_layout = *,|,javax.**,java.**,|,$*
ij_groovy_indent_case_from_switch = true
ij_groovy_indent_label_blocks = true
ij_groovy_insert_inner_class_imports = false
Expand Down Expand Up @@ -771,8 +771,9 @@ ij_groovy_while_brace_force = never
ij_groovy_while_on_new_line = false
ij_groovy_wrap_long_lines = false

[{*.gradle.kts, *.kt, *.kts, *.main.kts}]
ij_visual_guides = none
[{*.kt,*.kts}]
ij_visual_guides = 120
ij_kotlin_line_break_after_multiline_when_entry = false
ij_kotlin_align_in_columns_case_branch = false
ij_kotlin_align_multiline_binary_operation = false
ij_kotlin_align_multiline_extends_list = false
Expand Down Expand Up @@ -805,12 +806,12 @@ ij_kotlin_extends_list_wrap = on_every_item
ij_kotlin_field_annotation_wrap = split_into_lines
ij_kotlin_finally_on_new_line = false
ij_kotlin_if_rparen_on_new_line = false
ij_kotlin_import_nested_classes = true
ij_kotlin_imports_layout = *, java.**, javax.**, kotlin.**, ^
ij_kotlin_import_nested_classes = false
ij_kotlin_imports_layout = *,java.**,javax.**,kotlin.**,^
ij_kotlin_insert_whitespaces_in_simple_one_line_method = true
ij_kotlin_keep_blank_lines_before_right_brace = 2
ij_kotlin_keep_blank_lines_in_code = 2
ij_kotlin_keep_blank_lines_in_declarations = 2
ij_kotlin_keep_blank_lines_before_right_brace = 0
ij_kotlin_keep_blank_lines_in_code = 1
ij_kotlin_keep_blank_lines_in_declarations = 1
ij_kotlin_keep_first_column_comment = true
ij_kotlin_keep_indents_on_empty_lines = false
ij_kotlin_keep_line_breaks = true
Expand All @@ -824,7 +825,7 @@ ij_kotlin_method_parameters_right_paren_on_new_line = true
ij_kotlin_method_parameters_wrap = off
ij_kotlin_name_count_to_use_star_import = 2147483647
ij_kotlin_name_count_to_use_star_import_for_members = 2147483647
ij_kotlin_packages_to_use_import_on_demand = java.util.*, kotlinx.android.synthetic.**, io.ktor.**
ij_kotlin_packages_to_use_import_on_demand = ^
ij_kotlin_parameter_annotation_wrap = on_every_item
ij_kotlin_space_after_comma = true
ij_kotlin_space_after_extend_colon = true
Expand Down Expand Up @@ -855,7 +856,7 @@ ij_kotlin_wrap_elvis_expressions = 1
ij_kotlin_wrap_expression_body_functions = 1
ij_kotlin_wrap_first_method_in_call_chain = false

[{*.har, *.json}]
[{*.har,*.json}]
indent_size = 2
ij_visual_guides = none
ij_json_keep_blank_lines_in_code = 0
Expand All @@ -869,24 +870,24 @@ ij_json_spaces_within_braces = false
ij_json_spaces_within_brackets = false
ij_json_wrap_long_lines = false

[{*.htm, *.html, *.sht, *.shtm, *.shtml}]
[{*.htm,*.html,*.sht,*.shtm,*.shtml}]
ij_visual_guides = none
ij_html_add_new_line_before_tags = body, div, p, form, h1, h2, h3
ij_html_add_new_line_before_tags = body,div,p,form,h1,h2,h3
ij_html_align_attributes = true
ij_html_align_text = false
ij_html_attribute_wrap = normal
ij_html_block_comment_at_first_column = true
ij_html_do_not_align_children_of_min_lines = 0
ij_html_do_not_break_if_inline_tags = title, h1, h2, h3, h4, h5, h6, p
ij_html_do_not_indent_children_of_tags = html, body, thead, tbody, tfoot
ij_html_do_not_break_if_inline_tags = title,h1,h2,h3,h4,h5,h6,p
ij_html_do_not_indent_children_of_tags = html,body,thead,tbody,tfoot
ij_html_enforce_quotes = false
ij_html_inline_tags = a, abbr, acronym, b, basefont, bdo, big, br, cite, cite, code, dfn, em, font, i, img, input, kbd, label, q, s, samp, select, small, span, strike, strong, sub, sup, textarea, tt, u, var
ij_html_inline_tags = a,abbr,acronym,b,basefont,bdo,big,br,cite,cite,code,dfn,em,font,i,img,input,kbd,label,q,s,samp,select,small,span,strike,strong,sub,sup,textarea,tt,u,var
ij_html_keep_blank_lines = 2
ij_html_keep_indents_on_empty_lines = false
ij_html_keep_line_breaks = true
ij_html_keep_line_breaks_in_text = true
ij_html_keep_whitespaces = false
ij_html_keep_whitespaces_inside = span, pre, textarea
ij_html_keep_whitespaces_inside = span,pre,textarea
ij_html_line_comment_at_first_column = true
ij_html_new_line_after_last_attribute = never
ij_html_new_line_before_first_attribute = never
Expand All @@ -898,7 +899,7 @@ ij_html_space_inside_empty_tag = false
ij_html_text_wrap = normal
ij_html_uniform_ident = false

[{*.markdown, *.md}]
[{*.markdown,*.md}]
ij_visual_guides = none
ij_markdown_force_one_space_after_blockquote_symbol = true
ij_markdown_force_one_space_after_header_symbol = true
Expand All @@ -912,7 +913,7 @@ ij_markdown_min_lines_around_block_elements = 1
ij_markdown_min_lines_around_header = 1
ij_markdown_min_lines_between_paragraphs = 1

[{*.yaml, *.yml}]
[{*.yaml,*.yml}]
indent_size = 2
ij_visual_guides = none
ij_yaml_align_values_properties = do_not_align
Expand Down
43 changes: 29 additions & 14 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,21 +1,36 @@
# Flow MVI
![GitHub](https://img.shields.io/github/license/Nek-12/FlowMVI)
![GitHub last commit](https://img.shields.io/github/last-commit/Nek-12/FlowMVI)
![Maintenance](https://img.shields.io/maintenance/yes/2022)
[![Downloads on Jitpack](https://jitpack.io/v/Nek-12/FlowMVI/month.svg)](https://jitpack.io/#Nek-12/FlowMVI.svg)
![Issues](https://img.shields.io/github/issues/Nek-12/FlowMVI)

This is an MVI implementation based on coroutines with a few main goals:

1. Being simple to understand, implement and use
2. Following the Principle of Least Responsibility - all communication happens through strictly defined contract
3. Featuring a clean and readable DSL
4. Being thread-safe but asynchronous

## Let's get started:

Choose your dependency:
[![](https://jitpack.io/v/Nek-12/FlowMVI.svg)](https://jitpack.io/#Nek-12/FlowMVI)

```toml
[versions]
flowmvi = # ...

[dependencies]
flowmvi-android = { module = "com.github.Nek-12.FlowMVI:android", version.ref = "flowmvi" }
flowmvi-compose = { module = "com.github.Nek-12.FlowMVI:android-compose", version.ref = "flowmvi" }
flowmvi-core = { module = "com.github.Nek-12.FlowMVI:core", version.ref = "flowmvi" }
```

```kotlin
val flowVersion = /* look at the widget */
implementation("com.github.Nek-12.FlowMVI:core:${flowVersion}") //does not depend on any particular platform
implementation("com.github.Nek-12.FlowMVI:android-compose:${flowVersion}") //For Jetpack Compose Android projects
implementation("com.github.Nek-12.FlowMVI:android-view:${flowVersion}") //For View-based Android projects
implementation("com.github.Nek-12.FlowMVI:core:${flowMVIVersion}") //does not depend on any particular platform
implementation("com.github.Nek-12.FlowMVI:android-compose:${flowMVIVersion}") //For Jetpack Compose Android projects
implementation("com.github.Nek-12.FlowMVI:android-view:${flowMVIVersion}") //For View-based Android projects
```

## Core:
Expand All @@ -38,18 +53,17 @@ sealed interface ScreenAction: MVIAction {
}


val store = MVIStore<ScreenState, ScreenIntent, ScreenAction>(
initialState = DisplayingCounter(0),
val store by launchedStore<ScreenState, ScreenIntent, ScreenAction>(
scope = eventProcessingCoroutineScope,
initial = DisplayingCounter(0),
behavior = ActionShareBehavior.DISTRIBUTE,
reduce = { intent -> /*...*/ }
)

store.launch(eventProcessingScope)

//somewhere in the ui layer

store.subscribe(
coroutineScope,
consumerCoroutineScope,
consume = { action -> /* ... */ },
render = { state -> /* ... */ }
)
Expand All @@ -63,10 +77,9 @@ class ScreenViewModel: MVIViewModel<ScreenState, ScreenIntent, ScreenAction>(ini

override fun recover(from: Exception) = Error(from) // optional

override suspend fun reduce(intent: ScreenIntent) = when (intent) {

override suspend fun reduce(intent: ScreenIntent): Unit = when(intent) {
//no-op if state is not DisplayingCounter
is ClickedCounter -> withState<DisplayingCounter> { //this -> DisplayingCounter
is ClickedCounter -> updateState<DisplayingCounter> { //this -> DisplayingCounter

send(ShowMessage("Incremented counter"))

Expand Down Expand Up @@ -98,13 +111,13 @@ fun ComposeScreen() = MVIComposable(
```

If you don't want to use MVIComposable, just collect the actions flow using coroutineScope and render states
using `viewModel.states.collectAsStateWithLifecycle()`
using `viewModel.states.collectAsStateOnLifecycle()`

## Android (View):

```kotlin

//ViewModel and Model classes have not changed
// ViewModel and Model classes have not changed

class ScreenFragment: Fragment(), MVIView<ScreenState, ScreenIntent, ScreenAction> {

Expand Down Expand Up @@ -134,6 +147,8 @@ see [sample app](/app/src/main/java/com/nek12/flowMVI/sample/view/NoBaseClassVie

For more information and more elaborate examples, see the sample app.

- [] More docs are coming soon with much more detail.

## License

```
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ fun <T> StateFlow<T>.collectAsStateOnLifecycle(
): State<T> = collectAsStateOnLifecycle(value, context, lifecycleState)

@Composable
@Suppress("ComposableParametersOrdering", "ComposableNaming")
@Suppress("ComposableParametersOrdering", "ComposableNaming", "ComposableFunctionName")
fun <T> Flow<T>.collectOnLifecycle(
lifecycleState: Lifecycle.State = Lifecycle.State.STARTED,
consume: suspend CoroutineScope.(T) -> Unit,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,6 @@ fun <S : MVIState, I : MVIIntent, A : MVIAction, VM : MVIProvider<S, I, A>> MVIC
lifecycleState: Lifecycle.State = Lifecycle.State.STARTED,
content: @Composable MVIIntentScope<I, A>.(state: S) -> Unit,
) {

val scope = rememberScope(provider, lifecycleState)

// see [LifecycleOwner.subscribe] in :android for reasoning behind the dispatcher
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,9 @@
@file:Suppress("ComposableNaming", "ComposableEventParameterNaming")
@file:Suppress(
"ComposableNaming",
"ComposableEventParameterNaming",
"TopLevelComposableFunctions",
"ComposableFunctionName"
)

package com.nek12.flowMVI.android.compose

Expand Down
1 change: 1 addition & 0 deletions android/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -10,4 +10,5 @@ dependencies {
api(project(":core"))
api(libs.lifecycle.runtime)
implementation(libs.lifecycle.viewmodel)
api(libs.kotlinx.coroutines.android)
}
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,6 @@ inline fun <S : MVIState, I : MVIIntent, A : MVIAction> LifecycleOwner.subscribe
crossinline render: (state: S) -> Unit,
lifecycleState: Lifecycle.State = Lifecycle.State.STARTED,
) = lifecycleScope.launch {

// using multiple repeatOnLifecycle instead of flowWithLifecycle to avoid creating hot flows

// https://github.com/Kotlin/kotlinx.coroutines/issues/2886
Expand Down
Loading

0 comments on commit 5565b17

Please sign in to comment.