A modern Android architecture framework built on Jetpack & Kotlin.

Overview

RainbowCake

Build Status

RainbowCake logo

RainbowCake is an Android architecture framework, providing tools and guidance for building modern Android applications. It builds on top of Jetpack, both in terms of code and ideas.

Some of the main goals of this architecture:

  • Give guidance on all aspects of the application, covering not just the View architecture,
  • Clearly separate concerns between different layers and components,
  • Always keep views in a safe and consistent state with ViewModels,
  • Handle configuration changes (and even process death) gracefully,
  • Make offloading work to background threads trivial.

While RainbowCake is heavily opinionated, it also encourages you to deviate from it as needed. Feel free to pick and choose the ideas and library artifacts provided according to your own application’s needs!

For more information, see the official documentation on rainbowcake.dev.

Setup

RainbowCake is available from MavenCentral.

repositories {
    mavenCentral()
}

It ships in several artifacts - feel free to pick and choose from them (for more info, see Dependencies):

dependencies {
    implementation "co.zsmb:rainbow-cake-core:1.6.0" // Core library (required)
    implementation "co.zsmb:rainbow-cake-dagger:1.6.0" // Dagger 2 support
    implementation "co.zsmb:rainbow-cake-hilt:1.6.0" // Dagger Hilt support
    implementation "co.zsmb:rainbow-cake-koin:1.6.0" // Koin support
    implementation "co.zsmb:rainbow-cake-navigation:1.6.0" // Navigation features
    implementation "co.zsmb:rainbow-cake-timber:1.6.0" // Internal logging through Timber
    testImplementation "co.zsmb:rainbow-cake-test:1.6.0" // Testing utilities
}

License

Copyright 2021 Marton Braun

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

   http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
Comments
  • Add databinding support

    Add databinding support

    Hi Márton,

    First of all congratulations for your library, it is just a masterpiece that can save hundreds of hours to the community.

    I'd like to suggest you to support in some way databinding on RainbowCakeFragment. I'm using your library on some projects and I think It could be interesting to support this pattern.

    I've solved it doing that on my project:

    class MyFragment : RainbowCakeFragment<MyViewState, MyViewModel>() {
    
        override fun getViewResource() = R.layout.my_fragment
    
        override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
            super.onCreateView(inflater, container, savedInstanceState) // I'm forced to call super
            binding = MyFragmentListBinding.inflate(inflater, container, false)
            return binding.root
        }
    
        override fun render(viewState: MyViewState) {
            TransitionManager.beginDelayedTransition(listFragmentRoot)
            binding.viewState = viewState
        }
    }
    

    As you can see it simplifies a lot the code on the Fragment side, moving the logic to the XML file. Maybe you can find a better way of allowing this on next versions of the library without having to override getViewResource() method.

    opened by jmpaya 5
  • Support another Application() and Activity()

    Support another Application() and Activity()

    Hi @zsmb13,

    After being "stucked" with Dialogs and BottomSheets, I'm stucked with another problem today : deal with other libraries that already overrides Application and Activity.

    The lib I want to integrate is https://github.com/akexorcist/Localization, which implement its own LocalizationApplication and LocalizationActivity.

    I suppose it's out of the RainbowCake's scope, but maybe if we cannot integrate it completely we can at least expose modules rainbow-cake-core-localization & rainbow-cake-dagger-localization, as an alternative ? And of course make it for other libraries if needed. You can see my commit here with the current modules, just take it as Proof of concept for modules described above.

    If you're not interested, is it ok to you if I publish my fork to Jitpack ?

    Open to discuss with pleasure, Thanks!

    opened by julienherrero 4
  • The koin project's maven group id has changed

    The koin project's maven group id has changed

    The koin project's maven group id was previously org.koin and is now io.insert-koin. Also, the jcenter() repository is being shut down so I've removed it.

    Reference: github.com/InsertKoinIO

    opened by Benjiko99 4
  • No way to pass parameters to ViewModel when providing it to the Fragment (using Koin)

    No way to pass parameters to ViewModel when providing it to the Fragment (using Koin)

    Koin lets me define additional parameters to pass to the constructor of my ViewModels when they're being injected. RainbowCake doesn't support this.

    val uiModule = module {
        viewModel { (detailId: String) -> DetailViewModel(id, get()) }
    }
    
    class DetailFragment : RainbowCakeFragment<DetailViewState, DetailViewModel>() {
    
        // No way to pass parameters to getViewModelFromFactory()
        override fun provideViewModel() = getViewModelFromFactory()
    }
    

    Since I'm forced to implement provideViewModel() which is called from RainbowCakeFragment#onCreate(), I'm left with this ugly workaround to pass parameters to my ViewModel:

    Workaround: member variable initialized before super.onCreate()

    class DetailFragment : RainbowCakeFragment<DetailViewState, DetailViewModel>() {
    
        private lateinit var detailId: String
    
        override fun provideViewModel(): DetailViewModel {
            return getViewModel { parametersOf(detailId) }
        }
    
        override fun onCreate(savedInstanceState: Bundle?) {
            if (savedInstanceState == null) {
                DetailFragmentArgs.fromBundle(requireArguments()).let { args ->
                    detailId = args.detailId
                }
            }
    
            super.onCreate(savedInstanceState)
    
            viewModel.loadData()
        }
    }
    
    opened by Benjiko99 4
  • Support BottomSheet Dialog Fragments

    Support BottomSheet Dialog Fragments

    Hi, I am using RainbowCake to rework from scratch an application for my enterprise, and I am facing this same issue https://github.com/rainbowcake/rainbowcake/issues/21

    One of the most reason I use RainbowCake is the UI Render/State handling (great job!), so I'd like to use this mechanism in BottomSheetDialogFragment with RainbowCakeViewModel + Dagger. Maybe we'll can extend this to DialogFragment too.

    I know this come with little duplications, but I don't know how to do differently for now.

    Feel free to make any feedback or any change !

    opened by julienherrero 2
  • Toothpick support

    Toothpick support

    Has anyone tried to use this framework with DI from Toothpick? It shouldn't matter as long as you provide factory for viewmodels though.

    If no one has — what can I do to make framework work with it?

    opened by dobrowins 2
  • RainbowCakeConfiguration is not configurable in unit tests

    RainbowCakeConfiguration is not configurable in unit tests

    Hi,

    RainbowCakeConfiguration is only configurable from the Application which means the unit tests will have a default consumeExecuteExceptions = true, even if it's set to false in the application, thus I cannot test the exceptions inside the execute() methods. Any workarounds for this?

    opened by dntks 2
  • Add Hilt support

    Add Hilt support

    This PR will add the following changes:

    • A new module called rainbow-cake-hilt
    • A new demo application called hilt-demo
    • Update dependencies

    Resolves #6

    opened by stewe93 1
  • Provide guidance on modifying the library within a project

    Provide guidance on modifying the library within a project

    I wanted to add the library as a git submodule into my project so that it becomes a part of my codebase and I can make any modifications to it easily and not have to distribute them through maven or anything.

    I couldn't get it to work. I then also tried just regularly forking the repo and building it though mavenLocal but that also didn't work, as you have custom scripts in your build.gradle that trigger on build and try to sign the build and publish it to your maven reposities. Honestly I don't have any experience with this and it would nice if you could provide some help.

    opened by Benjiko99 1
  • How to stop reload data on navigator.pop() action?

    How to stop reload data on navigator.pop() action?

    Hi I have simple question I Add fragment A -> navigator.add(A()) then I Add Fragment B -> navigator.add(B()) after that when I came back to fragment A(navigator.pop()) Fragment A reloaded

    What can I do to stop reload Fragment?

    thanks

    opened by amirzeinaly 1
  • Mapping functional

    Mapping functional

    Class Mapper Provides functionality for mapping. Uses MappingProfileProvider to perform mapping TypeA to TypeB declared in one of the MappingProfiles mappings lists.

    Class MappingProfileProvider serves as a bridge between Mapper and MappingProfile. Ensures the uniqueness of the declaration for typed mapping functions. Also allows the mapping function to use nested mapping functions declared in others MappingProfile.

    Abstract class MappingProfile contains a list of mappings functions.
    The following example shows how to declare mapping Type Long to Type String.

    class MappingExampleProfile : MappingProfile({
        createMap<Long, String> { this.toString() }
    })
    

    We may later use it for nested mappings.

    createMap<Int, String> { mapper ->
            mapper.map<Long, String>(this.toLong())
        }
    

    How to use. Method of delivery MappingProfile to Mapper сan be any, depending on the user's implementation.

    Koin way: You must call registerMapper() in any of your koin modules. After you can declare MappingProfile in any of your modules:

    val AppModule = module {
       registerMapper()
       mappingProfile(MappingExampleProfile())
    }
    

    After inject Mapper to whatever you want using koin get() or inject() functions.

    Dagger 2 way: You must declare MappingProfile at your dagger module:

    @Provides
    @IntoSet
    fun provideMappingExampleProfile(): MappingProfile = MappingExampleProfile()
    

    After declare Mapper and MappingProfileProvider

    @Provides
    @Singleton
    fun provideMappingProfileProvider(
            profiles: Set<@JvmSuppressWildcards MappingProfile>
    ): MappingProfileProvider {
        return MappingProfileProvider
                .createProfileProvider(profiles.toList())
    }
    
    @Provides
    @Singleton
    fun provideMapper(mappingProfileProvider: MappingProfileProvider): Mapper {
        return Mapper(mappingProfileProvider)
    }
    

    No DI way(not recommended but one of possible): You can manually delivery MappingProfileProvider to Mapper

    val mapper = Mapper(
            MappingProfileProvider.createProfileProvider(
                    listOf(
                            MappingExampleProfile()
                    )
            )
    )
    

    Also you can add MappingProfile to already registered MappingProfileProvider by addProfile(profile: MappingProfile) function.

    opened by Andrushka1012 1
  • Events not being delivered if posted in the init block

    Events not being delivered if posted in the init block

    Events that are posted synchronously from within the ViewModel's init block are never delivered to the Activity/Fragment.

    The Activity has not yet started observing the events at this point (while the ViewModel class is being created), but they are not queued up and delivered even once observing begins.

    Relevant code in RainbowCakeActivity

    override fun onCreate(savedInstanceState: Bundle?) {
            viewModel = provideViewModel()
    
            viewModel.events.observe(this) { event ->
                event?.let { onEvent(it) }
            }
            viewModel.queuedEvents.observe(this) { event ->
                event?.let { onEvent(it) }
            }
        }
    

    class MyViewModel : 
        RainbowCakeViewModel<MyViewState>(MyViewState.Content()) {
    
        object MyEvent : QueuedOneShotEvent
    
        init {
            postQueuedEvent(MyEvent)
        }
    }
    
    class MyActivity : RainbowCakeActivity<MyViewState, MyViewModel>() {
    
        override fun provideViewModel() = getViewModelFromFactory()
    
        override fun render(viewState: MyViewState) {}
    
        // WTF: onEvent is never called !!!
        override fun onEvent(event: OneShotEvent) {
            when (event) {
                is MyEvent -> {
                    Toast.makeText(this, "MyEvent received", Toast.LENGTH_LONG).show()
                }
            }
        }
    }
    

    A workaround is to call postEvent() from within a execute block, giving the Activity enough time to start observing the events.

    opened by Benjiko99 1
Releases(1.6.0-RELEASE)
  • 1.6.0-RELEASE(Sep 28, 2021)

    1.6.0

    Twenty-second release of RainbowCake.

    What's new

    • Added a removeObserver method to LiveDataCollection

    Dependency updates

    • Kotlin 1.5.31
    Source code(tar.gz)
    Source code(zip)
  • 1.5.0-RELEASE(Aug 31, 2021)

    Twenty-first release of RainbowCake.

    What's new

    Dagger Hilt support

    Using Dagger Hilt? RainbowCake now has you covered! Huge thanks to stewe93 for contributing this.

    1. Include the new Hilt artifact:

      implementation "co.zsmb:rainbow-cake-hilt:$rainbow_cake_version"
      
    2. Set up Hilt following the official Android guide.

    3. Call into the getViewModelFromFactory function of the co.zsmb.rainbowcake.hilt package in your Activities or Fragments.

    Check out the hilt-demo module for an example of a basic setup.

    Note: You may also use multiple DI solutions within the same RainbowCake project simultaneously, for example if you're migrating a project piece by piece. Even shared ViewModels will work!

    There is now a Blank Hilt starter project available showcasing a blank app set up with RainbowCake's Hilt support.

    Dependency updates

    • Android Gradle Plugin 7.0.1
    • Kotlin 1.5.30
    • Coroutines 1.5.1
    • AppCompat 1.3.1
    • ConstraintLayout 2.1.0
    • Material 1.4.0
    • Lifecycle 2.3.1
    • Dagger 2.38.1
    • Koin 3.1.2
    • Timber 5.0.0
    Source code(tar.gz)
    Source code(zip)
  • 1.4.0(Jun 10, 2021)

    Twentieth release of RainbowCake.

    What's new

    Integration of requireKTX

    Instead of providing its own extensions for requiring values from Bundles, RainbowCake now depends on requireKTX to handle these operations. The require style methods in RainbowCake are now deprecated and will be removed.

    Updated Koin module to use Koin 3.x

    Migrated the Koin integration module from 2.x to 3.x, including the change of dependency coordinates from org.koin to io.insert-koin. For more about the 3.x version of Koin, see its documentation.

    Thanks to Benjiko99 for notifying me about this change.

    Minor things

    • Expose a CoroutineScope as the receiver of the lambda passed to execute methods
    • Mark ioContext as @InternalRainbowCakeApi instead of using deprecations on it
    • Migrate to an up-to-date Maven publishing setup

    Dependency updates

    • Removed jcenter as a dependency
    • Android Gradle Plugin 4.2.1
    • Kotlin 1.5.10
    • Coroutines 1.5.0
    • AppCompat 1.3.0
    • Material 1.3.0
    • Lifecycle 2.3.1
    • Dagger 2.36
    • Koin 3.0.2
    • ConstraintLayout 2.0.4
    Source code(tar.gz)
    Source code(zip)
  • 1.3.0-RELEASE(Dec 1, 2020)

    Nineteenth release of RainbowCake.

    What's new

    Support for DialogFragment and BottomSheetDialogFragment

    RainbowCake now ships two classes for supporting special kinds of Fragments. Using the new RainbowCakeDialogFragment and RainbowCakeBottomSheetDialogFragment (now isn't that a mouthful!) classes works very similarly to using RainbowCakeFragment.

    Thanks to julienherrero for their contribution that kicked this off.

    Dependency updates

    • Kotlin 1.4.20
    • Android Gradle Plugin 4.1.1
    Source code(tar.gz)
    Source code(zip)
  • 1.2.0-RELEASE(Oct 29, 2020)

    Eighteenth release of RainbowCake.

    What's new

    Eased requirements for layout inflation in RainbowCakeFragments

    Previously, this base Fragment strictly required overriding the getViewResource method, and inflated the layout returned from that method in onCreateView, which was also mandatory to call if overridden.

    This was inconvenient when using View Binding or Data Binding, so now:

    • getViewResource is no longer abstract, instead it returns 0 by default. If you don't override getViewResource, you must override onCreateView.
    • Overriding onCreateView no longer requires a call to the super method.

    Introduced the @InternalRainbowCakeApi opt-in annotation

    This annotation is only for internal use between RainbowCake's modules. Previous visibility hacks have been replaced with this new annotation. You should generally avoid opting into its usage, as it's not guaranteed public API.

    The coroutineScope used by RainbowCakeViewModel is now exposed as @InternalRainbowCakeApi for extensions that need access to this scope.

    Small changes

    • Removed the Application receiver of the rainbowCake configuration function, to make it easier to call

    Dependency updates

    • Kotlin 1.4.10
    • Android Gradle Plugin 4.1.0
    • Dagger 2.29.1
    • Gradle wrapper 6.7
    • AndroidX libraries to latest versions
    Source code(tar.gz)
    Source code(zip)
  • 1.1.0-RELEASE(Aug 26, 2020)

    Seventeenth release of RainbowCake.

    What's new:

    Kotlin 1.4 update

    The project is now compiled with Kotlin 1.4, the latest stable version of Kotlin 🎉. This cleaned up a bit of the implementation, and most importantly, all library modules now have explicit API mode enabled, ensuring that all public API is explicitly marked as public.

    Small things

    • Fixed an issue with built-in logging #16 (Thanks to Tamás Vágó!)
    • Updated target and compile SDK versions to 30
    • Version updates for various dependencies
    Source code(tar.gz)
    Source code(zip)
  • 1.0.0-RELEASE(Jun 27, 2020)

    Sixteenth release of RainbowCake.

    What's new:

    Removed deprecated constructs

    A handful or previously deprecated pieces of code are now removed. If you want to migrate away from them, use 0.7.0 and IDE migration features before upgrading to 1.0.0.

    • JobViewModel: replace usages with RainbowCakeViewModel
    • RainbowCakeViewModel#postEvent: update view state from the UI thread instead _ Navigation extensions in co.zsmb.rainbowcake.extensions: use the methods from the co.zsmb.rainbowcake.navigation.extensions package instead
    • The rainbow-cake-channels artifact: use Flows instead

    Small stuff

    • ViewModels are now initialized in onCreate instead of onAttach
    • Decoupled event dispatches, no synchronous, blocking dispatch anymore for either state or events
    • Improved internal logging
    • Updated Koin & Dagger to latest version
    • Updated visibility of a lots of things
    • Optimizations, more inline methods and helpers
    • Code documentation updates
    • Small bugfixes
    Source code(tar.gz)
    Source code(zip)
  • 0.7.0-RELEASE(May 24, 2020)

    Fifteenth release of RainbowCake.

    What's new:

    AndroidX migration

    RainbowCake has been migrated to AndroidX dependencies instead of the support libraries, and no longer supports projects built on the support library. A year and a half after release of AndroidX, this seemed like a reasonable time to make the jump.

    JobViewModel deprecation

    The JobViewModel class that provides the coroutine integration of the framework (via execute) is now deprecated, and will be removed in a couple releases. Its functionality has been pulled up into the RainbowCakeViewModel base class, which now handles view state, events, and coroutine support. Having just one ViewModel base class in the framework should make things less confusing.

    Please replace any usages of JobViewModel with RainbowCakeViewModel (you get IDE support for this, so it should be trivial).

    Channel module deprecation

    The rainbow-cake-channels module has been deprecated, and will be removed in an upcoming release entirely. Coroutine Flows should replace most usages of channels at this point.

    Small stuff

    • Some method visibilities have been restricted in RainbowCakeFragment. These method should not be called anywhere outside of descendants of the Fragment.
    • The observeStateAndEvents testing function now has a variant that can observe queued events, in addition to view state and events.
    • The requireArguments method is now deprecated, as the AndroidX Fragment class includes the method, making the extension unnecessary.
    • Added some new unit tests for SingleShotLiveData.
    • Small project configuration updates, dependency version bumps, etc.
    Source code(tar.gz)
    Source code(zip)
  • 0.6.0-RELEASE(Mar 22, 2020)

    Fourteenth release of RainbowCake.

    What's new:

    Small stuff

    • The reified popUntil extension used to return Unit instead of Boolean, which is what the original method returns to indicate if popping happened. Fixed!
    • ViewModel instances are now set in onAttach instead of onCreate. Because there was no real reason to wait until onCreate to do this.
    • ViewModelProviders has been deprecated, so now ViewModelProvider is being used directly.
    • Version updates (Gradle 6.2.2, AGP 3.6.1, Kotlin 1.3.70).
    • Revamped publishing setup for the libraries.
    Source code(tar.gz)
    Source code(zip)
  • 0.5.0-RELEASE(Nov 1, 2019)

    Thirteenth release of RainbowCake.

    What's new:

    Internal changes to ViewModel scopes

    Instead of implementing CoroutineScope in JobViewModel, it now contains a CoroutineScope instance, which falls in line with many recommendations regarding scopes, as well as the classic advice of favouring composition over inheritance.

    This should be an internal only change, but if you've been abusing the scope interface on ViewModels to launch coroutines on them from Fragments, it's a potentially breaking change.

    First release of rainbow-cake-test

    The architecture now ships with a dedicated testing module, which supports unit testing the architecture (Yay! 🎉). This testing module is in an experimental status, as it itself relies on experimental coroutine testing libraries at this point.

    Presenter testing

    For Presenter tests, you can use the PresenterTest base class, which will replace the IO dispatcher used in Presenters with the test dispatcher, to make it execute immediately:

    ViewModel testing

    For ViewModel tests, you can use the ViewModelTest base class, which will replace the Main dispatcher used by the execute method with the test dispatcher, and also replace the internal LiveData executor with a mock executor that executes everything on a single thread, in a blocking manner.

    The difficult part of testing ViewModels is having to observe their reactions to inputs through various LiveData-based mechanisms - both state changes and events work this way. To make this easy, the rainbow-cake-test library provides the observeStateAndEvents function that lets you assert changes to state, as well as any emitted events:

    vm.observeStateAndEvents { stateObserver, eventsObserver ->
        vm.loadArticle(1L)
        stateObserver.assertObserved(Loading, ArticleLoaded())
        vm.loadArticle(-1L)
        eventsObserver.assertObserved(InvalidIdError)
    }
    

    See the extensions on the MockObserver class for the currently available assertions. Note that you can also add your own assertion extensions on this class, as needed.

    All other tests

    Testing other, lower level components such as Interactors or Data Sources should not require additional support from the architecture. You can use the experimental coroutines test library to wrap such tests in runBlockingTest calls.

    Source code(tar.gz)
    Source code(zip)
  • 0.4.3-RELEASE(Nov 1, 2019)

    Eleventh release of RainbowCake.

    What's new:

    The contentFrame ID that's used in the activity_main layout is now declared in a separate XML file to avoid crashes produced by it not being present if the layout is overridden.

    Source code(tar.gz)
    Source code(zip)
  • 0.4.2-RELEASE(Nov 1, 2019)

    Tenth release of RainbowCake.

    What's new:

    Not much!

    Updates of various dependencies

    • Dagger 2.24 (incremental by default!)
    • Kotlin 1.3.50
    • Coroutines 1.3.0
    • Gradle 5.6
    • Android Gradle plugin 3.5.0

    Navigator documentation updates

    The pop and popUntil methods now have documented return values.

    Source code(tar.gz)
    Source code(zip)
  • 0.4.1-RELEASE(Jun 18, 2019)

    Ninth release of RainbowCake.

    What's new:

    ViewState handling fixes

    Version 0.4.0 caused some unexpected behaviour when reading the viewState immediately after setting it to a new value, as continuously blocking the thread between these two operations didn't allow the set operation to complete, therefore the read showed an outdated state. This should be fixed now.

    The postState method is now even more deprecated than before. You should really only update state via viewState and from the UI thread. Please.

    Source code(tar.gz)
    Source code(zip)
  • 0.4.0-RELEASE(Jun 9, 2019)

    Eight release of RainbowCake.

    What's new:

    Dagger artifact

    Dagger related code has been moved to a separate artifact, which you can include the following way:

    implementation "co.zsmb:rainbow-cake-dagger:$rainbow_cake_version"
    

    Some imports have also been changed to reflect this. Here's a quick table of what you'll need to migrate (should be just a quick search & replace, so no script this time):

    | Original | Replacement | | -------------------------------------------------- | ---------------------------------------------------- | | co.zsmb.rainbowcake.di.RainbowCakeComponent | co.zsmb.rainbowcake.dagger.RainbowCakeComponent | | co.zsmb.rainbowcake.di.RainbowCakeModule | co.zsmb.rainbowcake.dagger.RainbowCakeModule | | co.zsmb.rainbowcake.di.ViewModelKey | co.zsmb.rainbowcake.dagger.ViewModelKey | | co.zsmb.rainbowcake.RainbowCakeApplication | co.zsmb.rainbowcake.dagger.RainbowCakeApplication | | co.zsmb.rainbowcake.base.getViewModelFromFactory | co.zsmb.rainbowcake.dagger.getViewModelFromFactory |

    Koin support

    Why mess around with all that Dagger stuff? Because it's no longer the only game in town. You may now also use Koin 2.0 for your dependency injection needs with RainbowCake.

    1. Include the new Koin artifact:

      implementation "co.zsmb:rainbow-cake-koin:$rainbow_cake_version"
      
    2. Replace Dagger with Koin in your dependencies, here are some recommended artifacts:

      def koin_version = '2.0.1'
      implementation "org.koin:koin-core:$koin_version"
      implementation "org.koin:koin-android:$koin_version"
      implementation "org.koin:koin-android-viewmodel:$koin_version"
      
    3. Set up Koin. You won't need @Inject annotations any more, but you'll have to declare modules and start up your Koin (ideally, in your Application's onCreate method). See the getting started guide for more details.

    Note: You may also use the two DI solutions within the same RainbowCake project simultaneously, for example if you're migrating a project piece by piece. Even shared ViewModels should work!

    Proper license

    The project is now licensed under Apache 2. One more step towards proper open source.

    Android Studio template updates

    The screen template now has an option to generate new screens that are powered by Koin, and both the screen and ListAdapter templates now support AndroidX!

    Source code(tar.gz)
    Source code(zip)
  • 0.3.0-RELEASE(Jun 9, 2019)

    Seventh release of RainbowCake.

    What's new:

    Channels artifact

    Channel related code has now been moved from the core library to the rainbow-cake-channels artifact. It also includes a new feature, converting a LiveData instance to a Channel:

    fun getNews(): ReceiveChannel<List<News>> {
        return newsDao.getNews().toChannel()
    }
    

    This, as with other Channel related API, is experimental (but seems to work alright).

    Changes:

    Navigation extension reorganization

    Argument handling extensions have been moved from the core library to the navigation artifact. Their package names have also been changed to reflect this change. The old extensions are now deprecated.

    Documentation

    The previously used documentation repo is now deprecated. See rainbowcake.dev instead.

    Version updates:

    • Coroutines 1.2.0
    Source code(tar.gz)
    Source code(zip)
  • 0.2.0-RELEASE(Jun 9, 2019)

    Preface

    This release contains many structural changes, and some new features. However, it contains no critical bugfixes. This means that if you don't want to suffer the cost of updating to this version right now, the previous version should keep working for you just fine.

    If you keep using the old version and do find critical issues in it, report the issue, and a bugfix release using the old structure and package names will be provided for you, if necessary.

    Update steps

    Updating your project is going to be just a little bit more effort than usual (should still be about 2 minutes using Studio, really).

    1. Update your Gradle dependencies (as necessary, see the Repackaging section):

      def rainbow_cake_version = '0.2.0'
      implementation "co.zsmb:rainbow-cake-core:$rainbow_cake_version"
      implementation "co.zsmb:rainbow-cake-navigation:$rainbow_cake_version"
      implementation "co.zsmb:rainbow-cake-timber:$rainbow_cake_version"
      
    2. Perform the following search and replace actions in your project - Ctrl + Shift + R:

      | Original | Replacement | | ------------------------| ------------------------ | | hu.autsoft.rainbowcake| co.zsmb.rainbowcake | | BaseViewModel | RainbowCakeViewModel |
      | BaseFragment | RainbowCakeFragment | | BaseActivity | RainbowCakeActivity | | BaseApplication | RainbowCakeApplication | | BaseModule | RainbowCakeModule | | BaseComponent | RainbowCakeComponent |

      There is also a migration script available to perform this search and replace task. This script does its best to safely migrate your project, but be sure to check the changes it makes.

      The script can be invoked in the following way:

      ./rc-migration.sh ~/projects/MyProject
      

      Or for example, on Windows, from a Git bash:

      ./rc-migration.sh /c/projects/MyProject
      
    3. Add configuration to your project as necessary (see the New configuration options section for details).

      Note that previous versions of the library logged internal events to Timber by default, while the new configuration feature disables internal logging by default. This means you won't see stacktraces of uncaught Exceptions caught by JobViewModel anymore. If you wish to re-enable previous behaviour, set the following configuration options (again, details explained below):

      rainbowCake {
          isDebug = BuildConfig.DEBUG
          logger = Loggers.TIMBER
      }
      
    4. Update your Android Studio templates (should be just a simple git pull).

    Read on to see the explanation of why all these steps are required.

    Huge changes

    Repackaging

    The package names of the framework, as well as the artifact group IDs have been changed from hu.autsoft to co.zsmb.

    The framework is also no longer being published as a -SNAPSHOT. These are now regular, stable releases (albeit non-final, because nothing ever is).

    As a modularization effort, the framework is being split up into multiple artifacts - only three, for now. This means including three separate Gradle dependencies in your project, if you actually need the features from all of them.

    The currently available artifacts are:

    Core

    implementation "co.zsmb:rainbow-cake-core:0.2.0"
    

    Contains everything from previous versions, except for the navigation features.

    Navigation addon

    implementation "co.zsmb:rainbow-cake-navigation:0.2.0"
    

    Contains all the navigation features that were part of the base artifact before.

    Timber addon

    implementation "co.zsmb:rainbow-cake-timber:0.2.0"
    

    You only need this artifact if you want the framework to log about its internal events (this is mostly just the exceptions caught by JobViewModel), and you want it to do so using Timber. See details below.

    Base classes renamed

    The base classes BaseViewModel, BaseFragment, and BaseActivity have been renamed to RainbowCakeViewModel, RainbowCakeFragment, and RainbowCakeActivity, respectively.

    This change makes the Base* names available for applications using the framework, so that they may create their own Base* classes that inherit from the framework classes, and include any app-specific extra behaviour there.

    New configuration options

    The framework now has a configuration DSL, which can be invoked in the onCreate method of your Application class.

    Its usage looks like the following:

    override fun onCreate() {
        super.onCreate()
    
        rainbowCake {
            isDebug = false
            logger = Loggers.NONE
            consumeExecuteExceptions = true
        }
    }
    

    The available settings, and their possible values:

    • isDebug: Boolean, false by default.
      • If set to false, it disables all internal logging of the framework, regardless of the setting of logger. May affect other behaviour in the future as well (in debug mode, prod behaviour will definitely not change). Recommended value is BuildConfig.DEBUG.
    • consumeExecuteExceptions: Boolean, true by default (to keep existing behaviour).
      • Determines whether the execute method in JobViewModel should catch and log any uncaught exceptions in coroutines, or let them crash the app. Recommended to be set to false at the very least for debug builds, and should be considered even for production.
    • logger
      • Determines how the framework should log its internal events. Available options by default are NONE (as in no logging) and ANDROID (logs to Logcat via Log.d).
      • If the rainbowcake-timber dependency is included, TIMBER may also be used to log via Timber. Note that this doesn't plant any Trees, you still have to do that yourself.

    Events rework

    Event handling has been significantly reworked under the hood, since they were quite broken in some edge cases.

    1. When using shared ViewModel instances with scopes, only a single one of the attached Fragment would receive the events, chosen randomly.
    2. If a Fragment was inactive (in the background) while its ViewModel posted events, only the last event posted would be delivered when it became active again, due to the nature of LiveData.

    For the first issue: the new events mechanism ensures that all attached Fragments receive each event, so that they may each react to it as appropriate.

    As for the second problem, you may now decide whether an event only makes sense for the Fragment to receive immediately (most events will fall in this category!), or if they should be remembered if the Fragment is not currently active, and delivered later.

    Both of these types of events will still be received in the onEvent method of your RainbowCakeFragment or RainbowCakeActivity, but you have to send them in different ways.

    Active observer only events

    Events that should only be delivered immediately should still implement the OneShotEvent marker interface, and be sent using postEvent, just like before. (One small caveat: this method can now only be called from the UI thread, which you should already have been doing anyway.) If you send one of these events when the Fragment is not active, it will never be delivered.

    Active only events

    99% of the time, this is the behaviour you need for your events, and the type of events you should use.

    Queued events

    Events that matter even if they can't be delivered immediately have to implement the QueuedOneShotEvent marker, and be sent using postQueuedEvent. If the observing Fragment isn't currently active, the event will be queued, and all queued events will be delivered immediately when the Fragment becomes active again.

    Queued events

    Each Fragment instance has its own independent queue of events. Note that Fragments in the background can be destroyed and recreated by the framework, and their queues will be lost in this case - this is a best effort mechanism.

    Events with shared ViewModels

    Events wrapup

    If all of this looks confusing at first, the good news is that you probably don't need all this! You can just keep using events like before, and they'll keep working. They're just much more reliable now.

    MultiDex removed

    The framework used to include the multidex support dependency and initialize MultiDex in BaseApplication by default. Forcing this on applications in this form was a mistake (most notably since apps targeting API 21+ don't need these to use multidex) and has now been removed.

    Any apps targeting API levels below 21 should now perform these steps for themselves, if they require multidex.

    Small changes

    Slicker popUntil

    The popUntil navigation method can now be used with a reified type parameter instead of a KClass parameter. So instead of navigator?.popUntil(HomeFragment::class), you can now navigator?.popUntil<HomeFragment>()!

    ViewModel scoping improvements

    A convenience change in ViewModel scoping: before, only Activity scoped ViewModel instances could have keys. Now you can also key ViewModels scoped to a parent Fragment.

    The syntax for non-keyed ParentScope remains the same as before:

    override fun provideViewModel() = getViewModelFromFactory(scope = ParentFragment)
    

    And the optional key can be provided in the parameter:

    override fun provideViewModel() = getViewModelFromFactory(scope = ParentFragment("some_key"))
    

    Deprecations

    withArgs has been replaced with applyArgs roughly four months ago, therefore using withArgs is now an outright error, and doesn't just produce a suppressible warning. An intention action to perform this migration via Alt + Enter is still available.

    Contexts removed

    The Contexts object that actually contained Dispatcher instances has now been removed, and the library uses Dispatchers directly instead. RCDispatchers.

    Note that the withIOContext method is still available.

    Client code shouldn't really use this object directly, so in theory, this shouldn't break anything.

    Version updates

    • Dagger 2.17.
    • Android Gradle plugin 3.3.2
    Source code(tar.gz)
    Source code(zip)
  • 0.1.2-SNAPSHOT(Jun 9, 2019)

    Fifth snapshot release of RainbowCake.

    What's new:

    New Navigator method to execute pending actions together

    Previously, sequences of Navigator method calls have always executed individually, e.g. take this call:

    navigator?.run {
        popUntil(HomeFragment::class)
        add(SomeFragment())
    }
    

    Here, the current Fragments on top of HomeFragment would have first been removed, HomeFragment appeared for a split second, and then SomeFragment would be added on top.

    You can now prevent this "flashing" behaviour by calling navigator.executePending() after a series of actions, like so:

    navigator?.run {
        popUntil(HomeFragment::class)
        add(SomeFragment())
        executePending()
    }
    

    Version updates:

    • Kotlin 1.3.21
    • Android Gradle plugin 3.3.1
    Source code(tar.gz)
    Source code(zip)
  • 0.1.1-SNAPSHOT(Jun 9, 2019)

    Fourth snapshot release of RainbowCake.

    Update steps:

    Update your dependency version:

    implementation 'hu.autsoft:rainbow-cake:0.1.1-SNAPSHOT'
    

    What's new:

    Even more new argument handling methods

    New methods have been added to support Fragment arguments with Int and Serializable types. Note that the latter of these still isn't a recommendation to pass around large objects as arguments, it's only meant to serve as a way to pass around some small objects like java.util.UUID easier, without having to convert it to a String and back.

    These, again, conform to the naming convention of existing argument handling methods.

    All of these Bundle methods are also now documented and tested according to their documented behaviour. (Their internal implementation has also been unified to simplify them and make them safer.)

    A new Navigator convenience method

    The Navigator interface now has a setStack(Iterable<Fragment>) method to complement the existing setStack(vararg Fragment) method, and avoid having to convert Lists and other iterables to arrays.

    Version updates:

    • Kotlin 1.3.20
    Source code(tar.gz)
    Source code(zip)
  • 0.1.0-SNAPSHOT(Jun 9, 2019)

    Third snapshot release of RainbowCake.

    Update steps:

    Update your dependency version:

    implementation 'hu.autsoft:rainbow-cake:0.1.0-SNAPSHOT'
    

    Optionally, update your screen templates.

    What's new:

    Shared ViewModels between Fragments

    ViewModels by default are scoped to their Fragment, meaning a new instance is created for every new instance of the Fragment (barring configuration changes), and they are cleared when their Fragment is destroyed (as in their lifecycle completely ends).

    There are use cases where it would make sense to share ViewModel instances between Fragments, and the getViewModelFromFactory method now provides an opportunity for this in the form of an optional parameter. ViewModels may now be scoped to the current Activity or to a parent Fragment. For details, see the documentation.

    A demo showcasing a ViewPager where the pages share a ViewModel scoped to their parent Fragment is also available.

    Navigation improvements

    The Navigator provided by the library contains a fade animation between screen changes by default. It's not possible to override this default behaviour.

    You can override it globally, by providing new values for certain properties in your Activity that inherits from NavActivity:

    class MainActivity : SimpleNavActivity() {
    
        override val defaultEnterAnim: Int = R.anim.slide_in_right
        override val defaultExitAnim: Int = R.anim.slide_out_left
        override val defaultPopEnterAnim: Int = R.anim.slide_in_left
        override val defaultPopExitAnim: Int = R.anim.slide_out_right
        
    }
    

    You can also override animations one by one, by using overloads of the add and replace methods:

    navigator?.add(SomeFragment(),
        enterAnim = R.anim.slide_in_right,
        exitAnim = R.anim.slide_out_left,
        popEnterAnim = R.anim.slide_in_left,
        popExitAnim = R.anim.slide_out_right
    )
    

    Note that a simple 0 may be used for any of these values to disable an animation altogether.

    Be sure to check the documentation for all of the properties and methods mentioned above, as they contain much more information.

    New argument handling methods

    New methods have been added to support Fragment arguments with Boolean and Parcelable types. These conform to the naming convention of existing argument handling methods.

    New executeCancellable method

    Previously, any coroutines started by making execute calls in the ViewModel were only cancelled when the ViewModel was cleared. If you need to manage the Job representing coroutines manually instead, you can now do so with the executeCancellable method:

    class MyViewModel : JobViewModel<MyViewState>(Default) {
    
        private var loadingJob: Job? = null
    
        fun loadData() {
            loadingJob?.cancel()
            
            loadingJob = executeCancellable {
                // do something
            }
        }
    
    }
    

    Note that you shouldn't ever return this Job to your Fragment, so do not do this, as the loadData method here has an implicit return type of Job, instead of Unit:

    class MyViewModel : JobViewModel<MyViewState>(Default) {
    
        fun loadData() = executeCancellable {
            // do something
        }
    
    }
    

    Bug fixes and performance improvements (yes, really)

    • Fragment backstack management fixes around the replace operation of the Navigator implementation.
    • The rootJob in JobViewModel is now a SupervisorJob so that it's not cancelled altogether if a child coroutine fails (based on this article's advice).
    • The coroutineContext used by the CoroutineScope in JobViewModel is now only created once at instantiation.
    • Channel observations are now explicitly cleared when a ChannelViewModel is cleared.

    Version updates:

    • Kotlin 1.3.11
    • Coroutines 1.1.0
    • Android Gradle Plugin 3.3.0
    Source code(tar.gz)
    Source code(zip)
  • 0.0.2-SNAPSHOT(Jun 9, 2019)

    Second release of RainbowCake.

    Update steps:

    Update your dependency version:

    implementation 'hu.autsoft:rainbow-cake:0.0.2-SNAPSHOT'
    

    What's new:

    Changes

    withArgs has been renamed to the more fitting applyArgs (as it returns the Fragment it was called on). withArgs is still available, but should be migrated when possible. Automatic migration with intention action is available, should work project wide as well.

    New features

    Added an exhaustive extension property to force when to act as an expression and require all branches to be present, example usage:

    when (viewState: ViewState) {
         is Loading -> { /* Show loading */ }
         is Errored -> { /* Show error */ }
         is Ready -> { /* Show ready */ }
    }.exhaustive
    

    Version updates:

    • Kotlin 1.3
    • Coroutines 1.0.0
    • Android Gradle Plugin 3.2.1
    Source code(tar.gz)
    Source code(zip)
  • 0.0.1-SNAPSHOT(Jun 9, 2019)

🪐 Modern Android development with Hilt, Coroutines, Flow, JetPack(ViewModel) based on MVVM architecture.

Ceres ?? Modern Android development with Hilt, Coroutines, Flow, JetPack(ViewModel) based on MVVM architecture. Download Gradle Add the dependency bel

Teodor G. 21 Jan 11, 2023
📒 NotyKT is a complete 💎Kotlin-stack (Backend + Android) 📱 application built to demonstrate the use of Modern development tools with best practices implementation🦸.

NotyKT ??️ NotyKT is the complete Kotlin-stack note taking ??️ application ?? built to demonstrate a use of Kotlin programming language in server-side

Shreyas Patil 1.4k Jan 4, 2023
Android Clean Architecture in Rorty is a sample project that presents modern, approach to Android application development using Kotlin and latest tech-stack.

Android Clean Architecture in Rorty is a sample project that presents modern, approach to Android application development using Kotlin and latest tech-stack.

Mr.Sanchez 176 Jan 4, 2023
A beautiful Fashion Store like Android App Mock built on Jetpack Compose with compose navigation, hilt, dark theme support and google's app architecture found on uplabs Here

A beautiful Fashion Store like Android App Mock built on Jetpack Compose with compose navigation, hilt, dark theme support and google's app architecture found on uplabs Here

Puncz 87 Nov 30, 2022
Crunch-Mobile - A Food Delivery Mobile App which uses Modern App Architecture Pattern, Firebase And a Simple Restful Api

Crunch-Mobile This is a Food Delivery Mobile App which uses Modern App Architect

Bright Ugwu 1 Jan 1, 2022
A minimal notes application in Jetpack Compose with MVVM architecture. Built with components like DataStore, Coroutines, ViewModel, LiveData, Room, Navigation-Compose, Coil, koin etc.

Paper - A Minimal Notes App A minimal notes application in Jetpack Compose with MVVM architecture. Built with components like DataStore, Coroutines, V

Akshay Sharma 139 Jan 2, 2023
Searchbook - SearchBooks is an app built with Jetpack Compose and architecture of MVVM + MVI

SearchBooks SearchBooks is an app built with Jetpack Compose and architecture of

Jayden 4 Nov 20, 2022
LifecycleMvp 1.2 0.0 Kotlin is MVP architecture implementation with Android Architecture Components and Kotlin language features

MinSDK 14+ Download Gradle Add to project level build.gradle allprojects { repositories { ... maven { url 'https://jitpack.io' }

Robert 20 Nov 9, 2021
A sample application that build with combine use Clean Architecture framework and Github API

The Github Example Introduction This is a sample application that build with combine use Clean Architecture framework and Github API (https://develope

Nguyễn Hùng An 2 Aug 12, 2022
CryptoMovies is a small app that show modern Android developement: with Hilt, Coroutines, Flow, Jetpack and Material Design 3

CryptoMovies is a small app that show modern Android developement: with Hilt, Coroutines, Flow, Jetpack and Material Design 3.

Leonardo Pirro 7 Sep 2, 2022
This app features - Modern Design, MVVM, Hilt, Room, StateFlow, Jetpack Compose

MyMusic is a beautiful app showing how to build modern looking interfaces using Jetpack Compose, recommended libraries, best practices and architectur

Ibrahim 54 Dec 27, 2022
Skeleton project for show the architecture of Android project using MVVM, Clean Architecture and Kotlin coroutine Flow

ClearScoreDemo Skeleton project for showing the architecture of Android project using MVVM, Clean architecture and Kotlin coroutine Flow App Architect

Plabon Modak 1 Mar 6, 2022
FaceTimeClone app that implements Coroutines , mvvm architecture , clean architecture , navigation component , hilt , etc.... using kotlin language

This repository contains a FaceTimeClone app that implements Coroutines , mvvm architecture , clean architecture , navigation component , hilt , etc.... using kotlin language

null 17 Dec 13, 2022
A personal project made using Jetpack Compose, Clean-MVVM architecture and other jetpack libraries

A basic CRUD for recording your personal credit and debit transactions. Made using Jetpack Compose, Clean-MVVM and other Jetpack libraries, incorporated with Unit Tests.

Shoaib Ahmed 3 Dec 6, 2022
Kotlin Multiplatform lifecycle-aware business logic components (aka BLoCs) with routing functionality and pluggable UI (Jetpack Compose, SwiftUI, JS React, etc.), inspired by Badoos RIBs fork of the Uber RIBs framework

Decompose Please see the project website for documentation and APIs. Decompose is a Kotlin Multiplatform library for breaking down your code into life

Arkadii Ivanov 819 Dec 29, 2022
Kotlin Multiplatform Mobile + Mobile Declarative UI Framework (Jetpack Compose and SwiftUI)

Kotlin Multiplatform Mobile + Mobile Declarative UI Framework (Jetpack Compose and SwiftUI)

Kotchaphan Muangsan 3 Nov 15, 2022
A fork of our clean architecture boilerplate, this time using the Android Architecture Components

Android Clean Architecture Components Boilerplate Note: This is a fork of our original Clean Architecture Boilerplate, except in this repo we have swi

Buffer 1.3k Jan 3, 2023
Kotlin-based modern RecyclerView rendering weapon

Read this in other languages: 中文, English, Changelog Yasha Item introduction: Kotlin-based modern RecyclerView rendering weapon Item Features: No Adap

Season 514 Dec 21, 2022