MVU for Kotlin Multiplatform

Overview

Oolong

Build Status Maven Central Sonatype Nexus (Snapshots) License Slack chat

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

  • Model - a type to represent the program state

  • View - a function to map the state to view properties

  • Update - a function to update the state

By applying this simple pattern you can create composable, testable programs that can run on any platform. Oolong enables a common codebase for all platforms by using a render function which is implemented by each frontend.

Documentation

Get started with Oolong by reading the official guide.

Download

dependencies {
    implementation("org.oolong-kt:oolong:2.1.0")
}
Issues
  • Remove ': Any' bounds in generic types

    Remove ': Any' bounds in generic types

    Is your feature request related to a problem? Please describe.

    In Kotlin, we have the chance to have a compiler that makes safe to deal with nullability. Therefore, unlike in Java where null is very dangerous value that must be avoided at all cost, in Kotlin null is a valid and useful value to represent the absence of something. Yet the compiler will help us to make sure we treat it safely. On other words, the null of Kotlin is the equiavlent of Nothing in Elm: Useful and safe to use.

    Yet, many generic functions in oolong force the generic argument to be not null. Example: fun <A : Any, B : Any> map(effect: Effect<A>, f: (A) -> B): Effect<B>

    This adds an unecessary constraint on something that could have been safely nullable.

    It is also viral and will cause any helpers built on top to also add that generic boundary. Example:

    // Doesn't compile unless adding `: Any` bound to `T` and `R`
    fun <T, R> Effect<T>.map(transform: (T) -> R): Effect<R> = map(this, transform)
    

    By the way, as a side question: why not making map an extension function on Effect? It would be easier to discover and natural, since in Kotlin we're use to map over collections, sequences, flows, etc.

    I aggree, that it is probably very uncomon to use nullable types for model or messages. But, in a world of safe nullability, I don't see why it should completly be prevented by the framework. In elm for instance, one can freely use Maybe for model and messages.

    Describe the solution you'd like Remove uncessary : Any bound on generic arguments.

    Describe alternatives you've considered For a usage point of view, I can use Optional when I want deal with nullability in model or message. But it is not very idiomatic, we have nullability directly in the Kotlin type system.

    opened by jcornaz 7
  • [WIP] Add navigation component.

    [WIP] Add navigation component.

    Adds an abstraction over navigation which handles route changing, component delegation, and state caching. Example usage in the README.

    Let's discuss! @oolong-kt/developers

    opened by pardom 7
  • Effect breaks Android Build

    Effect breaks Android Build

    Describe the bug Android application fails to build when "effect" is used

    To Reproduce I've made the following repo to demonstrate the issue - Oolong Test

    • "master" fails to build
    • "working" builds successfully because the "effect" code in Store.kt is commented out

    Additional context I'm using oolong-jvm because this is just going to run on Android and doesn't require using Kotlin Multi-Platform. I am unsure if that is related because everything else appears to work as expected

    opened by dladukedev 5
  • Support for js platform

    Support for js platform

    Hi, I wanted to test Oolong on JavaScript.

    I added the target and to implement runBlocking for the tests I removed the returned type. I also added `@JsName()' for test methods, the js platform doesn't support spaces in method names.

    I don't know if it is the good way to do it but I was able to play with Oolong :).

    I'm using Bulma and bulma-kotlin: Capture d’écran 2020-06-01 à 08 02 41

    opened by jeancharles-roger 5
  • Documentation site alternatives

    Documentation site alternatives

    Currently the project site is generated with GitBook. Let's investigate alternatives that allow more flexibility.

    Alternatives:

    • https://docusaurus.io
    • https://www.docz.site
    • https://docsify.js.org

    Please leave your suggestions below.

    opened by pardom 3
  • Bump mixin-deep from 1.3.1 to 1.3.2 in /oolong

    Bump mixin-deep from 1.3.1 to 1.3.2 in /oolong

    Bumps mixin-deep from 1.3.1 to 1.3.2.

    Commits
    Maintainer changes

    This version was pushed to npm by doowb, a new releaser for mixin-deep since your current version.


    Dependabot compatibility score

    Dependabot will resolve any conflicts with this PR as long as you don't alter it yourself. You can also trigger a rebase manually by commenting @dependabot rebase.


    Dependabot commands and options

    You can trigger Dependabot actions by commenting on this PR:

    • @dependabot rebase will rebase this PR
    • @dependabot recreate will recreate this PR, overwriting any edits that have been made to it
    • @dependabot merge will merge this PR after your CI passes on it
    • @dependabot squash and merge will squash and merge this PR after your CI passes on it
    • @dependabot cancel merge will cancel a previously requested merge and block automerging
    • @dependabot reopen will reopen this PR if it is closed
    • @dependabot ignore this [patch|minor|major] version will close this PR and stop Dependabot creating any more for this minor/major version (unless you reopen the PR or upgrade to it yourself)
    • @dependabot ignore this dependency will close this PR and stop Dependabot creating any more for this dependency (unless you reopen the PR or upgrade to it yourself)
    • @dependabot use these labels will set the current labels as the default for future PRs for this repo and language
    • @dependabot use these reviewers will set the current reviewers as the default for future PRs for this repo and language
    • @dependabot use these assignees will set the current assignees as the default for future PRs for this repo and language
    • @dependabot use this milestone will set the current milestone as the default for future PRs for this repo and language

    You can disable automated security fix PRs for this repo from the Security Alerts page.

    dependencies 
    opened by dependabot[bot] 1
  • Consider alternative type and syntax for Next

    Consider alternative type and syntax for Next

    Is your feature request related to a problem? Please describe.

    I brought this up in Slack but would like to continue the conversation here to open it to other folks that would like to participate.

    The Next type is currently typealias'd to a Pair<Model, Effect<Msg>>. The usage of Pair comes from the desire to use the built in tuple types that the Kotlin language offers via generics and data class (Pair and Triple).

    Unfortunately, unlike Elm's tuples which have a nice syntax for creation with parentheses (e.g. (1, 2, "a", "b")), Kotlin relies on constructors for its data classes with no short-hand replacement. To work around that, the language offers an infix function to to aid in Pair's creation.

    This leads to idiomatic Oolong code in the Update/Init functions looking somewhat like this:

    val update: Update<Msg, Model> = { msg, model ->
      model to none()
    }
    

    This syntax can be a little hard to decipher, especially as a novice reader of the code. It requires some knowledge of the Oolong type definitions to understand why this syntax is being used. The keyword to reads more like a mapping than a union (to me, at least), and I think there's an opportunity here to be more explicit with types and have a more readable syntax.

    Describe the solution you'd like

    Give an explicit type to Next and provide a more ergonomic infix operator for its creation to lean in to the Kotlin language features and tools.

    Two steps to this solution:

    1. Redefine Next to data class Next<Model, Msg>(val model: Model, val effect: Effect<Msg>)
    2. Add a new infix function and: infix fun <Model, Msg> Model.and(effect: Effect<Msg>) = Next(this, effect)

    This changes our above example to read like this:

    val update: Update<Msg, Model> = { msg, model ->
        model and none()
    }
    

    Advantages:

    • the code reads as "return the updated model and these effects", which I think is a very nice improvement
    • extensions on Next will not pollute the Pair type in case there were extensions that were Oolong-specific
    • deconstructing the Next type will have named fields instead of first and second

    Cons:

    • lose access to any extensions on Pair
    • potentially breaking change to the type system

    note: I'd expect that we provide a to infix for compatibility initially, but have that deprecated

    @Deprecated(
        message = "prefer `and` operator, `to` will be removed in a future update",
        replaceWith = ReplaceWith("this.and(effect)")
    )
    infix fun <Model, Msg> Model.to(effect: Effect<Msg>) = Next(this, effect)
    

    The problem that this doesn't solve is the syntax for the optional Effect case, I think ideally you'd be able to have a syntax that supports returning just the model or returning the model and effects, e.g.

    val update: Update<Msg, Model> = { msg, model ->
        model
    }
    

    Not sure what might be available for that solution (outside of a compiler plugin that allows for a more ergonomic tuple creation syntax)

    Describe alternatives you've considered

    • with would be an acceptable alternative to and but it is already a reserved word in the Kotlin language
    • I opted against an infix operator (e.g. *) since that is not a standard Kotlin language feature and would likely be considered a bit obtuse in the way that to is today with the added negative of being less discoverable

    Additional context

    none

    opened by sddamico 1
  • Subscription support

    Subscription support

    Is your feature request related to a problem? Please describe.

    I want to subscribe to some events, that might be external to my system. Example:

    • Get an event every X seconds
    • Subscribe to events from a websocket

    Describe the solution you'd like

    Since the present library is inspired from elm, I'd find natural to imitate elm's subscription system (in Browser.element).

    That could be translated in Kotlin like this:

    data class Model(val ticks: Int = 0)
    sealed class Msg {
      object Tick : Msg()
    }
    
    val subscribe = Subscribe<Model, Msg> { model ->
      every(10.seconds).map { Msg.Tick }
    }
    
    // ...
    
    val dispose = Oolong.runtime(
        init,
        update,
        suscribe,
        view,
        render
    )
    

    Of course this is only a draft example, and any variation of the API would do.

    Describe alternatives you've considered

    I think I could start a coroutine from a disposable effect that will call dispatch to fire the messages.

    That would however require me to keep an instance of the dispose in my model, so that I can cancel it later.

    opened by jcornaz 2
  • Unit Test Documentation

    Unit Test Documentation

    Is there any way to unit test update ? If there's a way, is there any documentation ? Thank you

    opened by WendyYanto 3
  • Flipper Plugin

    Flipper Plugin

    Related links:

    • https://fbflipper.com/
    • https://fbflipper.com/docs/extending/index.html

    Please follow up with suggestions and ideas if you have them, @oolong-kt/developers.

    good first issue 
    opened by pardom 0
  • Render documentation

    Render documentation

    opened by pardom 1
Releases(v2.1.0)
  • v2.1.0(Sep 30, 2020)

    Added

    • Add a runtime overload which combines view and render.
    • Update Kotlin to 1.4.10

    Changed

    • CoroutineDispatcher replaced with CoroutineContext in runtime builder.
    • Dispatch deprecated in favor of Job.
    • oolong.Oolong.runtime deprecated in favor of oolong.runtime.
    • disposableEffect deprecated.
    • Allow incoming types to be nullable.
    • Deprecate Next, Init, Update, View, and Render in preference of underlying types.
    Source code(tar.gz)
    Source code(zip)
  • v2.0.7(Sep 30, 2020)

  • v2.0.6(Jun 30, 2020)

  • v2.0.5(Jun 26, 2020)

  • v2.0.4(Jun 26, 2020)

  • v2.0.3(Jun 26, 2020)

    Added

    • Update Kotlin to 1.3.72
    • Update Kotlin Coroutines to 1.3.5

    Removed

    • Deprecated coroutine scope and context arguments in runtime creator function.
    • Remove deprecated runtime creator function.
    Source code(tar.gz)
    Source code(zip)
  • v2.0.2(Jun 26, 2020)

  • v2.0.1(Jun 26, 2020)

  • v2.0.0(Jun 26, 2020)

  • v1.0.0(Jun 26, 2020)

Kotlin Multiplatform Router for Android and iOS

A powerful Kotlin Multiplatform Router for Android and iOS Support I am happy to help you with any problem on gitter Feel free to open any new issue!

Sebastian Sellmair 336 Jun 24, 2021
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.

Arkadii Ivanov 614 Aug 1, 2021
Model-View-ViewModel architecture components for mobile (android & ios) Kotlin Multiplatform development

Mobile Kotlin Model-View-ViewModel architecture components This is a Kotlin Multiplatform library that provides architecture components of Model-View-

IceRock Development 385 Jul 29, 2021
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

Redux-Kotlin 186 Jul 27, 2021
BaseDemo 是Android MVVM + Retrofit + OkHttp + Coroutine 协程 + 组件化架构的Android应用开发规范化架构

BaseDemo 是Android MVVM + Retrofit + OkHttp + Coroutine 协程 + 组件化架构的Android应用开发规范化架构,通过不断的升级迭代,目前主要分为两个版本,分别为分支 MVVM+Databinding 组件化版本,分支MVVM+Databinding+Single 单体版本。旨在帮助您快速构建属于自己的APP项目架构,做到快速响应上手,另外再长期的实践经验中汇总了大量的使用工具类,主要放在了项目 `lib_common` 组件中,以供大家参考使用。具体使用请开发者工具自己项目需求决定选择如何使用。

Huan Zhou 35 Jul 22, 2021
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

null 475 Jul 29, 2021
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

Areg Petrosyan 5 May 24, 2021
Moxy is MVP library for Android

Moxy This Moxy repository is deprecated and no longer supported. Please migrate to the actual version of the Moxy framework at Moxy communuty repo. De

Arello Mobile 1.6k Jul 25, 2021
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...

Isaias Cuvula 6 Jul 7, 2021
A full-featured framework that allows building android applications following the principles of Clean Architecture.

EasyMVP A powerful, and very simple MVP library with annotation processing and bytecode weaving. EasyMVP eliminates the boilerplate code for dealing w

null 1.3k Jul 21, 2021