Skip to content

Commit

Permalink
Merge pull request #30 from iZettle/withlatestfrom-operator
Browse files Browse the repository at this point in the history
Add withLatestFrom operator
  • Loading branch information
egarni authored Sep 27, 2018
2 parents 5667974 + 6ffb57a commit 8246f11
Show file tree
Hide file tree
Showing 2 changed files with 129 additions and 0 deletions.
55 changes: 55 additions & 0 deletions Flow/Signal+Combiners.swift
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,61 @@ public func merge<Signals: Sequence>(_ signals: Signals) -> CoreSignal<Signals.I
})
}

/// Returns a new signal combining each value from self with the latest value from the second signal.
///
/// a)---------b------c-----d--|
/// | | |
/// 0)------1------2-----------|
/// | |
/// +--------------------------+
/// | withLatestFrom() |
/// +--------------------------+
/// | | |
/// (a,0))---(b,1)--(c,2)-(d,2)|
///
/// - Note: Will terminate when any signal terminates with an error.
public extension SignalProvider {
func withLatestFrom<S: SignalProvider>(_ other: S) -> CoreSignal<Kind.DropWrite, (Value, S.Value)> where Kind.DropWrite == S.Kind.DropWrite {
let signal = providedSignal
let otherSignal = other.providedSignal
return CoreSignal(onEventType: { callback in
let state = StateAndCallback(state: S.Value?.none, callback: callback) // previous

state += otherSignal.onEventType { eventType in
switch eventType {
case .initial(nil):
break
case .initial(let val?):
state.protectedVal = val
case .event(.value(let val)):
state.protectedVal = val
case .event(.end(let error)):
return state.call(.event(.end(error)))
}
}

state += signal.onEventType { eventType in
switch eventType {
case .initial(nil):
state.call(.initial(nil))
case .initial(let val?):
if let otherValue = state.protectedVal {
state.call(.initial((val, otherValue)))
}
case .event(.value(let val)):
if let otherValue = state.protectedVal {
state.call(.event(.value((val, otherValue))))
}
case .event(.end(let error)):
state.call(.event(.end(error)))
}
}

return state
})
}
}

/// Returns a new signal merging the values emitted from `signals`
///
/// a)---b---c------d-|
Expand Down
74 changes: 74 additions & 0 deletions FlowTests/SignalProviderTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -441,6 +441,80 @@ class SignalProviderTests: XCTestCase {
}
}

func testWithLatestFrom() {
runTest(timeout: 10) { bag in
let a = ReadWriteSignal<String>("")
let b = ReadWriteSignal<Int>(0)

let expected = [("b", 1), ("c", 2), ("d", 2)]

var buffer = [(String, Int)]()

let signal = a.plain().withLatestFrom(b.plain())

let expectation = self.expectation(description: "Values should be combined in order")
bag += signal.onValue { v in
buffer.append(v)

if buffer.count == expected.count {
var equal = true
for (index, element) in expected.enumerated() {
let t = buffer[index]
if t.0 != element.0 {
equal = false
}
}

if equal { expectation.fulfill() }
}
}

a.value = "a"
b.value = 1
a.value = "b"
b.value = 2
a.value = "c"
a.value = "d"
}
}

func testWithLatestFromUsingSource() {
runTest(timeout: 10) { bag in
let a = ReadWriteSignal<String>(".")
let b = ReadWriteSignal<Int>(0)

let expected = [(".", 0), ("a", 0), ("b", 1), ("c", 2), ("d", 2)]

var buffer = [(String, Int)]()

let signal = a.withLatestFrom(b)

let expectation = self.expectation(description: "Values should be combined in order")
bag += signal.atOnce().onValue { v in
buffer.append(v)

if buffer.count == expected.count {
var equal = true
for (index, element) in expected.enumerated() {
let t = buffer[index]
if t.0 != element.0 {
equal = false
}
}

if equal { expectation.fulfill() }
}
}

a.value = "a"
b.value = 1
a.value = "b"
b.value = 2
a.value = "c"
a.value = "d"
}
}

func testCombineLatestWith() {
runTest(timeout: 10) { bag in
let a = ReadWriteSignal<String>("")
Expand Down

0 comments on commit 8246f11

Please sign in to comment.