Skip to content

Commit de6a136

Browse files
andersioNachoSoto
authored andcommitted
notifications as Signal.
1 parent 7cc375d commit de6a136

File tree

2 files changed

+49
-32
lines changed

2 files changed

+49
-32
lines changed

Sources/FoundationExtensions.swift

Lines changed: 7 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -12,34 +12,23 @@ import enum Result.NoError
1212
extension NotificationCenter: ReactiveExtensionsProvider {}
1313

1414
extension Reactive where Base: NotificationCenter {
15-
/// Returns a SignalProducer to observe posting of the specified
16-
/// notification.
15+
/// Returns a Signal to observe posting of the specified notification.
1716
///
1817
/// - parameters:
1918
/// - name: name of the notification to observe
2019
/// - object: an instance which sends the notifications
2120
///
22-
/// - returns: A SignalProducer of notifications posted that match the given
23-
/// criteria.
21+
/// - returns: A Signal of notifications posted that match the given criteria.
2422
///
25-
/// - note: If the `object` is deallocated before starting the producer, it
26-
/// will terminate immediately with an `interrupted` event.
27-
/// Otherwise, the producer will not terminate naturally, so it must
28-
/// be explicitly disposed to avoid leaks.
29-
public func notifications(forName name: Notification.Name?, object: AnyObject? = nil) -> SignalProducer<Notification, NoError> {
30-
// We're weakly capturing an optional reference here, which makes destructuring awkward.
31-
let objectWasNil = (object == nil)
32-
return SignalProducer { [base = self.base, weak object] observer, disposable in
33-
guard object != nil || objectWasNil else {
34-
observer.sendInterrupted()
35-
return
36-
}
37-
23+
/// - note: The signal does not terminate naturally. Observers must be
24+
/// explicitly disposed to avoid leaks.
25+
public func notifications(forName name: Notification.Name?, object: AnyObject? = nil) -> Signal<Notification, NoError> {
26+
return Signal { [base = self.base] observer in
3827
let notificationObserver = base.addObserver(forName: name, object: object, queue: nil) { notification in
3928
observer.send(value: notification)
4029
}
4130

42-
disposable += {
31+
return ActionDisposable {
4332
base.removeObserver(notificationObserver)
4433
}
4534
}

Tests/ReactiveSwiftTests/FoundationExtensionsSpec.swift

Lines changed: 42 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -23,11 +23,11 @@ class FoundationExtensionsSpec: QuickSpec {
2323
describe("NotificationCenter.reactive.notifications") {
2424
let center = NotificationCenter.default
2525

26-
it("should send notifications on the producer") {
27-
let producer = center.reactive.notifications(forName: .racFirst)
26+
it("should send notifications on the signal") {
27+
let signal = center.reactive.notifications(forName: .racFirst)
2828

2929
var notif: Notification? = nil
30-
let disposable = producer.startWithValues { notif = $0 }
30+
let disposable = signal.observeValues { notif = $0 }
3131

3232
center.post(name: .racAnother, object: nil)
3333
expect(notif).to(beNil())
@@ -36,26 +36,54 @@ class FoundationExtensionsSpec: QuickSpec {
3636
expect(notif?.name) == .racFirst
3737

3838
notif = nil
39-
disposable.dispose()
39+
disposable?.dispose()
4040

4141
center.post(name: .racFirst, object: nil)
4242
expect(notif).to(beNil())
4343
}
4444

45-
it("should send Interrupted when the observed object is freed") {
46-
var observedObject: AnyObject? = NSObject()
47-
let producer = center.reactive.notifications(forName: nil, object: observedObject)
48-
observedObject = nil
45+
it("should be freed if it is not reachable and no observer is attached") {
46+
weak var signal: Signal<Notification, NoError>?
47+
var isDisposed = false
4948

50-
var interrupted = false
51-
let disposable = producer.startWithInterrupted {
52-
interrupted = true
53-
}
54-
expect(interrupted) == true
49+
let disposable: Disposable? = {
50+
let innerSignal = center.reactive.notifications(forName: nil)
51+
.on(disposed: { isDisposed = true })
5552

56-
disposable.dispose()
53+
signal = innerSignal
54+
return innerSignal.observe { _ in }
55+
}()
56+
57+
expect(isDisposed) == false
58+
expect(signal).toNot(beNil())
59+
60+
disposable?.dispose()
61+
62+
expect(isDisposed) == true
63+
expect(signal).to(beNil())
5764
}
5865

66+
it("should be not freed if it still has one or more active observers") {
67+
weak var signal: Signal<Notification, NoError>?
68+
var isDisposed = false
69+
70+
let disposable: Disposable? = {
71+
let innerSignal = center.reactive.notifications(forName: nil)
72+
.on(disposed: { isDisposed = true })
73+
74+
signal = innerSignal
75+
innerSignal.observe { _ in }
76+
return innerSignal.observe { _ in }
77+
}()
78+
79+
expect(isDisposed) == false
80+
expect(signal).toNot(beNil())
81+
82+
disposable?.dispose()
83+
84+
expect(isDisposed) == false
85+
expect(signal).toNot(beNil())
86+
}
5987
}
6088
}
6189
}

0 commit comments

Comments
 (0)