diff --git a/Sources/Incremental/Incremental.swift b/Sources/Incremental/Incremental.swift
index cdeceea..051654b 100644
--- a/Sources/Incremental/Incremental.swift
+++ b/Sources/Incremental/Incremental.swift
@@ -44,16 +44,16 @@ fileprivate struct Register {
}
final class Observer {
- let _height: () -> Int
+ let _height: (_ seen: [AnyObject]) -> Int
let fire: () -> ()
var cancelled = false
- init(fire: @escaping () -> (), height: @escaping () -> Int) {
+ init(fire: @escaping () -> (), height: @escaping (_ seen: [AnyObject]) -> Int) {
self.fire = fire
self._height = height
}
- var height: Int {
- return _height()
+ func height(_ seen: [AnyObject]) -> Int {
+ return _height(seen + [self])
}
}
@@ -64,7 +64,7 @@ final class Queue {
func enqueue(_ newObservers: [Observer]) {
observers.append(contentsOf: newObservers)
- observers.sort { $0.height < $1.height }
+ observers.sort { $0.height([]) < $1.height([]) }
process()
}
@@ -98,7 +98,7 @@ public class Observable {
observer(value)
return observers.add(Observer(fire: {
observer(self.value)
- }, height: {
+ }, height: { _ in
return 0
}))
}
@@ -108,15 +108,16 @@ public class Observable {
observers.remove(token)
}
- var height: Int {
- let maxChildHeight = observers.values.map { $0.height }.max()
+ func height(_ seen: [AnyObject]) -> Int {
+ let newSeen = seen + [self]
+ let maxChildHeight = observers.values.filter { child in !newSeen.contains { $0 === child } }.map { $0.height(newSeen) }.max()
return (maxChildHeight ?? 0) + 1
}
@discardableResult func addChild(fire: @escaping () -> (), dependent: @escaping () -> Observable) -> Token {
fire()
return observers.add(Observer(fire: fire, height: {
- dependent().height
+ dependent().height($0)
}))
}
diff --git a/Tests/IncrementalTests/IncrementalTests.swift b/Tests/IncrementalTests/IncrementalTests.swift
new file mode 100644
index 0000000..edb4f99
--- /dev/null
+++ b/Tests/IncrementalTests/IncrementalTests.swift
@@ -0,0 +1,45 @@
+import Foundation
+import XCTest
+import Incremental
+
+func &&(lhs: Observable, rhs: Observable) -> Observable {
+ return lhs.flatMap { x in rhs.map { $0 && x } }
+}
+
+final class IncrementalTests: XCTestCase {
+ func testFlatMapNested() {
+ let o = Observable(0)
+ let o2 = o.flatMap { _ in o }
+ var results: [Int] = []
+ o2.observe { results.append($0) }
+ o.send(1) // eventually crashes here
+ XCTAssert(results == [0, 1])
+ }
+
+ func testFlatMapMap() {
+ let x = Observable(1)
+ let double = x.flatMap { value in x.map { value + $0 }}
+ var result: [Int] = []
+ let disposable = double.observe { result.append($0) }
+ x.send(2)
+ XCTAssertEqual(result, [2,4])
+ }
+
+ func test() {
+ let airplaneMode = Observable(false)
+ let cellular = Observable(true)
+ let wifi = Observable(true)
+
+ let notAirplaneMode = airplaneMode.map { !$0 }
+
+ let cellularEnabled = notAirplaneMode && cellular
+ let wifiEnabled = notAirplaneMode && wifi
+ let wifiAndCellular = wifiEnabled && cellularEnabled
+
+ var results: [Bool] = []
+ _ = wifiAndCellular.observe { results.append($0) }
+ airplaneMode.send(true)
+ airplaneMode.send(false)
+ XCTAssertEqual(results, [true,false,true])
+ }
+}