Skip to content

Commit

Permalink
Added Disposable (#9)
Browse files Browse the repository at this point in the history
  • Loading branch information
RuiAAPeres authored Dec 1, 2017
1 parent 44873da commit bcda48b
Show file tree
Hide file tree
Showing 2 changed files with 73 additions and 9 deletions.
49 changes: 40 additions & 9 deletions Receiver/Sources/Receiver.swift
Original file line number Diff line number Diff line change
Expand Up @@ -68,10 +68,9 @@ public class Receiver<Wave> {

private let values = Atomic<[Wave]>([])
private let strategy: Strategy
private let handlers: Atomic<[Handler]>
private let handlers = Atomic<[Int:Handler]>([:])

private init(strategy: Strategy) {
self.handlers = Atomic<[Handler]>([])
self.strategy = strategy
}

Expand All @@ -84,7 +83,7 @@ public class Receiver<Wave> {
for index in indexs {
let value = _values[index]
handlers.apply { _handlers in
for _handler in _handlers {
for _handler in _handlers.values {
_handler(value)
}
}
Expand All @@ -104,10 +103,14 @@ public class Receiver<Wave> {
/// - parameters:
/// - handle: An anonymous function that gets called every time a
/// a new value is sent.
public func listen(to handle: @escaping (Wave) -> Void) {
/// - returns: A reference to a disposable
@discardableResult public func listen(to handle: @escaping (Wave) -> Void) -> Disposable {
var _key: Int!
handlers.apply { _handlers in
_handlers.append(handle)
_key = (_handlers.keys.map { $0.hashValue }.max() ?? -1) + 1
_handlers[_key] = handle
}

switch strategy {
case .cold:
broadcast(elements: Int.max)
Expand All @@ -116,6 +119,12 @@ public class Receiver<Wave> {
case .hot:
broadcast(elements: 0)
}

return Disposable {[weak self] in
self?.handlers.apply { _handlers in
_handlers[_key] = nil
}
}
}

/// Factory method to create the pair `transmitter` and `receiver`.
Expand All @@ -132,23 +141,23 @@ public class Receiver<Wave> {
}
}

public extension Receiver {
extension Receiver {
/// Enum that represents the Receiver's strategies
enum Strategy {
public enum Strategy {
case cold
case warm(upTo: Int)
case hot
}
}

public extension Receiver {
extension Receiver {
/// The write only implementation of the Observer pattern.
/// Used to broadcast values (`Wave`) that will be observed by the `receiver`
/// and forward to all its listeners.
///
/// Note: Keep in mind that the `transmitter` will hold strongly
/// to its `receiver`.
struct Transmitter {
public struct Transmitter {
private let receiver: Receiver

internal init(_ receiver: Receiver) {
Expand All @@ -164,3 +173,25 @@ public extension Receiver {
}
}
}

extension Receiver {
/// Used to remove the handler from being called again (aka dispose of),
/// when you invoke `receiver.listen(handler)`.
/// Example:
///
/// Your Receiver is shared across multiple screens (or entities) and one
/// of those, stops existing. In this scenario,
/// you would call `diposable.dispose()` at `deinit` time.
public class Disposable {
private let cleanUp: () -> Void

internal init(_ cleanUp: @escaping () -> Void) {
self.cleanUp = cleanUp
}

/// Used to dispose of the handler passed to the receiver.
public func dispose() {
cleanUp()
}
}
}
33 changes: 33 additions & 0 deletions ReceiverTests/ReceiverTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -156,4 +156,37 @@ class ReceiverTests: XCTestCase {
XCTAssertNotNil(outterTransmitter)
XCTAssertNotNil(outterReceiver)
}

func test_disposable() {
let (transmitter, receiver) = Receiver<Int>.make()
var called = 0

let disposable = receiver.listen { wave in
called = called + 1
}

disposable.dispose()
transmitter.broadcast(1)
XCTAssertTrue(called == 0)
}

func test_disposable_MultipleListeners() {
let (transmitter, receiver) = Receiver<Int>.make()
var value = 0

let disposable1 = receiver.listen { wave in
value = 1
}

disposable1.dispose()
transmitter.broadcast(1)
XCTAssertTrue(value == 0)

receiver.listen { wave in
value = 2
}

transmitter.broadcast(1)
XCTAssertTrue(value == 2)
}
}

0 comments on commit bcda48b

Please sign in to comment.