/MviPattern

Android Mvi Pattern Example with Data Flow Reactive

GNU General Public License v3.0GPL-3.0

MviPattern

Android Mvi Pattern Example with Data Flow Reactive

Component

Repository

It is responsible for communicating with the true owner of the data (remote server, local database) and returning the domain object.

The Interface should not know who the owner of the Data is.

interface MessageRepository {
    suspend fun getMessages(): ImmutableList<Message>

    fun getMessageFlow(): Flow<ImmutableList<Message>>
}


class MessageRepositoryImpl @Inject constructor(
        // Remote Data Source
        private val messageApi: MessageApi,
        // Local Data Source
        private val messageDao: MessageDao
) : MessageRepository {

    override suspend fun getMessages(): ImmutableList<Message> {
        val messages = messageApi.getMessages().response
        messageDao.upsertMessages()
        return messages
    }

    override fun getMessageFlow(): Flow<ImmutableList<Message>> {
        return messageDao.getMessageFlow()
    }
}

UseCase

Performs a single, defined domain function. Receive injections from repositories and other UseCases.

class GetMessagesUseCase @Inject constructor(
        private val messsageRepository: MessageRepository
) {

    operator suspend fun invoke(): ImmutableList<Message> {
        return messageRepository.getMessages()
    }
}

Middleware

Handle state changes for a single domain that integrates multiple features. Inject a UseCase.

From Middleware, this can be screen-dependent.

class MessageMiddleware @Inject constructor(
        private val getMessagesUseCase: GetMessagesUseCase
) : Middleware<MessagesState> {

    override suspend fun process(currentState: MessagesState, intent: Intent): MessagesState {
        return when (intent) {
            is ScreenInitIntent -> currentState.reduce(messages = getMessagesUseCase())
            else -> currentState
        }
    }

}

ViewModel

Control the overall behavior for one screen. Middleware is injected.

@HiltViewModel
class MessageListViewModel @Inject(
        private val messageMiddleware : MessageMiddleware
) : MviViewModel<MessageListState>() {

    override fun produceMiddlewares(): List<Middleware<in MessageListState>> {
        return listOf(messageMiddleware)
    }

    override fun produceInitState(): MessageListState {
        return MessageListState()
    }
}

Flow Diagram

State Update With User Interaction

Image

@Composable
fun MyMessage(message : String) {
    val emitter = LocalIntentEmitter.current
    Text(text = message, modifier = Modifier.clickable(onClick = { emitter.dispatch(MessageClickIntent(message)) }))
}

State Update Without User Interaction

Image2

Socket example, we sometimes need to change the state of the screen by means other than direct action by the user in our application.

class MessageMiddleware @Inject constructor(
    private val observeMessageSocketUseCase : ObserveMessageSocketUseCase
) : Middleware<MessageState> {

    override fun produceIntentFlow() : Flow<Intent> {
        return observeMessageSocketUseCase().map { MessageSocketChangedIntent(it) }
    }
}