Kotlin Multiplatform Router for Android and iOS

Last update: Apr 12, 2022

Kompass

A powerful Kotlin Multiplatform Router for Android and iOS


GitHub top language Build Status Bintray GitHub last commit Gitter

Support

I am happy to help you with any problem on gitter
Feel free to open any new issue!

What Kompass can do for you

  • Perfect fit for MVVM, MVI, MVP, MVX architectures
  • Powerful routing concept that targets multiple platforms like Android, JVM & iOS
  • Easy to use API's
  • Highly configurable implementations

Android

  • Flexible routing with fragments
  • Built in solution for passing arguments to fragments
  • Very easy support for transitions/animations
  • No XML configuration
  • Built in DSL to configure the FragmentRouter
  • Survives configuration changes
  • Can restore the "routing stack" after process death

What Kompass can't do for now

While the core module is currently built and published for multiple platforms (JVM, iOS), there are no default Router implementations for any other platforms than Android yet. Those are currently "work in progress". Kompass can still be used as a common API for routing by providing custom implementations of a Router for your platform!

Setup

Step 1: Add the repository

Artifacts are linked to jCenter. Add jCenter repository to your root build.gradle

build.gradle

  allprojects {
     repositories {
        jcenter()
     }
  }

Step 2: Add the dependency (Multiplatform)

build.gradle.kts

kotlin {
    sourceSets {
        val commonMain by getting {
            dependencies {
                implementation("io.sellmair:kompass-core:0.2.0-alpha.5")
            }
        }
        
        /* Optional Android module */
        val androidMain by getting {
            dependencies {
                implementation("io.sellmair:kompass-android:0.2.0-alpha.5")
            }
        }
    }
}

Step 2: Add the dependency (Android Only)

build.gradle.kts

dependencies {
    implementation("io.sellmair:kompass-android:0.2.0-alpha.4")
}

Optional Step 3: (Android: Highly encouraged) Enable Kotlin's Android extensions (with @Parcelize)

build.gradle.kts

plugins {
    // ...
    id("org.jetbrains.kotlin.android.extensions")
}

// ...

// Currently still necessary for @Parcelize annotation
androidExtensions {
    isExperimental = true
}

Usage

Example

I recommend having a look at the example app built with Kompass



Gif



Defining routes

Routes can easily be represented by data classes. Let's say your App has three routes that you might want to display: A LoginRoute, ContactListRoute and a ChatRoute:

sealed class AppRoute : Route, Parcelable

@Parcelize
class LoginRoute: AppRoute()

@Parcelize
data class ContactListRoute(val contacts: List<Contact>): AppRoute()

@Parcelize
data class ChatRoute(val contact: Contact): AppRoute() 

All the arguments necessary to display a certain route should be present in the route itself. The example above uses the @Parcelize feature from the Kotlin's Android extensions

Creating a router instance (Android)

A FragmentRouter for Android can be configured quite easily by using the built in DSL for configuration.

router = FragmentRouter {
            transitions {
                register(LoginToContactListTransition())
                register(ContactListToChatTransition())
            }
            routing {
                route<LoginRoute> { LoginFragment::class }
                route<ContactListRoute> { ContactListFragment::class }
                route<ChatRoute> { ChatFragment::class }
            }
        }

The above DSL shows two configurations:

  • transitions: configures animations/transitions that should be running when routing
  • routing: configures which Fragment should be displayed for a certain route

Setting up a router instance (Android)

A FragmentRouter needs a ViewGroup to place the fragments in. This can be setup like this:

class MainActivity : AppCompatActivity(), KompassFragmentActivity {


    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)
        router.setup(savedInstanceState, R.id.container)
    }


    override fun onBackPressed() {
        router.popRetainRootImmediateOrFinish()
    }

}

Please note: In order to call the setup method, one needs to either implement KomapssFragmentActivity or KompassFragment!

Routing (Simple push)

Let's assume that the user taps on a certain contact in the contact list displayed by the ContactListRoute:

class ContactListViewModel {
 
    private val router = TODO("Maybe use DI?")
 
    fun onContactClicked(contact: Contact) {
        router.push(ChatRoute(contact))
    }

}

The code above will push the ChatRoute onto the "routing stack" and results in the ChatFragment being shown. Popping the "routing stack" will result in the ContactListFragment being displayed again.

Routing (Replacing the current route)

Let's assume the user successfully logged into your app. This should result in the current LoginRoute being replaced by the ContactListRoute

class LoginViewModel {

    private val router = TODO("What about Dagger?")
    
    fun onLoginSuccessful(user: User) {
        router.replaceTopWith(ContactListRoute(user.contacts))
    }
}

Wrapping multiple instructions into one lambda block will bundle them to one single operation on the routing stack. So you could alternatively write something like

fun onLoginSuccessful(user: User) {
    router { pop().push(ContactListRoute(user.contacts)) }
}

Routing (Arbitrary)

Kompass supports arbitrary routing: A instruction to the router is nothing more than a function from a list of routes to a new list of routes. Let's say your app would like to remove all ChatRoute with a certain contact

fun removeContactFromStack(contact: Contact) {
     router {
        with(filter { it.route.contact == contact })
     }
     
     //or
     
     router.plainStackInstruction { filter { it.route.contact == contact } }
}

Receiving the current route inside a Fragment

Accessing the route from within the any Fragment implementation is easily done by conforming to the KompassFragment interface:

class ContactListFragment : Fragment(), KompassFragment {
   
    override val router: FragmentRouter<AppRoute> = TODO() 
   
    private val route: ContactListRoute by route()
    
    override fun onCreate(savedInstanceState: Bundle?) {
        val contacts = route.contacts 
        
       //...
    }

}

Fragment Transitions

In order to support animations (fragment transitions) when routing you just need to implement a FragmentTransition. Example: Your chat app should show a Slide transition when going from the ContactListFragment to the ChatFragment and back. Simply implement the transition, check for your constraints and apply the transitions to the fragment. It is also possible to apply generic constraints to your transition using the GenericFragmentTransition API.

class ContactListToChatTransition : FragmentTransition {
    @SuppressLint("RtlHardcoded")
    override fun setup(
        transaction: FragmentTransaction,
        exitFragment: Fragment, exitRoute: Route,
        enterFragment: Fragment, enterRoute: Route
    ) {
        if (exitFragment is ContactListFragment && enterFragment is ChatFragment) {
            exitFragment.exitTransition = Slide(Gravity.LEFT)
            enterFragment.enterTransition = Slide(Gravity.RIGHT)
        }

        if (exitFragment is ChatFragment && enterFragment is ContactListFragment) {
            exitFragment.exitTransition = Slide(Gravity.RIGHT)
            enterFragment.enterTransition = Slide(Gravity.LEFT)
        }
    }
}

After the transition is implemented, just add it to the configuration of the FragmentRouter like seen above!

FragmentRouter { 
    transitions {
        register(LoginToContactListTransition())
    }
}

GitHub

https://github.com/sellmair/kompass
Comments
  • 1. Can't use objects as destination

    The annotation processor fails to generate the serialization methods for object destinations.

    Suppose the following destination:

    @Destination
    object HomeDestination: PodDestination()
    

    Kompass generates the following method:

    fun Kompass.Companion.bundleAsHomeDestination(bundle: Bundle): HomeDestination = HomeDestination()
    

    This calls HomeDestination() but should instead call HomeDestination.

    Reviewed by heinrichreimer at 2019-01-14 00:32
  • 2. Failing Build when using AndroidX.

    The annotation processor is failing when using androidX libs. Here's the stacktrace. [kapt] An exception occurred: java.lang.IllegalStateException: elementUtils.getTypeElement(this) must not be null at io.sellmair.kompass.compiler.extension.RenderContextUse$DefaultImpls.asElement(ProcessingEnvironment+use.kt:91) at io.sellmair.kompass.compiler.destination.visitor.AutoMapVisitor.asElement(AutoMapVisitor.kt:13) at io.sellmair.kompass.compiler.extension.RenderContextUse$DefaultImpls.asType(ProcessingEnvironment+use.kt:95) at io.sellmair.kompass.compiler.destination.visitor.AutoMapVisitor.asType(AutoMapVisitor.kt:13) at io.sellmair.kompass.compiler.extension.RenderContextUse$DefaultImpls.asType(ProcessingEnvironment+use.kt:119) at io.sellmair.kompass.compiler.destination.visitor.AutoMapVisitor.asType(AutoMapVisitor.kt:13) at io.sellmair.kompass.compiler.destination.visitor.AutoMapVisitor.destinationCase(AutoMapVisitor.kt:72) at io.sellmair.kompass.compiler.destination.visitor.AutoMapVisitor.destinationCase(AutoMapVisitor.kt:61) at io.sellmair.kompass.compiler.destination.visitor.AutoMapVisitor.buildImplementation(AutoMapVisitor.kt:39) at io.sellmair.kompass.compiler.destination.visitor.AutoMapVisitor.visit(AutoMapVisitor.kt:26) at io.sellmair.kompass.compiler.destination.visitor.AutoMapVisitor.visit(AutoMapVisitor.kt:16) at io.sellmair.kompass.compiler.destination.visitor.AutoMapVisitor.visit(AutoMapVisitor.kt:13) at io.sellmair.kompass.compiler.common.VisitorKt$plus$1.visit(Visitor.kt:10) at io.sellmair.kompass.compiler.common.VisitorKt$plus$1.visit(Visitor.kt:9) at io.sellmair.kompass.compiler.common.VisitorKt$plus$1.visit(Visitor.kt:9) at io.sellmair.kompass.compiler.DestinationProcessor.process(DestinationProcessor.kt:54) at org.jetbrains.kotlin.kapt3.base.ProcessorWrapper.process(annotationProcessing.kt:99) at com.sun.tools.javac.processing.JavacProcessingEnvironment.callProcessor(JavacProcessingEnvironment.java:794) at com.sun.tools.javac.processing.JavacProcessingEnvironment.discoverAndRunProcs(JavacProcessingEnvironment.java:705) at com.sun.tools.javac.processing.JavacProcessingEnvironment.access$1800(JavacProcessingEnvironment.java:91) at com.sun.tools.javac.processing.JavacProcessingEnvironment$Round.run(JavacProcessingEnvironment.java:1035) at com.sun.tools.javac.processing.JavacProcessingEnvironment.doProcessing(JavacProcessingEnvironment.java:1176) at com.sun.tools.javac.main.JavaCompiler.processAnnotations(JavaCompiler.java:1170) at com.sun.tools.javac.main.JavaCompiler.processAnnotations(JavaCompiler.java:1068) at org.jetbrains.kotlin.kapt3.base.AnnotationProcessingKt.doAnnotationProcessing(annotationProcessing.kt:55) at org.jetbrains.kotlin.kapt3.base.AnnotationProcessingKt.doAnnotationProcessing$default(annotationProcessing.kt:27) at org.jetbrains.kotlin.kapt3.AbstractKapt3Extension.runAnnotationProcessing(Kapt3Extension.kt:210) at org.jetbrains.kotlin.kapt3.AbstractKapt3Extension.analysisCompleted(Kapt3Extension.kt:175) at org.jetbrains.kotlin.kapt3.ClasspathBasedKapt3Extension.analysisCompleted(Kapt3Extension.kt:93) at org.jetbrains.kotlin.cli.jvm.compiler.TopDownAnalyzerFacadeForJVM$analyzeFilesWithJavaIntegration$2.invoke(TopDownAnalyzerFacadeForJVM.kt:98) at org.jetbrains.kotlin.cli.jvm.compiler.TopDownAnalyzerFacadeForJVM.analyzeFilesWithJavaIntegration(TopDownAnalyzerFacadeForJVM.kt:108) at org.jetbrains.kotlin.cli.jvm.compiler.TopDownAnalyzerFacadeForJVM.analyzeFilesWithJavaIntegration$default(TopDownAnalyzerFacadeForJVM.kt:85) at org.jetbrains.kotlin.cli.jvm.compiler.KotlinToJVMBytecodeCompiler$analyze$1.invoke(KotlinToJVMBytecodeCompiler.kt:370) at org.jetbrains.kotlin.cli.jvm.compiler.KotlinToJVMBytecodeCompiler$analyze$1.invoke(KotlinToJVMBytecodeCompiler.kt:61) at org.jetbrains.kotlin.cli.common.messages.AnalyzerWithCompilerReport.analyzeAndReport(AnalyzerWithCompilerReport.kt:101) at org.jetbrains.kotlin.cli.jvm.compiler.KotlinToJVMBytecodeCompiler.analyze(KotlinToJVMBytecodeCompiler.kt:361) at org.jetbrains.kotlin.cli.jvm.compiler.KotlinToJVMBytecodeCompiler.compileModules$cli(KotlinToJVMBytecodeCompiler.kt:126) at org.jetbrains.kotlin.cli.jvm.K2JVMCompiler.doExecute(K2JVMCompiler.kt:154) at org.jetbrains.kotlin.cli.jvm.K2JVMCompiler.doExecute(K2JVMCompiler.kt:51) at org.jetbrains.kotlin.cli.common.CLICompiler.execImpl(CLICompiler.java:94) at org.jetbrains.kotlin.cli.common.CLICompiler.execImpl(CLICompiler.java:50) at org.jetbrains.kotlin.cli.common.CLITool.exec(CLITool.kt:88) at org.jetbrains.kotlin.daemon.CompileServiceImpl$compile$1$1$2.invoke(CompileServiceImpl.kt:408) at org.jetbrains.kotlin.daemon.CompileServiceImpl$compile$1$1$2.invoke(CompileServiceImpl.kt:101) at org.jetbrains.kotlin.daemon.CompileServiceImpl$doCompile$$inlined$ifAlive$lambda$2.invoke(CompileServiceImpl.kt:929) at org.jetbrains.kotlin.daemon.CompileServiceImpl$doCompile$$inlined$ifAlive$lambda$2.invoke(CompileServiceImpl.kt:101) at org.jetbrains.kotlin.daemon.common.DummyProfiler.withMeasure(PerfUtils.kt:137) at org.jetbrains.kotlin.daemon.CompileServiceImpl.checkedCompile(CompileServiceImpl.kt:969) at org.jetbrains.kotlin.daemon.CompileServiceImpl.doCompile(CompileServiceImpl.kt:928) at org.jetbrains.kotlin.daemon.CompileServiceImpl.compile(CompileServiceImpl.kt:407) at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62) at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) at java.lang.reflect.Method.invoke(Method.java:498) at sun.rmi.server.UnicastServerRef.dispatch(UnicastServerRef.java:346) at sun.rmi.transport.Transport$1.run(Transport.java:200) at sun.rmi.transport.Transport$1.run(Transport.java:197) at java.security.AccessController.doPrivileged(Native Method) at sun.rmi.transport.Transport.serviceCall(Transport.java:196) at sun.rmi.transport.tcp.TCPTransport.handleMessages(TCPTransport.java:568) at sun.rmi.transport.tcp.TCPTransport$ConnectionHandler.run0(TCPTransport.java:826) at sun.rmi.transport.tcp.TCPTransport$ConnectionHandler.lambda$run$0(TCPTransport.java:683) at java.security.AccessController.doPrivileged(Native Method) at sun.rmi.transport.tcp.TCPTransport$ConnectionHandler.run(TCPTransport.java:682) at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1142) at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:617) at java.lang.Thread.run(Thread.java:745)

    Reviewed by roshanpisharody at 2018-09-12 10:55
  • 3. Keep state on Process-Death

    Kompass doesn't keep the current state on process-death. Routing to a certain destination, terminating the app, re-opening will lead route to the last destination. This could be changed by separating lambdas and destinations from inside the backstack and serializing the backstack to the onSaveInstance bundle.

    Reviewed by fablue at 2018-01-29 20:01
  • 4. [73] LoginProcessingFragment stuck loading

    Fix issue where LoginProcessingFragment gets stuck loading.

    It's actually not required to remove the Handler callback, as this will be handled correctly without the stop method in the case of both configuration change and process death. onCleared already does a perfect job of cleaning these up when they need to be cleaned up!

    Reviewed by isaac-udy at 2019-05-15 11:10
  • 5. Decouple Android dependencies

    I believe, Kompass is a great router not only for Android. If we decouple the Android-related implementation detail into separate modules, we could use Kompass also for other frameworks. I guess that would have a clear benefit and shouldn't even be too complicated as the only Android-dependent stuff is Bundle and how to set a sail, which of course can be generalized using some clever interfaces and modules.

    Clearly, this would break some code, but I would like to investigate the idea further.

    Reviewed by heinrichreimer at 2019-01-16 13:29
  • 6. Question about destination .

    Hi , currently im using your library its great and ease to implement and may i know how to identify which is destination is currently is active? or is there a currently function to that please let me know thanks.

    Reviewed by maccam04 at 2018-03-20 08:59
  • 7. Sample: LoginProcessingFragment can get stuck in loading state forever

    https://github.com/sellmair/kompass/blob/ceed98827b5aa5687df78b83d9de8060ffc7e0dc/android-example--fragment/src/main/java/io/sellmair/kompass/android/example/viewmodel/LoginProcessingViewModel.kt#L37-L39

    Because of viewModel.stop() in onPause, if app is put to background and brought to foreground while LoginProcessingFragment is on front, then the app gets stuck here forever.

    You might want to consider some form of observable that the is bound to onStart/onStop rather than onPause (and no counterpart).

    After rotation, it still gets stuck on the screen:

    Screenshot 2019-05-06 at 13 39 37
    Reviewed by Zhuinden at 2019-05-06 11:39
  • 8. androidx lifecycle dependency needed

    The app crashes since it can not find LifecycleEventObserver which is part of androidx.lifecycle:lifecycle-extensions:2.1.0-alpha04. It would be nice if this extra dependency is not needed 🤙🏼.

    Reviewed by JulianBissekkou at 2019-08-26 17:20
  • 9. `RoutingStack.Element`: Enforce correct hashCode and equals

    Wrong equals or hashCode implementations will result in silently breaking behaviours in DefaultFragmentStackPatcher . e.g. fragments will be removed to early, since they cannot be found in the newStack

    Reviewed by sellmair at 2019-04-14 11:05
  • 10. Check if target is suitable

    Currently, the compiler itself does not check whether or not the target for a given destination makes sense (if it is a suitable view, fragment or activity). This will lead to a build failure that is hard to understand.

    We should:

    • Check the correctness at kapt-time
    • Print a nice error message telling the user what is wrong
    Reviewed by sellmair at 2018-08-20 20:29
  • 11. Convenience extensions for Activity and Fragment

    As an user of this library I want to get my destinations more straight forward. I want to type

    val myDestination = myDestination()
    

    Instead of

    val myDestination = arguments?.tryAsMyDestination()?:throw IllegalArgumentException()
    

    Inside my fragment/activity

    Reviewed by sellmair at 2018-02-07 18:53
  • 12. Discussion: DSL syntax

    I'd like to start a bit of a discussion around the DSL syntax.

    Multiple paths to one goal:

    router.clear()
    router.push(Route())
    
    router { 
        clear() push Route()
    }
    
    router {
        clear()
        push(Route())
    }
    

    There are several ways to perform similar actions. The first way isn't correct, as it won't bundle up the actions as a single item, but my understanding is the last two ways are essentially the same. ̛I think it would be a better idea to have a single syntax, even if it means that a single "push" needs to be performed as:

    router { push() }
    

    Builder syntax:

    router {
        clear() push RouteA() push RouteB() push RouteC()
    }
    

    The builder continuation syntax that allows a user to push a series of routes like this is inconsistent with the use of infix functions, as a user is unable to split these onto different lines. The following code will not compile:

    router {
        clear() push RouteA()
            push RouteB()
            push RouteC()
    }
    

    I have two questions:

    1. Is the builder/continuation syntax actually useful? Could commands just be split onto different lines?
    2. If the builder/continuation syntax is useful, should the use of infix be removed, so a user can split the continuation over multple lines?
    Reviewed by isaac-udy at 2019-05-14 00:10
  • 13. Direct `FragmentRouteStorage`

    It may be possible to implement a FragmentRouteStorage that takes the key of a route and retrieves the router later from the router by using the key again

    Reviewed by sellmair at 2019-04-22 16:15
Extendable MVI framework for Kotlin Multiplatform with powerful debugging tools (logging and time travel), inspired by Badoo MVICore library
Extendable MVI framework for Kotlin Multiplatform with powerful debugging tools (logging and time travel), inspired by Badoo MVICore library

Should you have any questions or ideas please welcome to the Slack channel: #mvikotlin Inspiration This project is inspired by Badoo MVICore library.

May 17, 2022
MVU for Kotlin Multiplatform

Oolong Oolong is an Elm inspired Model-View-Update (MVU) implementation for Kotlin multiplatform. As the name implies, three core concepts comprise th

May 16, 2022
Redux implementation for Kotlin (supports multiplatform JVM, native, JS, WASM)

Redux-Kotlin ![badge][badge-ios] A redux standard for Kotlin that supports multiplatform projects. Full documentation at http://reduxkotlin.org. Misso

May 16, 2022
Unidirectional Data Flow in Kotlin - Port of https://github.com/ReSwift/ReSwift to Kotlin
Unidirectional Data Flow in Kotlin - Port of https://github.com/ReSwift/ReSwift to Kotlin

ReKotlin Port of ReSwift to Kotlin, which corresponds to ReSwift/4.0.0 Introduction ReKotlin is a Redux-like implementation of the unidirectional data

May 11, 2022
Android Clean Architecture💎 Base Project Android with Kotlin and MVVM applying clean architecture
 Android Clean Architecture💎 Base Project Android with Kotlin and MVVM applying clean architecture

Android Clean Architecture?? Base Project Android with Kotlin and MVVM applying clean architecture

May 18, 2022
MVVM RECIPE ANDROID APP Is an app where I show how to use MVVM, retrofit, dagger hilt, coroutine, liveData, Kotlin, navigation component, and so on...
MVVM RECIPE ANDROID APP Is an app where I show how to use MVVM, retrofit, dagger hilt, coroutine, liveData, Kotlin, navigation component, and so on...

MVVM RECIPE ANDROID APP Is an app where I show how to use MVVM, retrofit, dagger hilt, coroutine, liveData, kotlin, navigation component, and so on...

Apr 4, 2022
JeTaxi is built on Clean Architecture-MVVM with Kotlin and follows modern android development trends.
 JeTaxi is built on Clean Architecture-MVVM with Kotlin and follows modern android development trends.

JeTaxi is built on Clean Architecture-MVVM with Kotlin and follows modern android development trends. Also, It uses some of Jetpack and popular libraries. These are Kotlin Coroutine-Flow, kotlinx.serialization, Hilt, Compose, Accompanist, Retrofit2, OkHttp3, Chucker, MockWebServer, Truth.

Mar 7, 2022
Android App using Kotlin, MVVM, ViewModel, LiveData, Coroutines, Room and DataBinding
Android App using Kotlin, MVVM, ViewModel, LiveData, Coroutines, Room and DataBinding

Words Android App using Kotlin, MVVM, ViewModel, LiveData, Coroutines, Room and

Mar 9, 2022
A sample project in Kotlin to demonstrate AndroidX, MVVM, Coroutines, Hilt, Room, Data Binding, View Binding, Retrofit, Moshi, Leak Canary and Repository pattern.

This repository contains a sample project in Kotlin to demonstrate AndroidX, MVVM, Coroutines, Hilt, Room, Data Binding, View Binding, Retrofit, Moshi, Leak Canary and Repository pattern

May 9, 2022
A sample to showcase Kotlin, MVVM, Koin, Coroutines, StateFlow, Room, WorkManager, Retrofit and Unit test.

TVMaze-Cache A sample to showcase Kotlin, MVVM, Koin, Coroutines, StateFlow, Room, WorkManager, Retrofit and Unit test. Features MVVM Architecture + R

Mar 13, 2022
📒Note taking app, MVVM with Google Architectural components Room, LiveData and ViewModel written in Kotlin, androidx libraries
📒Note taking app, MVVM with Google Architectural components Room, LiveData and ViewModel written in Kotlin, androidx libraries

?? MyNotes Note taking Android App using androidx libraries, MVVM with Google Architectural components Room, LiveData and ViewModel. Written in Kotlin

May 1, 2022
An android app built using Kotlin following Multi-Module Clean Architecture MVVM
An android app built using Kotlin following Multi-Module Clean Architecture MVVM

RickyandMorty An android app built using Kotlin that consumes RickyadMorty API to display characters.It has been built following Clean Architecture Pr

Feb 3, 2022
MVVM Kotlin Android Architecture
MVVM Kotlin Android Architecture

Model-View-ViewModel (ie MVVM) Model-View-ViewModel (ie MVVM) is a template of a client application architecture, proposed by John Gossman as an alter

Mar 13, 2022
MVVM Android Studio Kotlin Project Dog Images
MVVM Android Studio Kotlin Project Dog Images

MVVM Android Studio Kotlin Project Dog Images This project implement: MVVM Retro

Apr 17, 2022
Android MVVM with Single Activity sample app that uses kotlin coroutines flow
Android MVVM with Single Activity sample app that uses kotlin coroutines flow

Android MVVM with Single Activity sample app that uses kotlin coroutines flow. This is a sample app that uses kotlin coroutines flow , stateflow. This

Apr 8, 2022
simple app used Kotlin MVVM Dagger2 Room Coroutines Retrofit2

Exhibits Application which retrieves data from Webserver (via Retrofit), saves it into Room and get from it if user is offline. There are applying MVV

Oct 14, 2021
Clean Architecture - Kotlin, MVVM, Use cases
Clean Architecture - Kotlin, MVVM, Use cases

CleanArchitecture Is Clean Architecture only MVVM ? NO, MVVM is a part of clean architecture. MVVM includes Model, View and ViewModel and in addition

May 13, 2022
Kotlin+Flow+Retrofit+OKHttp+ViewBanding+ViewModel+LiveData封装的一个Kotlin版本的MVVM框架

Gitee 地址:kmvvm Github 地址:kmvvm CHANGE LOG 技术要点 支持Flow+Retrofit+OkHttp实现链式http请求 支持Rxjava+Retrofit+OkHttp实现链式http请求 封装基类:BaseActivity、BaseVMActivity、Ba

May 23, 2022
Ceci est une application d'actualités de l'architecture MVVM avec Kotlin
Ceci est une application d'actualités de l'architecture MVVM avec Kotlin

MVVM-Appli Infos Ceci est une application d'actualités de l'architecture MVVM avec Kotlin utilisant des composants : Retrofit, Room, Coroutines, et Na

Nov 4, 2021