Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

MVICore multiplatform #114

Open
wants to merge 31 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 1 commit
Commits
Show all changes
31 commits
Select commit Hold shift + click to select a range
8399ad1
Mpp implementation of basic elements
ShikaSD Oct 1, 2019
9237883
Mpp implementation of lifecycle
ShikaSD Oct 1, 2019
f453cc1
Binder implementation
ShikaSD Oct 9, 2019
24209ed
Suppress for function names
ShikaSD Oct 9, 2019
1687223
Allow last binder message to be propagated
ShikaSD Dec 23, 2019
5da45f7
Add initializer block to binder
ShikaSD Dec 23, 2019
3dadd57
Minor rearrangements
ShikaSD Dec 23, 2019
be42e12
Add js and macos targets
ShikaSD Dec 27, 2019
6933bb5
Add tests for reducer feature
ShikaSD Dec 27, 2019
fff16bd
Migrate to interfaces instead of function supertypes to make js compile
ShikaSD Dec 28, 2019
876d93e
Rx compat module
ShikaSD Dec 30, 2019
39a25da
Update gradle version
ShikaSD Dec 30, 2019
17a71ee
Update travis run to include new modules
ShikaSD Dec 30, 2019
4c04075
Check feature correctness when frozen
ShikaSD Dec 30, 2019
5a88ff4
Migrate binder to atomics
ShikaSD Dec 31, 2019
a2fc84d
Add actor reducer tests
ShikaSD Jan 2, 2020
0bc48af
Add cancel test for native
ShikaSD Jan 2, 2020
4e89f9a
Add benchmarks and fix impl of sources
ShikaSD Jan 3, 2020
02125f6
More benches
ShikaSD Jan 3, 2020
743657d
Remove string operations from benches for reference numbers
ShikaSD Jan 3, 2020
2cc2c90
Use typealiases for common elements
ShikaSD Jan 3, 2020
970f22e
Add base feature and cancellable tests
ShikaSD Jan 6, 2020
9e0b1e2
Add test to check states after crash
ShikaSD Jan 7, 2020
fe54594
Fix native frozen tests
ShikaSD Jan 7, 2020
2c6624b
Sneak out linux tests into travis build
ShikaSD Jan 7, 2020
aaf683a
Use reaktive utils package
ShikaSD May 8, 2020
ba45911
Make observer use atomic references
ShikaSD May 9, 2020
33ae5f6
Add ios targets
ShikaSD May 9, 2020
9778557
Fix variance
ShikaSD May 9, 2020
5cadb96
Add tests for rx reducer feature
ShikaSD May 9, 2020
0b65cb9
Add tests for binder in rx
ShikaSD May 9, 2020
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Prev Previous commit
Next Next commit
Add initializer block to binder
  • Loading branch information
ShikaSD committed Dec 23, 2019
commit 5da45f72e917e7d1e34f5712175efc4e455be3eb
Original file line number Diff line number Diff line change
Expand Up @@ -8,13 +8,14 @@ import com.badoo.mvicore.common.lifecycle.Lifecycle
import com.badoo.mvicore.common.lifecycle.Lifecycle.Event.BEGIN
import com.badoo.mvicore.common.lifecycle.Lifecycle.Event.END
import com.badoo.mvicore.common.source
import com.badoo.mvicore.common.sources.DelayUntilSource

abstract class Binder : Cancellable {
internal abstract fun <Out, In> connect(connection: Connection<Out, In>)
}

fun binder(): Binder = SimpleBinder()
fun binder(lifecycle: Lifecycle): Binder = LifecycleBinder(lifecycle)
fun binder(init: Binder.() -> Unit = { }): Binder = SimpleBinder(init)
fun binder(lifecycle: Lifecycle, init: Binder.() -> Unit = { }): Binder = LifecycleBinder(lifecycle, init)

fun <In> Binder.bind(connection: Pair<Source<In>, Sink<In>>) {
val (from, to) = connection
Expand All @@ -25,18 +26,44 @@ fun <Out, In> Binder.bind(connection: Connection<Out, In>) {
connect(connection)
}

internal class SimpleBinder : Binder() {
internal class SimpleBinder(init: Binder.() -> Unit) : Binder() {
private val cancellables = mutableListOf<Cancellable>()

/**
* Stores internal end of every `emitter` connected through binder
* Allows for propagation of events in case emission disposes the binder
* E.g.:
* from -> internalSource -> to // Events from `from` are propagated to `to`
* #dispose()
* from xx internalSource -> to // Remaining events from `internalSource` are propagated to `to`
*/
private val fromToInternalSource = mutableMapOf<Source<*>, SimpleSource<*>>()

/**
* Delay events emitted on subscribe until `init` lambda is executed
*/
private val initialized = source(initialValue = false)

init {
init()
initialized(true)
}

override fun <Out, In> connect(connection: Connection<Out, In>) {
val internalSource = getInternalSourceFor(connection.from!!)
val outSource = if (connection.connector != null) {
val transformedSource = if (connection.connector != null) {
connection.connector.invoke(internalSource)
} else {
internalSource as Source<In>
}
outSource.connect(connection.to)

val delayInitialize = if (initialized.value!!) {
transformedSource
} else {
DelayUntilSource(initialized, transformedSource)
}

delayInitialize.connect(connection.to)
}

private fun <T> getInternalSourceFor(from: Source<T>): SimpleSource<T> =
Expand All @@ -51,10 +78,10 @@ internal class SimpleBinder : Binder() {
}
}

internal class LifecycleBinder(lifecycle: Lifecycle) : Binder() {
internal class LifecycleBinder(lifecycle: Lifecycle, init: Binder.() -> Unit) : Binder() {
private var lifecycleActive = false
private val cancellables = mutableListOf<Cancellable>()
private val innerBinder = SimpleBinder()
private val innerBinder = SimpleBinder(init)
private val connections = mutableListOf<Connection<*, *>>()

init {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,8 +1,7 @@
package com.badoo.mvicore.common.binder

import com.badoo.mvicore.common.Cancellable
import com.badoo.mvicore.common.Sink
import com.badoo.mvicore.common.Source
import com.badoo.mvicore.common.sources.MapNotNullSource

internal class NotNullConnector<Out, In>(private val mapper: (Out) -> In?): Connector<Out, In> {
override fun invoke(element: Source<out Out>): Source<In> {
Expand All @@ -12,14 +11,3 @@ internal class NotNullConnector<Out, In>(private val mapper: (Out) -> In?): Conn
override fun toString(): String =
mapper.toString()
}

private class MapNotNullSource<Out, In>(private val delegate: Source<Out>, private val mapper: (Out) -> In?): Source<In> {
override fun connect(sink: Sink<In>): Cancellable =
delegate.connect {
mapper(it)?.let { sink(it) }
}

override fun cancel() {
delegate.cancel()
}
}
Original file line number Diff line number Diff line change
@@ -1,5 +1,9 @@
package com.badoo.mvicore.common

/**
* NOTE: in conversions from other frameworks you need to override equals and hashCode
* to support binder "emit after dispose" functionality
*/
interface Source<T> : Cancellable {
fun connect(sink: Sink<T>): Cancellable
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
package com.badoo.mvicore.common.sources

import com.badoo.mvicore.common.Cancellable
import com.badoo.mvicore.common.Sink
import com.badoo.mvicore.common.Source
import com.badoo.mvicore.common.cancellableOf

internal class DelayUntilSource<T>(
private val signal: Source<Boolean>,
private val delegate: Source<T>
): Source<T> {
override fun connect(sink: Sink<T>): Cancellable {
val delayedSink = DelayedSink(sink)
val cancelDelay = signal.connect { if (it) { delayedSink.send() } }
val cancelSubscription = delegate.connect(delayedSink)
return cancellableOf {
cancelDelay.cancel()
cancelSubscription.cancel()
}
}

override fun cancel() {
delegate.cancel()
}

private class DelayedSink<T>(private val delegate: Sink<T>) : Sink<T> {
private var passThrough = false
private val events = mutableListOf<T>()

override fun invoke(value: T) {
if (passThrough) {
delegate(value)
} else {
events += value
}
}

fun send() {
passThrough = true
events.forEach { delegate(it) }
events.clear()
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
package com.badoo.mvicore.common.sources

import com.badoo.mvicore.common.Cancellable
import com.badoo.mvicore.common.Sink
import com.badoo.mvicore.common.Source

internal class MapNotNullSource<Out, In>(private val delegate: Source<Out>, private val mapper: (Out) -> In?): Source<In> {
override fun connect(sink: Sink<In>): Cancellable =
delegate.connect {
mapper(it)?.let { sink(it) }
}

override fun cancel() {
delegate.cancel()
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -197,4 +197,16 @@ class BinderTest {

sink.assertValues(0)
}

@Test
fun binder_messages_sent_on_initialize_are_not_lost() {
val passThroughSource = source<Int>()
val binder = binder {
bind(source to passThroughSource)
source.invoke(0)
bind(passThroughSource to sink)
}

sink.assertValues(0)
}
}