19

How can I create a signal out of a notification name? For example, I want to go from:

[[NSNotificationCenter defaultCenter] addObserver:self
                                         selector:@selector(userDidChange:)
                                             name:kTTCurrentUserLoggedOffNotification
                                           object:nil];

to something like:

[signalForName(kTTCurrentUserLoggedOffNotification) subscribeNext:^(id x){
...
}];
Kreiri
  • 7,840
  • 5
  • 30
  • 36
meisel
  • 2,151
  • 2
  • 21
  • 38

3 Answers3

46

-[NSNotificationCenter rac_addObserverForName:object:] returns an infinite signal. You can subscribe to it like this

Objective-c

[[[[NSNotificationCenter defaultCenter] rac_addObserverForName:UIApplicationDidEnterBackgroundNotification object:nil]
  takeUntil:[self rac_willDeallocSignal]]
  subscribeNext:^(id x) {
     NSLog(@"Notification received");
}];

Swift

NSNotificationCenter.defaultCenter()
  .rac_addObserverForName(UIKeyboardWillShowNotification, object: nil)
  .takeUntil(self.rac_willDeallocSignal())
  .subscribeNext { (_) in
     print("Notification received")
  }

This signal is as stated infinite. If you need this signal/subscription to be bound to the lifetime of self you can add takeUntil: with rac_willDeallocSignal like this:

Objective-c

[[[[NSNotificationCenter defaultCenter] rac_addObserverForName:UIApplicationDidEnterBackgroundNotification object:nil]
  takeUntil:[self rac_willDeallocSignal]]
  subscribeNext:^(id x) {
     NSLog(@"Notification received");
}];

Swift

NSNotificationCenter.defaultCenter()
  .rac_addObserverForName(UIKeyboardWillShowNotification, object: nil)
  .takeUntil(self.rac_willDeallocSignal())
  .subscribeNext { (_) in
     print("Notification received")
  }
hfossli
  • 22,616
  • 10
  • 116
  • 130
  • This seems to work, for sure. But if it has to be done like this, then the method is fundamentally flawed. Signals live until there is a reference to them. If the signal is released, then it should be disposed which in turn removes the observer. You may look around in your code, there may be a strong reference to the signal that keeps it from being deallocated. Or if this is not the case, then please report an issue to the project as this is not the expected behavior in my opinion. – allprog Nov 14 '13 at 16:14
  • 4
    @allprog [Subscribers retain their signals](https://github.com/ReactiveCocoa/ReactiveCocoa/blob/master/Documentation/MemoryManagement.md) until completion, error, or disposal. That's why infinite signals can get pretty gnarly. `-takeUntil:` is a good solution, or something similar that deterministically terminates the signal at a certain point. – Justin Spahr-Summers Nov 14 '13 at 21:26
  • 2
    @JustinSpahr-Summers Thanks for the clarification. I knew I was too eager to shout the wolf! But you're there to correct me. :) Could the documentation contain these intricacies? E.g. "this is an infinite signal" and give a pointer to some description that shows what one needs to take into consideration. I'll be honest, the framework seems to have some of these "booby traps" and it would be better if the specifics of the returned signal in these cases were emphasized. – allprog Nov 14 '13 at 22:32
  • 1
    @allprog That's a fair point. Filed [an issue](https://github.com/ReactiveCocoa/ReactiveCocoa/issues/945) about it. – Justin Spahr-Summers Nov 14 '13 at 22:47
10

In the RACExtensions you can find the NSNotificationCenter (RACSupport) category. That has a method for this purpose:

- (RACSignal *)rac_addObserverForName:(NSString *)notificationName
                               object:(id)object;
allprog
  • 16,540
  • 9
  • 56
  • 97
  • 1
    I experience this is never removed.. Do we need to dispose this signal ourselves? – hfossli Nov 14 '13 at 14:46
  • The observation is tied to the signal. If that is disposed, then the observer is removed. Can it be that you are keeping strong reference to the signal somewhere? (This is the tricky part of ObjC :) ) – allprog Nov 14 '13 at 15:36
  • 2
    Found out :) see new answer – hfossli Nov 14 '13 at 15:51
-1

Swift version using ReactiveCocoa 4.1:

NSNotificationCenter.defaultCenter()
      .rac_addObserverForName(UIKeyboardWillShowNotification, object: nil)
      .takeUntil(self.rac_willDeallocSignal())
      .subscribeNext { (_) in
          print("UIKeyboardWillShowNotification")
      }
Fabio
  • 1,757
  • 19
  • 33