The most complete and powerful data-binding library and persistence infra for Kotlin 1.3, Android & Splitties Views DSL, JavaFX & TornadoFX, JSON, JDBC & SQLite, SharedPreferences.

Overview

Build Status Extremely lightweight Hits-of-Code Kotlin 1.3 Awesome Kotlin Channel at Kotlin Slack Telegram chat

Lychee (ex. reactive-properties)

Lychee is a library to rule all the data.

ToC

Approach to declaring data

Typically, we declare data using classes:

/*data*/ class Player(
    val name: String,
    val surname: String,
    var score: Int,
)

But there are some mistakes in the example above:

  • there aren't any convenient ways to handle arbitrary class properties:
    • reflection does not play well with ProGuard/R8 and Graal,
    • kapt is slow and does not play well with separate compilation,
    • both break encapsulation and expose field names (or values of annotations) as serialization interface,
    • none of them knows precisely how to serialize values, they just try to guess according to field types,
    • there are no standard annotations: every JSON library has its own annotations (Gson: @SerializedName and @TypeAdapter), every ORM, ActiveRecord, or another database-related thing has its own, too,
    • TypeAdapter concept is cursed: every library tries to support all standard types (how often do you need to store AtomicIntegerArray? Gson has built-in support for it, this is crazy!), and tree shakers (a.k.a. dead code eliminators) cannot figure out which ones are actually used (this requires deep understanding of reflection API and Map<Type, TypeAdapter> machinery, 10+ years of ProGuard were not enough to get this deep);
  • along with interface (property names and types), this also declares implementation details (backing fields). Thus, you're getting only in-memory representation. (To workaround the issue, Realm, for example, extends your classes so getters&setters are overridden while fields are unused, and rewrites your bare field accesses, if any, to use getters&setters.) Theoretically, this can be fixed by extracting interface:
interface Player {
    val name: String
    val surname: String
    var score: Int
 // fun copy()? can we also ask to implement equals()? no.
}
data class MemoryPlayer(override val …) : Player
class JsonPlayer(private val json: JsonObject) : Player {
    override val name: String get() = json.getString("name")
    …
}
class SqlPlayer(private val connection: Connection) : Player {
    override val name: String get() = connection.createStatement()…
}

but implementations are 146% boilerplate;

  • no mutability control. var score is mutable but not observable;
  • hashCode, equals, and toString contain generated bytecode fully consisting of boilerplate;
  • data class copy not only consists of boilerplate but also becomes binary incompatible after every primary constructor change;
  • data class componentNs are pure evil in 99% cases: destructuring is good with positional things like Pair or Triple but not with named properties.

:persistence module provides the solution. Interface is declared by inheriting Schema:

object Player : Schema<Player>() {
    val Name = "name" let string
    val Surname = "surname" let string
    val Score = "score".mut(i32, default = 0)
}

Here, Player, string, and i32 (not int because it's Java keyword) are all subtypes of DataType. Thus, they declare how to store data both in-memory and on wire. Name, Surname, and Score are field definitions, two immutable and one mutable, based on typed key pattern.

Implementations are subtypes of Struct<SCHEMA>, so they implement storage machinery while staying decoupled from data schema:

val player: StructSnapshot<Player> = Player { p ->
    p[Name] = "John"
    p[Surname] = "Galt"
    // Score gets its default value.
}

StructSnapshot is immutable (and very cheap: it is an Array, not a HashMap) implementation. It can only be read from:

assertEquals(0, player[Player.Score])

Here, Player {} is SCHEMA.invoke(build: SCHEMA.() -> Unit) function which tries to mimic struct literal; p is StructBuilder<SCHEMA>—a fully mutable temporary object. Structs implement hashCode, equals, toString, and copy of this kind: player.copy { it[Score] = 9000 }. It creates new StructBuilder and passes it to the function you provide. (Similar thing is called newBuilder in OkHttp, and buildUpon in android.net.Uri.)

There's also a good practice to implement a constructor function which gives less chance of forgetting to specify required field values:

fun Player(name: String, surname: String) = Player { p ->
    p[Name] = name
    p[Surname] = surname
}

Properties

Properties (subjects, observables) inspired by JavaFX and Vue.js MVVM-like approach are available in :properties module. A Property provides functionality similar to BehaviorSubject in RxJava, or Property in JavaFX, or LiveData in Android Arch.

  • Simple and easy-to-use
  • Lightweight: persistence + properties + android-bindings define around 1500 methods including easy-to-shrink inline funs and inline classes
  • zero reflection (the only use of kotlin.reflect is required if you delegate your Kotlin property to a Lychee Property and eliminated by Kotlin 1.3.70+ compiler)
  • Extensible: not confined to Android, JavaFX or whatever (want MPP? File an issue with sample use-cases)
  • Single-threaded and concurrent (lock-free) implementations
  • Ready to use MVVM/data-binding for Android (and some bindings to JavaFX)
  • Sweet with View DSLs like Splitties and TornadoFX
  • Depends only on Kotlin-stdlib and Kotlin-MPP Collection utils for overheadless EnumSets
  • Presentation about properties: initial problem statement and some explanations

With :persistence + :properties, it's also possible to observe mutable fields:

val observablePlayer = ObservableStruct(player)
val scoreProp: Property<Int> = observablePlayer prop Player.Score
someTextView.bindTextTo(scoreProp.map(CharSequencez.ValueOf)) // bind to UI, for example

// both mutate the same text in-memory int value and a text field:
scoreProp.value = 10
observablePlayer[Player.Score] = 20

Other data-binding libraries

to explain why I've rolled my own:

Properties sample

val prop: MutableProperty<Int> = propertyOf(1)
val mapped: Property<Int> = prop.map { 10 * it }
assertEquals(10, mapped.value)

prop.value = 5
assertEquals(50, mapped.value)


val tru = propertyOf(true)
val fals = !tru // operator overloading
assertEquals(false, fals.value)

Sample usage in GUI application

Android layout (Splitties Views DSL):

setContentView(verticalLayout {
    padding = dip(16)

    addView(editText {
        id = 1 // let view save its state, focus, etc
        hint = "Email"
        bindTextBidirectionally(vm.emailProp)
        bindErrorMessageTo(vm.emailValidProp.map { if (it) null else "E-mail is invalid" })
    })

    addView(editText {
        id = 2
        hint = "Name"
        bindTextBidirectionally(vm.nameProp)
    })

    addView(editText {
        id = 3
        hint = "Surname"
        bindTextBidirectionally(vm.surnameProp)
    })

    addView(button {
        bindEnabledTo(vm.buttonEnabledProp)
        bindTextTo(vm.buttonEnabledProp.map { if (it) "Save changes" else "Nothing changed" })
        setWhenClicked(vm.buttonClickedProp)
        // ^ set flag on action
    })

}.wrapInScrollView())

JavaFX layout (using JFoenix):

children.add(JFXTextField().apply {
    promptText = "Email"
    textProperty().bindBidirectionally(vm.emailProp)
})

children.add(Label().apply {
    text = "E-mail is invalid"
    bindVisibilityHardlyTo(!vm.emailValidProp)
})

children.add(JFXTextField().apply {
    promptText = "Name"
    textProperty().bindBidirectionally(vm.nameProp)
})

children.add(JFXTextField().apply {
    promptText = "Surname"
    textProperty().bindBidirectionally(vm.surnameProp)
})

children.add(JFXButton("Press me, hey, you!").apply {
    disableProperty().bindTo(!vm.buttonEnabledProp)
    textProperty().bindTo(vm.buttonTextProp)
    setOnAction { vm.buttonClickedProp.set() }
})

Common ViewModel:

class MainVm(
        // user is backed by arbitrary data source: in-memory, database, SharedPreferences, …
        private val user: TransactionalPropertyStruct<User>
) : PersistableProperties {

    // user input

    // clone user into memory
    private val editableUser = ObservableStruct(user, false)

    // expose properties for View
    val emailProp get() = editableUser prop User.Email
    val nameProp get() = editableUser prop User.Name
    val surnameProp get() = editableUser prop User.Surname

    // handle actions

    val buttonClickedProp = propertyOf(false).clearEachAnd {
        // reset flag and perform action—patch user with values from memory
        user.transaction { t ->
            t.setFrom(editableUser, User.Email + User.Name + User.Surname)
        }
    }

    // preserve/restore state of this ViewModel (for Android)
    override fun saveOrRestore(io: PropertyIo) {
        /*
        When saving state, property values are written to io which is PropertyOutput.
        When restoring, property values are assigned from io which is PropertyInput.
        
        Infix function calls:
        */
        io x emailProp
        io x nameProp
        io x surnameProp
    }

    // some feedback for user actions

    val emailValidProp = emailProp.map { it.contains("@") }

    // compare snapshots
    private val usersDifferProp = user.snapshots().mapWith(editableUser.snapshots(), Objectz.NotEqual)

    val buttonEnabledProp = usersDifferProp and emailValidProp

}

Persistence and Android

Things available in :android-bindings:

// this will copy data from player into the given SharedPreferences instance
val storedPlayer = SharedPreferencesStruct(player, getSharedPreferences(…))
val scoreProp = storedPlayer prop Player.Score
val score = storedPlayer[Player.Score]
// and this is different:
storedPlayer.transaction { p ->
    p[Score] = 100500
}
  • implementing PersistableProperties helps you to save or restore the state of a ViewModel to ByteArray/Parcel by implementing a single method, without declaring symmetrical, bolierplate, and error-prone writeToParcel and createFromParcel methods and without having Android dependencies:
class SomeViewModel : PersistableProperties {
    …
    override fun saveOrRestore(io: PropertyIo) {    
        io x prop1
        io x prop2
        io x prop3
    }
}

see full save & restore example.

  • JSON support built on top of android.util.JsonReader/Writer:
// reading
val jsonPlayer = """{"name":"Hank","surname":"Rearden"}"""
        .reader() // StringReader
        .json() // JsonReader
        .tokens() // TokenStream
        .readAs(Player) // StructSnapshot<Player>

val jsonPlayers = """[ {"name":"Hank","surname":"Rearden"}, ... ]"""
        .reader().json().tokens().readListOf(Player)

// writing
type.tokensFrom(value).writeTo(JsonWriter(…))

TokenStream abstraction is an iterator over tokens and it's helpful for changing schema of provided data (instead of using “mappers”), see sample transform usage.

SQL

:sql module provides Table, a wrapper over Schema:

// trivial table. Primary key column is not mentioned within Schema
val Players = tableOf(Player, "players", "_id", i64)

With Session (implementations: JdbcSession, Android-specific SqliteSession), you're getting

  • SQL templates:
val selectNameEmailBySmth = session.query(
    "SELECT a.name, b.email FROM anywhere a JOIN anything b WHERE smth = ?",
    /* argument */ string,
    // return a list of positionally bound string-to-string tuples:
    structs(projection(string, string), BindBy.Position)
) // (String) -> List<Struct<Tuple<String, …, String, …>>>

val updateNameByEmail = session.mutate(
    "UPDATE users SET name = ? WHERE email = ?",
    string, string,
    execute()
) // Transaction.(String, String) -> Unit
  • Triggers:
val listener = session.observe(
    UserTable to TriggerEvent.INSERT,
    UserTable to TriggerEvent.DELETE,
) { report ->
    val userChanges = report.of(UserTable)
    println("+" + userChanges.inserted.size)
    println("-" + userChanges.removed.size)
}
…
listener.close() // unsubscribe

HTTP

HTTP is unfortunately the most popular application layer protocol. Its abilities are not restricted to passing binary or JSON bodies: there are headers, query parameters, form fields, multipart, and more.

With :http, you can declare endpoints using some of DataTypes from :persistence:

val user = GET("/user/{role}/",
    Header("X-Token"), Path("role"), Query("id", uuid),
    Response<String>())

This gives you several features:

  • HTTP client templates: val getUser = okHttpClient.template(baseUrl, user, deferred(::parseUser)) => (token: String, role: String, id: UUID) -> Deferred<String>. Here you define parseUser yourself. Thus, you are free to handle responses as you want: throw exception for non-2xx responses, or return Either<HttpException, ResponseEntity>, or ignore response code at all and just parse response body;
  • server-side type-safe routing: undertowRoutingHandler.add(user, ::respond, ::respondBadRequest) { token, role, id -> "response" };
  • link generation: if endpoint declaration uses GET method and does not contain headers, it is possible to build URL:
GET("/user/{role}/", Path("role"), Query("id", uuid))
    .url(baseUrl, "admin", UUID.randomUUID())
    // => //user/admin/?id=0b46b157-84b9-474c-83bb-76c2ddf58e75

Hey, have you just reinvented Retrofit?

Well, yes, but actually no. Retrofit

  • works only on client-side,
  • requires method return types (Call, Observable, Deferred) to be tied to async framework,
  • promotes Service-style interfaces.

Lychee-HTTP, on the other side,

  • allows Endpoints to be both invoked from client-side and implemented at server-side,
  • decouples async wrapper from return value,
  • httpClient.template(endpoint) returns a function, server-side endpoint handler is a funcion, thus, no Services/Controllers.

FAQ

What's the purpose of this library?

The main purpose is MVVM/DataBinding, especially in Android where preserving ViewModel state may be quirky. ViewModel/ViewState can be declared as a set of mappings, where the values of some properties depend on some other ones.

Why not use an existing solution?

  • javafx.beans.property.Property

    It was the main source of inspiration. But the class hierarchy is too deep and wide, looks like a complicated solution for a simple problem. Has no support for multithreading. Looks like unsubscription won't take effect during notification.

  • android.util.Property

    A very trivial thing for animation. Has ReflectiveProperty subclass which is close to JavaFX concept (every property is a Property) not observable and reflective (and thus sad).

  • io.reactivex.BehaviorSubject

    Has no read-only interface. You can either expose an Observable (without get) or a BehaviorSubject (with get and set). Has no single-threaded version. Part of non-modular, poorly designed RxJava.

  • LiveData

    Confined to Handler/Looper which limits usage to Android only and complicates testing. It's also an abstract class, thus customization is limited.

  • XML data-binding

    Uses XML layouts (inflexible) and code generation (sucks in many ways, still breaks regularly). Ties layouts to hard-coded Java classes thus killing XML reusability.

Why version is 0.0.x?

1.x versions mean stable and compatible API/ABI. Lychee interface is not volatile, but is a subject to change, move, rename. This means that it can be used in production apps (migrations are easy), but libraries should update as fast as Lychee does. If your library does (or going to) depend on Lychee, file an issue: I will take into account which APIs do you use and maybe add a link to your library.

0.1.0 version is to be released after adding mutational and linearization tests, 1.0.0 is planned after dropping workarounds for KT-24981: @JvmSynthetic for classes, KT-24067: type checking and casting of multi-arity function objects, and when inline classes come more stable (e. g. KT-31431 JVM backend failure on inline functions of inline classes, KT-33224: @JvmName for function with inline class parameter ).

Where and how should I dispose subscriptions?

When the property is not being observed, it not observes its source and thus not being retained by it. Consider the following code:

val someGlobalProp = propertyOf(100)
val mappedProp = someGlobalProp.map { it * 10 }
// mappedProp has no listeners and thus not observes someGlobalProp

println(mappedProp.value) // value calculated on demand

mappedProp.addChangeListener { ... }
// mappedProp now listens for someGlobalProp changes
// and not eligble for GC until someGlobalProp is not

someGlobalProp.value = 1
// mappedProp value calculated due to original value change
// mappedProp's listener was notified

All Android bindings are based on bindViewTo which creates a Binding. It is a flyweight observing View attached state, Activity started state, and Property changes. When view gets attached to window, Binding is getting subscribed to Activity lifecycle via Lifecycle-Watcher; when Activity is started, Binding listens for data source. When Activity gets stopped or View gets detached, binding unsubscribes and becomes eligible for garbage collection along with the whole View hierarchy.

How much black magic do you use under the hood?

Some operator overloading, some inline classes, several compilation error suppressions, tons of unchecked casts. No reflection, zero annotation processing. If you encounter any problems, they most likely will be related to type inference or Java interop.

Is there anything similar to RxJava's Single?

Nope. Java since v. 1.8 contains CompletableFuture for async computations. It also was backported to Java 6.5 Android a long time ago. Note that it is distributed under “GPL v. 2.0 with classpath exception” which is not as restrictive as GPL itself.

You can mutate concurrent properties from background threads (e.g. in the end of async computations), triggering UI state change as needed and without any callbacks.

ProGuard rules for Android?

Here you are.

Adding to a project

Download Properties

Download Persistence

Download Extended Persistence

Download Android Bindings

// `allprojects` section of top-level build.gradle || root of module-level build.gradle
repositories {
    ...
    maven { url 'https://dl.bintray.com/miha-x64/maven' }
}

// module-level build.gradle
dependencies {
    def lychee = '0.0.16'
//  val lychee = "0.0.16"
    implementation("net.aquadc.properties:properties:$lychee") // observables for both JVM and Android
    implementation("net.aquadc.properties:persistence:$lychee") // persistence for JVM and Android
    implementation("net.aquadc.properties:extended-persistence:$lychee") // partial structs, tuples, either, unsigned, primitive[], token transforms
    implementation("net.aquadc.properties:android-bindings:$lychee") // AAR for Android(x): View bindings, Parcel, SharedPreferences as Struct, Handler as Executor
    implementation("net.aquadc.properties:android-json:$lychee") // android.util.JsonReader as TokenStream
    implementation("net.aquadc.properties:android-json-on-jvm:$lychee") // implements android.util.JsonReader for server and desktop, use with android-json outside of Android 
    implementation("net.aquadc.properties:sql:$lychee") // observable SQL and SQL templates
    implementation("net.aquadc.properties:http:$lychee") // RPC over HTTP: client-side HTTP templates, server-side routing, type-safe link generator
}
Comments
  • Migrate from jCenter to mavenCentral

    Migrate from jCenter to mavenCentral

    Not available on bintray. JCenter deprecation and end of service: https://developer.android.com/studio/build/jcenter-migration

    Cannot sync project because of 403 forbidden error

    opened by mfperminov 2
  • SQL (Android-SQLite, JDBC) support

    SQL (Android-SQLite, JDBC) support

    • [x] Prototype, SQLite, observability
    • [x] SQL templates
    • [x] Create trigger-based observability which will work for external INSERT/UPDATE/DELETE statements and for multi-node applications with a shared database
    • [x] Support mutating SQL templates
    • [x] deprecate DAOs and Records
    opened by Miha-x64 2
  • Write DSL for saving/restoring mutable properties' values

    Write DSL for saving/restoring mutable properties' values

    e. g.

    val smth: Whatever.() -> Unit = {
        +someStringProp
        +someIntProp
        +someParcelableProp // (?)
    }
    
    toParcel(smth)
    fromParcel(smth)
    toByteArray(smth)
    toByteArray(smth)
    

    Persistent ViewModels may be very useful.

    enhancement 
    opened by Miha-x64 2
  • Union types, ADTs (Kotlin: sealed classes; Rust & Swift: enums)

    Union types, ADTs (Kotlin: sealed classes; Rust & Swift: enums)

    With existing type system, union can be expressed as a Partial with only one field set. The only drawback is that serialization format may require unions to be handled differently, see https://serde.rs/enum-representations.html

    opened by Miha-x64 1
  • Workaround Atomic* crashes on Samsungs with Android 5.0.x

    Workaround Atomic* crashes on Samsungs with Android 5.0.x

    @see https://github.com/ReactiveX/RxJava/pull/3979/files of course, this fix is inappropriate. Gonna pessimize perf only on those problematic devices.

    opened by Miha-x64 1
  • More extensible Persistence

    More extensible Persistence

    Remove List, Enum, EnumSet support from PropertyIo; introduce interface Converter<T> { fun read(DataInput): T; fun write(DataInput, T) }; add List, Enum, EnumSet, ByteString support as extensions.

    Maybe move this to another artifact.

    opened by Miha-x64 1
  • Schedulers

    Schedulers

    Add a way to subscribe on a certain thread. For mapper properties, add a way to compute on another thread. Along with #15, this may work like

    // list: DiffProperty<List<Item>, SomeDiffMeta>
    list.mapWithDiff(
        computeOn = someBackgroundScheduler,
        notifyOn = mainThread
    ) { items, meta ->
        items to DiffUtil.doStuff(items, meta)
    }
    
    enhancement 
    opened by Miha-x64 1
  • New extensions

    New extensions

    mutableProp.casValue(expect, new): Boolean
    mutableProp.updateValue { ... } // using CAS, inline
    
    booleanMutableProp.takeEvery(true) {
        "value was switched to true but we've switched back to false and called this code"
    } // using CAS to switch
    booleanMutableProp.set(): Boolean
    booleanMutableProp.reset(): Boolean
    

    Also, group extensions better.

    enhancement 
    opened by Miha-x64 1
  • Easy way to unbind (dispose subscription)

    Easy way to unbind (dispose subscription)

    During binding, one passes a category, e. g. DATA, UI, DEFAULT. Then it becomes easy to dispose some category of binding, even automatically, e. g. prop.unsubscribeAll(UI).

    Change listener may want to do some clean-up when it gets unsubscribed. Its type, (old: T, new: T) -> Unit, may be replaced with something like

    interface PropertyChangeListener<in T> : (old: T, new: T) -> Unit {
        fun unsubscribed()
    }
    

    Convenience constructor should be added:

    inline fun <T> PropertyChangeListener(crossinline onChange: (old: T, new: T) -> Unit) = ...
    

    PropertyChangeListener name looks too long, suggestions are appreciated.

    enhancement 
    opened by Miha-x64 1
  • Concurrent bindTo + observedStateChanged

    Concurrent bindTo + observedStateChanged

    Thread 1 adds/removes first/last listener to such property causing observed state change and (un)subscription from/to sample property Thread 2 binds the same property to another one causing re-subscription

    bug 
    opened by Miha-x64 0
  • FAQ

    FAQ

    How this works. Comparison to fx.Property, Android.util.Property, io.reactivex.BehaviorSubject. No binary compatibility? Inline, flyweights. Automatic disposal. When properties are eligible for GC.

    opened by Miha-x64 0
  • Add kotlin compiler plugin to generate helpers and schemas

    Add kotlin compiler plugin to generate helpers and schemas

    Current API is a bit cursed. I'd really like to have an ability to treat Schemas as usual data classes. At least, when I'm constructing or changing them. For instance, compiler plugin can generate constructor functions, copy functions, etc.

    object Player : Schema<Player>() {
        val Name = "name" let string
        val Surname = "surname" let string
        val Score = "score".mut(i32, default = 0)
    
        // Can be generated by compiler plugin
        operator fun invoke(
            name: String,
            surname: String,
            score: Int? = null
        ): StructSnapshot<Player> = Player { p ->
            p[Name] = "John"
            p[Surname] = "Galt"
            score?.let { p[Score] = it }
        }
    }
    
    // Can be generated by compiler plugin
    fun StructSnapshot<Player>.copy(
        name: String? = null,
        surname: String? = null,
        score: Int? = null
    ): StructSnapshot<Player> = copy { p ->
        name?.let { p[Name] = name }
        surname?.let { p[Surname] = surname }
        score?.let { p[Score] = score }
    }
    
    opened by IlyaGulya 0
  • Observable resources

    Observable resources

    It is possible to observe configuration changes for changingConfigurations of a resource:

    res.text(R.string.*) => Property<CharSequence>
    textView.bindTextResTo(R.string.*)
    

    Consider: whether it is useful?

    opened by Miha-x64 0
  • Layout

    Layout

    Many storages do not support nesting. Thus, a Struct must be flattened somehow. Currently, there's no consistent solution across the library:

    • :android-bindings SharedPreferencesStruct serializes a whole field into a Base64'ed String if it is a Collection of non-string or is a Struct,
    • :sql Table has Embedded and NamingConvention to allow embedding Structs into Structs,
    • :http has no support for passing Structs as query parameters or form fields and waiting for this issue resolution.

    The proposal is to implement a separate Layout type (inside core :persistence) which will deal with structs and naming conventions, and provide some additional info like hasCollectionsOfStructs, hasMultiDimensionalCollections, hasCollectionsOfNullables etc, so storages could fail instantly and require additional info, if necessary. Inflating and flattening machinery follows Layout and moves to :persistence.

    embedded could become automatic everywhere (since SQL-friendly API #32 seems to be good without relations and graph fetching machinery, thus there will be no 1-to-1, 1-to-N, N-to-1, N-to-M relations, only embedding) but we must receive NamingConvention somehow, thus the API question remains open. :sql type() and nativeType() remain within Table as SQL-specific things. SharedPrefs and HTTP could support their own adapters for complicated cases.

    enhancement 
    opened by Miha-x64 0
  • `MutableProperty.updateValue`

    `MutableProperty.updateValue`

    like setValue, but will post value only if it is different from current

    // signature:
    fun update(newValue: T, dropIf: (T, T) -> Boolean)
    
    // sample:
    prop.update(new, dropIf = Objectz.Equal)
    prop.update(new, dropIf = Objectz.Same)
    
    opened by Miha-x64 0
  • Patching trees

    Patching trees

    Given a data tree (mutable Struct), there may exist a fun Struct.doSomething() which will turn this struct into a certain state without additional allocations, like JetPack Compose does, but without compiler plugin, and (as a bonus) in Java-friendly way.

    To mimic Compose behaviour, such structs may also be renderable.

    enhancement 
    opened by Miha-x64 0
Releases(0.0.17)
  • 0.0.17(May 28, 2021)

    Persistence

    • added FieldSet.contains(FieldSet)
    • fixed Schema.fieldByName for nonexistent names

    Properties

    • casValue now does equality check, not identity check (useful for boxed primitives and looks more user-friendly)
    • strictly required syncIf overloads to be called with named arguments
    • added just(T) function factory which returns () -> T

    Android bindings

    • RemoteViews.bind()
    • more flexible setWhen(Long)Clicked

    HTTP

    • using Call.Factory interface instead of OkHttpClient class

    SQL

    • nativeType() lambdas receive Connection
    • added Transaction.update(Table, ID, PartialStruct)
    • deprecated Transaction.truncate
    • [Eagerly|Lazily].[cell|struct] versions for returning null instead of throwing an exception
    Source code(tar.gz)
    Source code(zip)
  • 0.0.16(Nov 30, 2020)

    Properties

    • fixed re-subscription during notification
    • fixed removing queued listeners from single-thread properties
    • added meaningful Property.toString()

    SQL

    • Templates: session.query() -> Query()
    • Templates: added executeForRowCount() and executeForInsertedKey()
    • Transaction.insert(Struct) now accepts PartialStructs
    • Triggers: destructuring for ListChanges (val (ins, upd, del) = report.of(…))
    • removed ActiveRecord API stubs

    Exteneded Persistence

    • SCH.buildPartial {} -> SCH.Partial {}

    Android

    • deprecated observeChangesIf, added syncIf instead
    • added RecyclerView.observeAdapter()
    • added ViewAnimator.bindDisplayedChildTo(indexProperty: Property<Int>)
    • added LazyView
    • deprecated bindings to Support library
    Source code(tar.gz)
    Source code(zip)
  • 0.0.15(Sep 8, 2020)

    SQL

    Trigger-based observability Mutating SQL templates Deprecated Record-based API Session: implemented Closeable, added trimMemory()

    HTTP

    Removed Exchange implicit receiver from handler functions.

    Android JSON on JVM

    With this module you can use android.util.JsonReader/Writer outside of Android along with :android-json bindings.

    Extended persistence

    Added IncrementalN<…> type which is a special partial tuple. For example, Incremental3<Int, String, Long> is analogous to Either4<Unit, Int, Pair<Int, String>, Triple<Int, String, Long>>

    Source code(tar.gz)
    Source code(zip)
  • 0.0.14(Jul 4, 2020)

    Android-JSON

    Extracted JSON-related things from Android Bindings. Android-JSON is Java library, so you can pretend you have android.util.Json* and reuse it outside Android.

    • Fixed JSON path state after lenient []/{} read

    Persistence

    • Java-friendly enum type constructor and Partial.load function

    Extended Persistence

    • Added Box and Unit types
    • Java-friendly tuple constructors and destructuring functions, Either types
    • some fixes around TokenStream transforms

    HTTP

    • allowed building address without schema, i. e. starting with //
    • fixes for blob/byteArray types in query parameters etc
    • Undertow support for server-side
    Source code(tar.gz)
    Source code(zip)
  • 0.0.13(Jun 10, 2020)

    Persistence

    • Java interop: @JvmName("newStruct") on operator fun invoke(…): Struct<SCH>
    • added TokenStream.iteratorOfTransient
    • Some types can have different binary and text representations (e. g. UUID tends to be byte[] in binary, but becomes hex-with-hyphens in JSON)

    Extended Persistence

    • Java interop: @JvmName("newTuple") on operator fun invoke(…): Struct<Tuple>
    • Either: some deprecations (required for better Java interop in future); added map, fold, and unwrap functions

    SQL

    • PostgreSQL support; native and overridden SQL types
    • When fetching single cell or struct, you can choose what to do if result set if empty
    • Separate APIs for fetching “stable” and “transient” structs — added Lazily.transientStructs()
    • Some cleanup around Tables and DAO-based API

    JavaFX bindings

    • added Labeled.bindTextTo, Node.bindEnabledTo

    HTTP

    Introduced :http module for type-safe URL building, requesting, and routing.

    Source code(tar.gz)
    Source code(zip)
  • 0.0.12(Mar 14, 2020)

    Persistence

    • Lens became (Struct) -> T instead of (Partial) -> T, deprecated ofStruct(), added ofPartial()
    • Renamed DataTypes which are Java keywords (int, long, enum etc) to be Rust keywords (i32, i64) or just non-keywords (enumeration) for Java interop possibilities
    • TokenStream.iteratorOf(type) for reading streams on demand
    • Renamed schema.build to invoke, Player.build { => Player { (similarly to Rust struct literals)

    Extended Persistence

    • added t1 * t2 * t3 => Tuple3("first", t1, "second", t1, "third", t3) factories
    • added t1 + t2 + t3 => either3("first", t1, "second", t1, "third", t3) factories

    Properties

    • added MutableProperty<Boolean>.flip()
    • many FieldSet.intersect(FieldSet) overloads with intersected return type

    SQL

    • fixed support for non-long primary keys in SqliteSession
    • added JDBC/SQLite templates (Session.query API) with abilities to fetch Eagerly and Lazily
    • added projection(schema): Table — a factory method for building anonymous tables useful for raw queries
    • added NestingCaseNamingConvention for, well, nesting (outer.inner)
    • added transaction.insertAll(table, iterator)

    Android Bindings

    • internal binding optimizations
    • removed Lifecycle Watcher Fragment which worked incorrectly
    Source code(tar.gz)
    Source code(zip)
  • 0.0.11(Dec 5, 2019)

    Persistence

    Added TokenStream API, coroutine-based tokens { yield... } builder.

    Extended persistence

    Added transforms for TokenStreams: inline, outline, associate, entries.

    Android bindings

    Re-implemented JSON reading and writing on top of streams: jsonString.reader().json()....

    Source code(tar.gz)
    Source code(zip)
  • 0.0.10(Nov 3, 2019)

    Persistence

    • exact data types are now exposed by type parameters: type: DataType<T> -> type: DT where DT : DataType<T>

    Extended persistence

    • Added Tuple and Either types

    SQL

    • Supported storing Partials of other runtime representations then Struct/PartialStruct, e. g. Either

    Android Bindings

    • moved .properties.android.persistence to .persistence.android
    • moved some Suport and Design bindings, added duplicates for AndroidX and Material

    Android Sample

    Rewritten UI with Splitties instead of obsolete Anko.

    Source code(tar.gz)
    Source code(zip)
  • 0.0.9(May 27, 2019)

    Android bindings

    +View.setWhenLongClicked(clickedProperty: MutableProperty<Boolean>)
    -SharedPreferenceProperty<T> :  MutableProperty<T>
    +SharedPreferenceProperty<T> : TransactionalProperty<SharedPreferences.Editor, T>
    

    Persistence

    +PartialStruct<SCH : Schema<SCH>>
    +Lens<SCH : Schema<SCH>, in STR : PartialStruct<SCH>, T> : (STR) -> T?
    +Lens<SCH, Struct<SCH>, T>.ofStruct(): (Struct<SCH>) -> T
    +NamedLens<SCH : Schema<SCH>, in STR : PartialStruct<SCH>, T> : Lens<SCH, STR, T>
    -FieldDef<SCH : Schema<SCH>, T> : (Struct<SCH>) -> T
    +FieldDef<SCH : Schema<SCH>, T> : NamedLens<SCH, PartialStruct<SCH>, T>
    -StructTransaction<SCH>.setFrom(source: Struct<SCH>, fields: FieldSet<SCH, FieldDef.Mutable<SCH, *>>)
    +StructTransaction<SCH>.setFrom(source: PartialStruct<SCH>, fields: FieldSet<SCH, FieldDef.Mutable<SCH, *>>): FieldSet<SCH, FieldDef.Mutable<SCH, *>>
    sealed class DataType<T> {
    +    abstract class Partial<T, SCH : Schema<SCH>> : DataType<T>
    }
    

    Properties

    • computed diff property cancels computation when gets unobserved
    interface PropertiesMemento {
    +    fun reader(): PropertyIo
    }
    class ObservableStruct<SCH : Schema<SCH>> : BaseStruct<SCH>, PropertyStruct<SCH> {
    -    fun setFrom(source: Struct<SCH>, fields: FieldSet<SCH, FieldDef.Mutable<SCH, *>>)
    +    fun setFrom(source: PartialStruct<SCH>, fields: FieldSet<SCH, FieldDef.Mutable<SCH, *>>): FieldSet<SCH, FieldDef.Mutable<SCH, *>>
    }
    

    SQL

    Great work on supporting embedded [nullable] [partial] structs as the simplest form of 'relations'

    Added extended-persistence

    +partial(schema: SCH): DataType.Partial<PartialStruct<SCH>, SCH>
    +class PartialStructSnapshot<SCH : Schema<SCH>> : BaseStruct<SCH>
    +PartialStruct<SCH>.getOrNull(field: FieldDef<SCH, T>): T?
    +PartialStruct<SCH>.getOrDefault(field: FieldDef<SCH, T>, defaultValue: T): T
    +PartialStruct<SCH>.getOrElse(field: FieldDef<SCH, T>, defaultValue: () -> T): TSCH.buildPartial(build: +SCH.(StructBuilder<SCH>) -> Unit): PartialStruct<SCH>
    +Struct<SCH>.take(fields: FieldSet<SCH, FieldDef<SCH, *>>): PartialStruct<SCH>
    +PartialStruct<SCH>.copy(fields: FieldSet<SCH, FieldDef<SCH, *>>, mutate: SCH.(StructBuilder<SCH>) -> Unit): PartialStruct<SCH>
    
    +byteCollection: DataType.Collect<ByteArray, Byte>
    +shortCollection: DataType.Collect<ShortArray, Short>
    +intCollection: DataType.Collect<IntArray, Int>
    +longCollection: DataType.Collect<LongArray, Long>
    +floatCollection: DataType.Collect<FloatArray, Float>
    +doubleCollection: DataType.Collect<DoubleArray, Double>
    
    +uByte: DataType.Simple<UByte>
    +uShort: DataType.Simple<UShort>
    +uInt: DataType.Simple<UInt>
    
    Source code(tar.gz)
    Source code(zip)
  • 0.0.8(Feb 26, 2019)

    Persistence

    Added

    fun <SCH : Schema<SCH>> Struct<SCH>.copy(mutate: SCH.(StructBuilder<SCH>) -> Unit): StructSnapshot<SCH>
    

    Deprecated

    fun <E : Enum<E>, U : Any> enum(encodeAs: DataType.Simple<U>, encode: (E) -> U, fallback: (U) -> E = ...): DataType.Simple<E>
    

    Properties

    Added

    fun <E : Enum<E>> enum(fallback: (String) -> E = ...): DataType.Simple<E>
    
    • fixed a critical typo in InMemoryPropertiesMemento.writeTo
    • fixed possible correctness problem when bi- and multi-mapped properties have sources with mixed concurrentness — some incorrect code may crash now
    • less memory impact when updating single-threaded bi- and multi-mapped properties

    Minor signature changes:

    fun <T> Property<T>.onEach(func: (T) -> Unit): Unit   =>
    fun <P : Property<T>, T> P.onEach(func: (T) -> Unit): P
    
    fun MutableProperty<Boolean>.clearEachAnd(crossinline action: () -> Unit): Unit    =>
    fun <P : MutableProperty<Boolean>> P.clearEachAnd(crossinline action: () -> Unit): P
    
    fun <T : Any> MutableProperty<T?>.clearEachAnd(crossinline action: (T) -> Unit): Unit   =>
    fun <P : MutableProperty<T?>, T : Any> P.clearEachAnd(crossinline action: (T) -> Unit): P
    

    Android bindings

    • fixed ParcelPropertiesMemento.CREATOR field to have exactly Parcelable$Creator type and be compatible with common ProGuard rules

    SQL

    • fixed critical SqliteDialect codegen problem around nullable types

    Android-specific stats

    | Module | Defined methods | ~DEX size, KB | | -----------------|----------------:| -------------:| | persistence | 220 | 31.5 | | properties | 540 | 84.3 | | Android bindings | 272 | 38.1 | | SQL | 316 | 60.2 |

    Source code(tar.gz)
    Source code(zip)
  • 0.0.7(Feb 17, 2019)

    Properties core

    Removed

    concurrentMutableDiffPropertyOf(value: T): MutableDiffProperty<T, D>
    object Identity
    object Equals
    PropertyIo: chars, ints, longs, floats, doubles, stringList, enum, enumSet
    Property<Collection>: isEmpty, isNotEmpty
    Property.equalTo
    Property<CharSequence>: length, isEmpty, isNotEmpty, isBlank, isNotBlank, trimmed
    mutablePropertyOf, concurrentMutablePropertyOf, unsynchronizedMutablePropertyOf
    

    Changed

    CharSequencez.ValueOf: (Any?) -> String to (Any) -> String
    

    Added

    CharSequencez.ValueOfOrNull
    CharSequencez.ValueOfOrBlank
    

    Behaviour changes

    • fixed subscription of multi-arity listeners
    • fixed unsubscription of concurrent diff listeners
    • fixed sequential correctness of bi- and multi-mapped properties
    • fixed concurrent correctness of bi-mapped ones

    Persistence

    Removed useless parameters from enumSet DataType factories

    JavaFx bindings

    removed FxProperty.bind and bindBidirectionally

    Android bindings

    Removed PrefAdapters and according SharedPreferenceProperty constructor

    Deprecated

    fun View.bindToAttachedToWidow(attachedToWindowProperty: MutableProperty<Boolean>)
    

    Behabiour changes

    • bindViewTo and all View bindings now observe Activity's started state (via awful workaound ;)
    • fixed SharedPreferencesStruct constructor(Struct, SharedPreferences)
    Source code(tar.gz)
    Source code(zip)
  • 0.0.6(Feb 6, 2019)

    Properties core

    • if notification happens right now, postponing actual ChangeListener subscription until comes a value which was never seen yet
    • fixed a bug around multi-arity subscribers for DiffProperty
    • renamed concurrentMutableDiffPropertyOf to concurrentDiffPropertyOf and deprecated the latter
    • executing thread-confined ChangeListeners in-place when possible (i. e. when changed from target thread and no pending notifications)
    • made methods searching for platform executors easily removable by ProGuard
    • added Objectz, Arrayz, CharSequencez, Enumz, Collectionz utilities
    • new APIs
    interface TransactionalProperty<TRANSACTION, T> : Property<T>
    fun immutablePropertyOf(value: Unit): Property<Unit>
    fun Property<subtype of Number>.any arithmetic operator(Property<same Number>): Property<same Number>
    fun <T, U> Property<T>.zipWith(that: Property<U>): Property<Pair<T, U>>
    fun <T, U> Property<T>.flatMap(transform: (T) -> Property<U>): Property<U>
    fun <T> updatedEvery(period: Long, unit: TimeUnit = TimeUnit.MILLISECONDS, compute: () -> T): Property<T>
    
    • added utilities:
    fun <T> identity(): (T) -> T
    fun isEqualTo(that: Any?): (Any?) -> Boolean
    fun notEqualTo(that: Any?): (Any?) -> Boolean
    fun isSameAs(that: Any?): (Any?) -> Boolean
    fun notSameAs(that: Any?): (Any?) -> Boolean
    
    • added extensions for properties of collections
    operator fun <K, V> MutableProperty<Map<K, V>>.plusAssign(newMapping: Pair<K, V>)
    operator fun <K, V> MutableProperty<Map<K, V>>.minusAssign(victimKey: K)
    operator fun <T> MutableProperty<List<T>>.plusAssign(newElement: T)
    operator fun <T> MutableProperty<Set<T>>.plusAssign(newElement: T)
    operator fun <T> MutableProperty<Set<T>>.minusAssign(victim: T)
    
    inline fun <T> Collection<Property<T>>.valueList(): Property<List<T>>
    
    • added extensions for properties
    fun <T> Property<T>.observeChangesIf(subscribe: Boolean, onChange: ChangeListener<T>)
    fun <T : Any, U> Property<T?>.flatMapNotNullOrDefault(default: U, transform: (T) -> Property<U>): Property<U>
    fun <T> MutableProperty<T>.unbind()
    fun <T : Any> MutableProperty<T?>.clearEachAnd(crossinline action: (T) -> Unit)
    
    • deprecated
    inline fun <T> Property<T>.equalTo(that: Property<T>): Property<Boolean> // in favour of Objectz.Equal
    val Property<CharSequence>.***: Property<Int> // in favour of CharSequencez.***
    

    Added Persistence module

    Its API will be slightly changed in the future.

    // Streams
    interface BetterDataOutput<T>
    interface BetterDataInput<T>
    fun <D, SCH : Schema<SCH>> BetterDataOutput<D>.write(output: D, struct: Struct<SCH>)
    fun <D, T> DataType<T>.write(out: BetterDataOutput<D>, put: D, value: T)
    fun <D, SCH : Schema<SCH>> BetterDataInput<D>.read(input: D, schema: SCH): StructSnapshot<SCH>
    fun <D, T> DataType<T>.read(inp: BetterDataInput<D>, ut: D): T
    
    // Structs
    abstract class Schema<SELF : Schema<SELF>>
    sealed class FieldDef<SCH : Schema<SCH>, T>
    
    interface Struct<SCH : Schema<SCH>>
    interface TransactionalStruct<SCH : Schema<SCH>> : Struct<SCH>
    /* and some stuff around transactions */
    abstract class BaseStruct<SCH : Schema<SCH>>(
            final override val schema: SCH
    ) : Struct<SCH>
    class StructSnapshot<SCH : Schema<SCH>> : BaseStruct<SCH>
    
    fun <SCH : Schema<SCH>> SCH.build(build: SCH.(StructBuilder<SCH>) -> Unit): StructSnapshot<SCH>
    
    inline class FieldSet<SCH : Schema<SCH>, FLD : FieldDef<SCH, *>> /* and some methods for manipulating these sets */
    
    // Types
    sealed class DataType<T> {
        class Nullable<T : Any>(val actualType: DataType<T>) : DataType<T?>()
        abstract class Simple<T>(val kind: Kind) : DataType<T>() /* primitives, strings, byte arrays */
        abstract class Collect<C, E>(val elementType: DataType<E>) : DataType<C>()
    }
    val string: DataType.Simple<String>
    val byteArray: DataType.Simple<ByteArray>
    val bool: DataType.Simple<Boolean>
    val byte: DataType.Simple<Byte>
    val short: DataType.Simple<Short>
    val int: DataType.Simple<Int>
    val long: DataType.Simple<Long>
    val float: DataType.Simple<Float>
    val double: DataType.Simple<Double>
    val byteString: DataType<ByteString> = byteString(byteArray)
    fun byteString(blobType: DataType.Simple<ByteArray>): DataType<ByteString>
    inline fun <T : Any> nullable(type: DataType<T>): DataType.Nullable<T>
    fun <T> serialized(type: DataType<T>): DataType.Simple<T>
    fun enum(/* several overloads */): DataType.Simple<E>
    fun enumSet(/* several overloads */): DataType<Set<E>>
    fun <E> collection(elementType: DataType<E>): DataType.Collect<Collection<E>, E>
    fun <E> set(elementType: DataType<E>): DataType.Collect<Set<E>, E>
    

    Properties && Persistence

    Added

    interface PropertyStruct<SCH : Schema<SCH>> : Struct<SCH>
    interface TransactionalPropertyStruct<SCH : Schema<SCH>> : PropertyStruct<SCH>, TransactionalStruct<SCH>
    class ObservableStruct<SCH : Schema<SCH>> : BaseStruct<SCH>, PropertyStruct<SCH>
    interface PropertyIo members {
        operator fun <T> DataType<T>.invoke(prop: MutableProperty<T>)
    }
    

    Deprecated

    interface PropertyIo members {
        fun chars(prop: MutableProperty<CharArray>)
        fun ints(prop: MutableProperty<IntArray>)
        fun longs(prop: MutableProperty<LongArray>)
        fun floats(prop: MutableProperty<FloatArray>)
        fun doubles(prop: MutableProperty<DoubleArray>)
        fun stringList(prop: MutableProperty<List<String>>)
        fun <E : Enum<E>> enum(prop: MutableProperty<E>, type: Class<E>)
        fun <E : Enum<E>> enumSet(prop: MutableProperty<Set<E>>, type: Class<E>)
        /* and according PropertyIo.x(prop) extensions */
    }
    

    Renamed

    class PropertyInput -> PropertyReader
    class PropertyOutput -> PropertyWriter
    

    Android bindings

    Added:

    // View binding
    fun <V : View, T> V.bindViewTo(source: Property<T>, destination: android.util.Property<V, T>)
    
    fun View.bindBackgroundTo(backgroundProperty: Property<Drawable?>)
    fun View.bindBackgroundTo(backgroundProperty: Property<Int>)
    fun View.bindBackgroundColorTo(backgroundColorProperty: Property<Int>)
    
    fun TextView.bindToText(textProperty: MutableProperty<in String>)
    
    fun CardView.bindCardBackgroundColorTo(backgroundColorProperty: Property<Int>)
    fun CardView.bindCardBackgroundColorResTo(backgroundColorResProperty: Property<Int>)
    
    interface Holder<T>
    fun <T> Holder<T>.setItemWhenClicked(target: MutableProperty<in T>): View.OnClickListener
    fun RecyclerView.ViewHolder.setPositionWhenClicked(target: MutableProperty<in Int>): View.OnClickListener
    fun <T> Holder<T>.setItemWhenLongClicked(target: MutableProperty<in T>): View.OnLongClickListener
    fun RecyclerView.ViewHolder.setPositionWhenLongClicked(target: MutableProperty<in Int>): View.OnLongClickListener
    
    abstract class ObservingAdapter<VH : RecyclerView.ViewHolder> : RecyclerView.Adapter<VH>()
    
    // Persistence
    fun <SCH : Schema<SCH>> JsonReader.readListOf(schema: SCH): List<StructSnapshot<SCH>>
    fun <SCH : Schema<SCH>> JsonReader.read(schema: SCH): StructSnapshot<SCH>
    fun <SCH : Schema<SCH>> JsonWriter.write(list: List<Struct<SCH>>, fields: FieldSet<SCH, FieldDef<SCH, *>>)
    fun <SCH : Schema<SCH>> JsonWriter.write(struct: Struct<SCH>, fields: FieldSet<SCH, FieldDef<SCH, *>>)
    object ParcelIo : BetterDataInput<Parcel>, BetterDataOutput<Parcel>
    class SharedPreferenceStruct<SCH : Schema<SCH>> : BaseStruct<SCH>, TransactionalPropertyStruct<SCH>
    

    Deprecated:

    // View binding
    View.bindToAttachedToWidow(attachedToWindowProperty: MutableProperty<Boolean>)
    
    // Persistence
    interface PrefAdapter<T> /* and all its implementations */
    class SharedPreferenceProperty members {
        fun bindTo(sample: Property<T>)
        fun casValue(expect: T, update: T): Boolean
    }
    

    JavaFX bindings

    Added

    fun <T> Property<List<T>>.fxList(): ObservableList<T>
    fun <T> MutableProperty<T>.bindTo(that: ObservableValue<T>)
    fun ButtonBase.setWhenClicked(clickedProperty: MutableProperty<Boolean>)
    
    Source code(tar.gz)
    Source code(zip)
  • 0.0.5(Aug 12, 2018)

    Core

    — performance optimizations https://github.com/Miha-x64/reactive-properties/commit/8b288e44288aa709c00f9bec79ae037fd1f6b566, https://github.com/Miha-x64/reactive-properties/commit/1f91f69d3889f97efb847cd0ad3bb11e4e44a113, https://github.com/Miha-x64/reactive-properties/commit/25a4513dde26571d6a7498c80b313b44e0e10932 — bound (bidirectionally mapped) properties https://github.com/Miha-x64/reactive-properties/commit/0daf01bdaa8c3b303d877eae831629c2138de935, https://github.com/Miha-x64/reactive-properties/commit/c47ec70f109819f44553853667cf7f336d979810, https://github.com/Miha-x64/reactive-properties/commit/b9286a046a08f7a3fab4535dd886e91c510353a7 — fixed casValue behaviour, https://github.com/Miha-x64/reactive-properties/commit/315dbdc76a63f39b1b46539f9f89321193069bb6 — added MutableProperty.plusAssign extension https://github.com/Miha-x64/reactive-properties/commit/4df7a0a9dcbcd816bb3b3f04e9bd496f9c24bfe0 (a bunch of extensions may be added and moved to another artifact in the future) — potential fix for theoretical concurrent bug #40

    Android bindings

    Nothing changed, just recompiled against new core.

    Source code(tar.gz)
    Source code(zip)
  • 0.0.4(Jul 21, 2018)

    Properties: — moved platform executors and their factories into according *-bindings packages; — fixed single-threaded debounced properties' notification thread; — added Property<T>.equalTo(Property<T>): Property<Boolean> extension; — FJ pools with parallelism != 1 is now unsupported for subscription because thread is unpredictable.

    Android bindings: — not clearing out text selection on bound (EditTexts); — fixed POM dependencies section; — added CompoundButton.checked bindings.

    Source code(tar.gz)
    Source code(zip)
  • 0.0.3(Jun 28, 2018)

    — Cancel async notification when ConfinedChangeListener gets removed #30 ; — made ConfinedChangeListener internal; — Property<T>.debounced() parameter unit defaults to millis; — Added Handlecutor class — it's an android.os.Handler implementing j.u.c.Executormajor behaviour change: addChangeListener now subscribes on current thread #27; use addUnconfinedChangeListener if you need old behaviour; — fixed a potential deadlock in #36, no we're lock-free again; — fixed potential bugs when property value is assigned to itself 😳; — made internal declarations inaccessbile from Java, #34; — now attached views can be bound safely, too, #26; — Android bindings: moved some classes to more specific packages; — moved HandlerAsExecutor to Android bindings and renamed it to HandlerExecutor (core uses Handlecutor now); — Android bindings: added IdleExecutor.

    Source code(tar.gz)
    Source code(zip)
  • 0.0.2(May 5, 2018)

    Both

    • methods count reduction, aggressive class merging, synthetic and bridge methods removal, breaks binary compatibility wherever possible because of many inline functions which expose many changed classes

    Reactive Properties

    • safer notification code (#18): if a property gets (un)subscribed during notification, changes are applied instantly; if a new value is being set during notification, the next notification gets deferred
    • added mapOn (#19) which calculates mapped property value on a specified worker
    • renamed MutableProperty.cas to casValue, breaks both source and binary compatibility
    • removed Short, Char, ShortArray, Array<String> support from persistence because these types are rare, breaks both source and binary compatibility
    • (Mutable)Property.value is a property again, removed getValue/setValue accessors and value extension-property, breaks source compatibility
    • fixed Property.onEach extension, #21
    • DiffProperty support (#15): diff-property is a property which also provides a difference between old and new values
    • ForkJoinPool support — debounce and background mapping of single-threaded property will deliver result to the pool where it was called (Android's Handler and JavaFX Platform are supported from the very beginning, too)
    • fixed MutableProperty.bindTo behaviour under contention
    • all dependent properties (mapped, debounced, distinct, bound, etc) now observe original property only when they are being observed (#23), this prevents memory leaks
    • all inline extensions are now in PropertiesInline class (which shouldn't ever be loaded by VM), all not inline — in Properties, breaks binary compatibility
    • fixed MutableProperty<Boolean>.clearEachAnd — now it calls action when just bound to a property with true value
    • deprecated unsynchronizedMutablePropertyOf and concurrentMutablePropertyOf, use propertyOf and concurrentPropertyOf instead

    Android Bindings

    • View.bindTo now throws an exception when called on attached View on API 17+ (no isAttachedToWindow for API <17, we can't perform this check safely, for production; just bind views which are not attached yet, for Fragments this means onCreateView, not onViewCreated), breaks incorrect code
    • View.bindTo now passes also a view to bind function, breaks both source and binary compatibility
    • SharedPreferenceProperty: added StringSetPrefAdapter, IntPrefAdapter, LongPrefAdapter, FloatPrefAdapter, DoublePrefAdapter, BoolPrefAdapter
    • removed mutablePropertyOf extension-functions for View, Activity, Fragment since we have propertyOf
    • TextView: compound drawables support
    Source code(tar.gz)
    Source code(zip)
  • Snapshot-1(Sep 9, 2017)

Owner
Mike
Remote & part-time, Android & server-side, development & consulting.
Mike
Kotlin-client-dsl - A kotlin-based dsl project for a (Client) -> (Plugin) styled program

kotlin-client-dsl a kotlin-based dsl project for a (Client) -> (Plugin) styled p

jackson 3 Dec 10, 2022
Kotlin Object Notation - Lightweight DSL to build fluid JSON trees

Kotlin Object Notation Lightweight kotlin MPP DSL for building JSON trees Setup Just drop the dependency in your commonMain sourceSet kotlin { sourc

Martynas Petuška 43 Dec 10, 2022
Extended-Hanoi - Extended Hanoi Tower in Kotlin Tornadofx

Extended-Hanoi Extended Hanoi Tower in Kotlin Tornadofx

null 10 Dec 19, 2022
CPU Scheduler Visualization written in Kotlin TornadoFX

CPU-Scheduler-Visualization CPU Scheduler Visualization written in Kotlin TornadoFX 목적 CPU Secheduling algorithm을 시각화해주는 프로그램입니다. FCFS, NonPreemptiveS

null 0 Apr 25, 2022
[Android Library] A SharedPreferences helper library to save and fetch the values easily.

Preference Helper A SharedPreferences helper library to save and fetch the values easily. Featured in Use in your project Add this to your module's bu

Naveen T P 13 Apr 4, 2020
ViewModel LiveData Sample - Sample of using ViewModel, LiveData and Data Binding

ViewModel_LiveData_Sample Sample Code for Lesson 8 using ViewModel, LiveData and

null 0 Mar 18, 2022
A Kotlin library for reactive and boilerplate-free SharedPreferences in Android

KPreferences A Kotlin library for reactive and boilerplate-free Shared Preferences in Android. With KPreferences you can use Kotlin's marvelous delega

Mohamad Amin Mohamadi 19 Dec 16, 2020
Easy lightweight SharedPreferences library for Android in Kotlin using delegated properties

Easy lightweight SharedPreferences library for Android in Kotlin using delegated properties Idea Delegated properties in Kotlin allow you to execute a

null 25 Dec 27, 2022
Kotpref - Android SharedPreferences delegation library for Kotlin

Kotpref Android SharedPreference delegation for Kotlin. Install repositories { mavenCentral() } dependencies { // core implementation 'co

Takao Chiba 684 Dec 22, 2022
This provides the javafx runtimes for windows, linux, and mac os x86 platforms for Ignition

ignition JavaFX Provider This provides the javafx runtimes for windows, linux, and mac os x86 platforms for Ignition Steps to use run gradlew build st

Jonathan Coffman 2 Oct 17, 2022
A JavaFX app to visualize RNA alignments from Rfam database.

Kaknas Kaknas is a JavaFX graphical tool to visualize and manipulate RNA alignments from the Rfam database. It is powered with RNArtistCore. For a giv

Fabrice Jossinet 5 Apr 7, 2022
Android Library to make SharedPreferences usage easier.

KotlinPreferences Kotlin Android Library, that makes preference usage simple and fun. KotlinPreferences now have a brother. With KotlinPreferences, yo

Marcin Moskała 50 Nov 6, 2022
Adapter library for SharedPreferences

EasyPrefs Adapter library for SharedPreferences which reduces boilerplate needed to store simple data, but open enough to not interfere with your own

Kacper Wojciechowski 6 Nov 23, 2021
Android SharedPreferences Helper

PocketDB Android SharedPreferences Helper This is SharedPreferences Helper like a database noSql. Support AES encryption Latest Version Download depen

Muhammad Utsman 9 Jun 20, 2021
📒 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
A fragment binding library - the easier, efficient way.

Byda allprojects { repositories { ... maven { url 'https://jitpack.io' } } } dependencies { implementation 'com.github.Codzure:B

null 3 May 27, 2021
This repository is part of a Uni-Project to write a complete Compiler for a subset of Java.

Compiler This repository is part of a Uni-Project to write a complete Compiler for a subset of Java. Features error recovery using context sensitive a

null 3 Jan 10, 2022
Clean MVVM with eliminating the usage of context from view models by introducing hilt for DI and sealed classes for displaying Errors in views using shared flows (one time event), and Stateflow for data

Clean ViewModel with Sealed Classes Following are the purposes of this repo Showing how you can remove the need of context in ViewModels. I. By using

Kashif Mehmood 22 Oct 26, 2022
A Lightweight PDF Viewer Android library which only occupies around 125kb while most of the Pdf viewer occupies up to 16MB space.

Pdf Viewer For Android A Simple PDF Viewer library which only occupies around 125kb while most of the Pdf viewer occupies upto 16MB space. How to inte

Rajat 362 Dec 29, 2022