ios notification, define handler function directly - ios

I want to do some actions when receiving "ready" notification.
Basically, we do :
// earlier in a method
...
[[NSNotificationCenter defaultCenter]
addObserver:self
selector:#selector( notificationHandler )
name:#"ready"
object:nil];
// later in file
- (X) notificationHandler{
...
}
In my case, the method which will handle the notification will be an one-line standing function, so if possible, I would like to define it right in the observer block.
[[NSNotificationCenter defaultCenter]
addObserver:self
selector:#selector( I WANT TO DEFINE A FUNCTION HERE )
name:#"ready"
object:nil];
Any idea ?

Just to let you know that exist this method:
[[NSNotificationCenter defaultCenter] addObserverForName:#"ready"
object:nil
queue:nil
usingBlock:^(NSNotification *note) {
//Do something
}];
and also that you are talking about write a function or a block in a #selector directive...doesn't make sense and is not possible.
Enjoy the function above ;)

Even if there are some official APIs to do this, I strongly suggest you to NOT use them, cause they're leaked. You can use FXNotifications from Nick Lockwood, which provides the feature you want: use a block as notification listener.
Add the .h & .m files inside your project and then call:
[[NSNotificationCenter defaultCenter] addObserver:self
forName:#"ready"
object:nil
queue:[NSOperationQueue mainQueue]
usingBlock:^(NSNotification *note, id observer) {
// Your inline function here
}];

Related

Why doesn't adding observer for a notification with blocks work?

I have this method to get shift my text fields when they keyboard appears:
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(keyboardWillShow:) name:UIKeyboardWillShowNotification object:nil];
- (void)keyboardWillShow:(NSNotification *)notification {
self.scrollView.contentOffset = CGPointMake(0.0f, keyboardShift);
}
Then I tried to use the block method instead:
[[NSNotificationCenter defaultCenter] addObserverForName:UIKeyboardWillShowNotification object:nil queue:nil usingBlock:^(NSNotification *note) {
self.scrollView.contentOffset = CGPointMake(0.0f, keyboardShift);
}];
However, using this method I set a breakpoint inside the block but it's not being called. Is there anything I'm missing? Why doesn't this method work but the other one does?
What if you try replacing the nil parameter for the queue with [NSOperationQueue mainQueue]
Also, if you go with the block API, make sure you keep a reference to the token returned so that you can remove the observer at the right time later on.
addObserverForName returns an object that you should hold on to. From the header:
The return value is retained by the system, and should be held onto by the caller in order to remove the observer with removeObserver: later, to stop observation.
See the example in the documentation.

iOs Remove Observers from ViewController on TabBarController

I have a UITabBarController with some tab and in all of them i have:
-(void)viewDidappear:(BOOL)animated{
......
[[NSNotificationCenter defaultCenter] addObserverForName:kNotificationName object:nil queue: nil, usingBlock{...}
}
and
-(void)viewDidDisappear:(BOOL)animated{
......
[[NSNotificationCenter defaultCenter] removeObserver:self];
}
In another class i post the notification with name kNotificationName:
[[NSNotificationCenter defaultCenter] postNotification:kNotificationName object:nil];
I've set some log on all of this methods, and the order they are called is correct but...if i switch from First to Second tab (and the notification is posted), the first and the second tab receive the notification (but the viewDidDisappear of the first tab is called!).
If from second tab i go to third tab, the first, second, and third tab receive the notification.
I've tried to use:
[[NSNotificationCenter defaultCenter] removeObserver:self name:postNotification:kNotificationName object:nil];
but the behaviour is the same. All observer are notified.
EDIT1:
As suggest to the other topic i've moved all in viewWillAppear: and viewWillDisappear:, but this haven't have any effect.
I've tried to remove the observer after have received the notification, like this:
-(void)viewWillAppear:(BOOL)animated
{
[super viewWillAppear:animated];
[[NSNotificationCenter defaultCenter] addObserverForName:kDLSyncEngineSyncCompletedNotificationName object:nil queue:nil usingBlock:^(NSNotification *note) {
....
[[NSNotificationCenter defaultCenter] removeObserver:self name:kDLSyncEngineSyncCompletedNotificationName object:nil];
}];
}
But, using this (bad) approach too, the notification is received to the first tab too (i've added this code only on the first tab, to check if after press the second tab, the first tab is receiving again the notification).
EDIT2 - SOLVED - (but what is the difference?)
Instead of using *addObserverForName:object:queue:usingBlock*: I've used *addObserver:selector:name:object:* and in this way all works.
Apple documentation say this for the method with usingBlock:
The block is copied by the notification center and (the copy) held
until the observer registration is removed.
Sure, I've called removeObserver....
Inside the block in the addObserver method -
usingBlock:^(NSNotification *notification){
/*
do something
*/
[[NSNotificationCenter defaultCenter] removeObserver:self];
}];
As you might have already checked this in documentation -
Be sure to invoke removeObserver: or removeObserver:name:object: before notificationObserver or any object specified in addObserver:selector:name:object: is deallocated.

iOS NSNotificationCenter Observer not being removed

I have the following code within AppDelegate. The purpose being to create a couple of observers, and then call some code. Once that code completes it then posts a notification, and the observer should then remove both observers and call the completion handler.
My issue is that it appears that the observers are not being removed as I expected. The notification is posted, and the NSLog entry is written to console, so I know that the observer is working. However, on the second time of calling, the NSLog is called twice, third time three times etc.
My thoughts are that this is to do with the removal being within the block of code that is running from the observer, however, I am unsure how I can resolve this (if indeed this is what the issue is).
Could someone be so kind as to explain how I can achieve this?
Thanks.
-(void) application:(UIApplication *)application performFetchWithCompletionHandler:
(void (^)(UIBackgroundFetchResult))completionHandler {
[[NSNotificationCenter defaultCenter] addObserverForName:#"fetchDidCompleteNewData" object:nil
queue:nil usingBlock:^(NSNotification *completed) {
//Remove Observers
[[NSNotificationCenter defaultCenter] removeObserver:self
name:#"fetchDidCompleteNewData"
object:nil];
[[NSNotificationCenter defaultCenter] removeObserver:self
name:#"fetchDidCompleteNoData"
object:nil];
// Post completion
completionHandler(UIBackgroundFetchResultNewData);
NSLog(#"Background fetch completed... New Data");
}];
[[NSNotificationCenter defaultCenter] addObserverForName:#"fetchDidCompleteNoData" object:nil
queue:nil usingBlock:^(NSNotification *completed) {
//Remove Observers
[[NSNotificationCenter defaultCenter] removeObserver:self
name:#"fetchDidCompleteNoData"
object:nil];
[[NSNotificationCenter defaultCenter] removeObserver:self
name:#"fetchDidCompleteNewData"
object:nil];
//post completion
completionHandler(UIBackgroundFetchResultNoData);
NSLog(#"Background fetch completed... No New Data");
}];
GetDetails *getDetails = [[GetDetails alloc] init];
[getDetails backgroundRefresh];
}
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
[[UIApplication sharedApplication] setMinimumBackgroundFetchInterval:UIApplicationBackgroundFetchIntervalMinimum];
return YES;
}
You are not registering self as an object. Moreover, when the block is pushed onto the stack by addObserverForName: the method has not yet returned so the notification is nil.
Make a global object using block, eg
__block __weak id notification;
then,
notification = [[NSNotificationCenter defaultCenter] addObserverForName:#"fetchDidCompleteNewData" object:nil queue:nil usingBlock:^(NSNotification *completed) {
//Remove Observers
[[NSNotificationCenter defaultCenter] removeObserver:notification];
}];
My thoughts are that this is to do with the removal being within the
block of code that is running from the observer, however, I am unsure
how I can resolve this (if indeed this is what the issue is).
Could someone be so kind as to explain how I can achieve this?
Certainly.
You can easily test your theory by not using the addObserverForName:object:queue:usingBlock: method and instead using the addObserver:selector:name:object: method, where the selector is the name of a function you call instead of using a block.
Simply use the API guide for NSNotificationCenter for details about these methods, or in general, since you question was about what other method you could use that does not require a block statement, consulting the API is the first place to check for alternative tools within the class.

How do I call multiple methods in #selector for notification?

Currently I'm only calling one method when application will enter foreground. How do I call various methods in #selector?
[[NSNotificationCenter defaultCenter] addObserver:self
selector:#selector(displayHappyFace)
name:UIApplicationWillEnterForegroundNotification
object:nil];
Just create a separate function for all your other function.
[NSNotificationCenter defaultCenter] addObserver:self
selector:#selector(AllFunction)
name:UIApplicationWillEnterForegroundNotification
object:nil];
All functions.
-(void) AllFunction
{
[self displayHappyFace];
[self otherFunction];
}
Add another observer to UIApplicationWillEnterForegroundNotification if you wish to keep the methods' logic separate:
[[NSNotificationCenter defaultCenter] addObserver:self
selector:#selector(displayHappyFace)
name:UIApplicationWillEnterForegroundNotification
object:nil];
[[NSNotificationCenter defaultCenter] addObserver:self
selector:#selector(callOtherMethod)
name:UIApplicationWillEnterForegroundNotification
object:nil];
#selector supports only one method. Remember to remove self as the observer before releasing its memory, to avoid messages being passed to a nil object.
You can only put one selector there.
Best practice is to create a new method called handleNotificationName: for each notification.
Example:
- (void)handleUIApplicationWillEnterForegroundNotification:(NSNotification *)aUIApplicationWillEnterForegroundNotification { }
This makes it really easy to figure out where your app handles each notification and makes code maintenance easy.
Inside the handler method you can call whatever methods you need to. You can have conditional logic also based on state you have or based the userInfo dictionary of the Notification ( if it has one ).
Don't forget to remove your notification observer in you object's dealloc method (at least, if not somewhere else because you might not want to always receive the notification depending on the use case)

NSNotificationCenter can lead to bugs. Do you know more elegant solutions?

I can add observer twice (by accident) to the notification center and I will get notifications twice.
Is it possible to get only one notification? Do you know more elegant solutions?
I show you this example because this may lead to bugs.
- (void)viewDidLoad
{
[[NSNotificationCenter defaultCenter] addObserver:self
selector:#selector(keyboardDidShow:)
name:UIKeyboardDidShowNotification
object:nil];
[[NSNotificationCenter defaultCenter] addObserver:self
selector:#selector(keyboardDidShow:)
name:UIKeyboardDidShowNotification
object:nil];
}
- (void)keyboardDidShow:(NSNotification *)ntf
{
}
If you're not sure if you added the observer somewhere else, you can use the following code everytime you're adding an Observer
[[NSNotificationCenter defaultCenter] removeObserver:self name:aName object:nil];
[[NSNotificationCenter defaultCenter] addObserver:self selector:aSelector name:aName object:nil];
This way you are removing the old one (if it existed) and adding a new one.
It's not 100% fail proof but it's a start. This could fail in Multi-Threaded apps where the calls are being made async or other unique situations.
You can also set an object to nil and then later use that object as if was still valid.
Not everything can be made fail safe.

Resources