Giter Site home page Giter Site logo

pedroql / mvflow Goto Github PK

View Code? Open in Web Editor NEW
125.0 4.0 10.0 759 KB

Simple Android MVI architecture using Kotlin Flows

License: MIT License

Kotlin 64.31% HTML 18.05% CSS 17.64%
android mvi mvi-architecture kotlin mvi-android kotlin-flow android-library

mvflow's Issues

Consider moving from SharedFlow to Channel

MVFlow uses a SharedFlow to handle the Effects, then launchWhenX {} in the View to collect the effects.

However this raises some good points about not using SharedFlow and instead use a Channel(Channel.BUFFERED). Because some Effects can be lost due to configuration changes. And even using launchWhen(Started/Resumed) {} is not immune to this, and he proposes a simple observer class to handle it.

Handler State Become Stale for Actions That Produce Long Running Flows

There are a couple ways I can see to fix this, neither of which is backwards compatible.

  1. Pass StateFlow<State> as parameter instead of State in Handler
typealias Handler<State, Action, Mutation> = (StateFlow<State>, Action) -> Flow<Mutation>

//Flow<Action>.collectIntoHandler changes to
handler.invoke(state, action, LoggingEffectSender(logger))

//Inside handler functions the current state can be accessed using
{ state, action ->
  state.value
}
  1. Pass function () -> State as parameter instead of State in handler
typealias Handler<State, Action, Mutation> = (() -> State, Action) -> Flow<Mutation>

//Flow<Action>.collectIntoHandler changes to
handler.invoke({state.value}, action, LoggingEffectSender(logger))

//Inside handler functions the current state can be accessed using
{ state, action ->
   state()
}

Example: Notice that MyState(count=1) and MyState(count=2) are printed when it should print all the way up to count=6

data class MyState(val count: Int = 1)
  sealed class MyAction {
    object Start : MyAction()
  }

  sealed class MyMutation {
    data class IncrementCount(val newCount: Int) : MyMutation()
  }

  @Test
  fun testStaleState() {
    runBlocking {
      val handler: Handler<MyState, MyAction, MyMutation> = { state, action ->
        when (action) {
          MyAction.Start -> flow {
            emit(MyMutation.IncrementCount(state.count + 1))
            kotlinx.coroutines.delay(100)
            emit(MyMutation.IncrementCount(state.count + 1))
            kotlinx.coroutines.delay(100)
            emit(MyMutation.IncrementCount(state.count + 1))
            kotlinx.coroutines.delay(100)
            emit(MyMutation.IncrementCount(state.count + 1))
            kotlinx.coroutines.delay(100)
            emit(MyMutation.IncrementCount(state.count + 1))
          }
        }
      }
      val reducer: Reducer<MyState, MyMutation> = { state, mutation ->
        when(mutation){
          is MyMutation.IncrementCount -> state.copy(count =  mutation.newCount)
        }
      }
      MVFlow(MyState(), handler, reducer, this).takeView(this, object : MVFlow.View<MyState, MyAction>{
        override fun actions(): Flow<MyAction> {
          return flowOf(MyAction.Start)
        }

        override fun render(state: MyState) {
          println(state)
        }
      })
    }
  }

Ability to allow same Flow emission

We're implementing our click events (e.g: buttonA), but unable to (re)click the buttonA unless we're resetting the events/click another events

The buttonA is just a simple showing bottom dialog, we're providing an ID to its intent then fetch API and display the data retrieved from server

Is it possible to allow same flow emission for particular/specific events? or should we implement reset state instead manually?

Best practices to send a throwable from Fragment/Activity?

MVFlow class

    sealed class Error : Action {
       object MaxChar : Error()
       object NPE : Error()
       object UPA : Error()
    }

In Activity/Fragment

private val actionsChannel = Channel<Action>()

// somewhere in the code
// e.g: 1
viewBinding.editText.doAfterTextChanged {
   if (it.length > 200) actionsChannel.offer(Action.Error.MaxChar)
}

// e.g: 2

try { 
    doSomething(null)
} catch (e: NullPointerException) {
    actionsChannel.offer(Action.Error.NPE)
}

fun doSomething(nonNull: String) {
 // do something
} 

// e.g: 3

private var lateinit abc: String

// we forgot to initialized,
// therefore will throw UninitializedPropertyAccessException: lateinit property `abc`
try { 
   doSomething(abc) 
} catch (e: UninitializedPropertyAccessException) {
   actionsChannel.offer(Action.Error.UPA)
}

Question

or is there any alternative to distinguish between throwing errors in our view (e.g: without an action), maybe using Effects #30?

Android sample app wont even sync on latest Android Studio Iguana

Can't get the sample app to compile or even Gradle sync, opened the sample_app folder in Android Studio Iguana and no matter what jdk I select I get:

BUG! exception in phase 'semantic analysis' in source unit '_BuildScript_' Unsupported class file major version 61

How to stop an async task launched by the handler

I have a serch that each time that you type a character it launches a request. The problem is that if I don't cancel I could have this race condition:

  • Action Search "mv"
  • Action Search "mvf"
  • Mutation with items from "mvf"
  • Mutation with items from "mv"

At the end the user sees the results of "mv" instead of "mvf".

If I can cancel the collection of the current old flow I would fix it. Maybe there is other way to avoid this problem. I tried out this one: badoo/MVICore#68 (comment) but Flow doesn't have a takeUntil.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.