NSNotification not working from another class - ios

Part of an app I'm working on involves putting a blank screen over the current content if the user becomes inactive. As such after x seconds a blank page view controller is opened:
(From within ViewController.m and triggered by detecting an NSNotification from ScreenBlank.m)
UIPageViewController *blankPage = [self.storyboard instantiateViewControllerWithIdentifier:#"BlankPageViewController"];
[self presentViewController:blankPage animated:YES completion:nil];
This blank screen is then removed when a user touches the screen (And thus confirms their activity), like so:
(From inside the touchesBegan callback of ViewController.m)
[self dismissViewControllerAnimated:YES completion:nil];
The problem I'm having, is that I now want to trigger removing this screen blanking elsewhere, such as when the user logs out. This is done by removing a card from a plugged in card-reader, and means that it is called from a separate class (And one that is instantiated from within ActionMgr.m).
The class in question (CardWatcher) is created like so:
CardWatcher *newInstance = [[CardWatcher alloc] init];
[newInstance StartCardChecker];
And that instance of CardWatcher issues a notification when the card is removed, like so:
[[NSNotificationCenter defaultCenter] postNotificationName:#"logout" object:nil];
This notification is then listened for in ViewController, and on being triggered, executes EXACTLY the same code as is used to blank the screenearlier:
[self dismissViewControllerAnimated:YES completion:nil];
By using logging, I have determined that the notification is being received fine, and is executing the dismissViewControllerAnimated code, but for some reason, said code is not actually dismissing the view controller.
The only reason I can think of, is that it is (In the long run) called form within an instance of a class, but even then it's being passed via an NSNotification, and so in my eyes its source should be irrelevant?
Any help would be much appreciated!

It sounds like the notification is not generated on the main thread and you are hence trying to dismiss the page from a secondary thread. Make sure that you are posting the notification on the main thread and not from secondary threads. When a notification triggers UI events, such as this one, the code that manipulates the UI is required to be executed on the main thread.
Delivering Notifications To Particular Threads
Regular notification centers deliver notifications on the thread in
which the notification was posted. Distributed notification centers
deliver notifications on the main thread. At times, you may require
notifications to be delivered on a particular thread that is
determined by you instead of the notification center. For example, if
an object running in a background thread is listening for
notifications from the user interface, such as a window closing, you
would like to receive the notifications in the background thread
instead of the main thread. In these cases, you must capture the
notifications as they are delivered on the default thread and redirect
them to the appropriate thread.
Just try the following code and let me know if it worked for you.
dispatch_async(dispatch_get_main_queue(),^{
[[NSNotificationCenter defaultCenter] postNotificationName:#"logout" object:nil];
});

Related

Using NSNotificationCenter to call method in main app from extensions widget?

I have a widget that I would like to call back to my main app so as to make a call to the server to update data. I looked into delegation, but registering the widget's view controller as a delegate didn't seem very practical. So I moved on to trying to use NSNotificationCenter. I have set it up, but the selector is not being called. In my main iOS app I have this in the viewDidLoad method.
[[NSNotificationCenter defaultCenter] addObserver:self
selector:#selector(loadNewData:)
name:kUpdateData
object:nil];
And at the bottom of that file I have this:
/**
* Updates the table when the today widget is called for updated info
**/
- (void)loadNewData:(NSNotification *) notification
{
[self loadTableData];
}
That's in my main app. Now, in my notification center widget/extension, I make this call:
[[NSNotificationCenter defaultCenter]
postNotificationName:kUpdateData
object:nil];
The postNotificationName being passed in, `kUpdateData', is a constant that is resolved to #"updateData". I can see in the debugger that the postNotificationName method is being called, but the main app is not responding to it (regardless of it is in the foreground or the background). What am I doing wrong?
As a side note, the only reason I am doing this is to remove the need for repetitive code and re-implementing things I have already made.
As far as i know extension cant access or call main app methods... what can do is either do the server execution in extension or set a value in shared NSUserDefault so when your app is brought to foreground you can check this value and connect with server accordinly.

Delay NSNotification until another is complete

I have a global function that downloads JSON data from a server and parses it. It normally takes about 5 seconds to run, so we do this in the background and then send out a notification, kNotificationDownloadSuccessful.
One of the several observers of the notification is a MapView, who re-draws annotations based on the data it received. This, obviously, has to happen after it's told the data is ready by receiving kNotificationDownloadSuccessful.
In some cases I want to re-center the map on the new data. This, obviously, cannot happen until the map has completed drawing annotations. So I implemented a second notification, kNotificationMapLocationSet, which the callers fire if they want this to occur, and the map listens for it.
Now the problem... kNotificationMapLocationSet is being received before kNotificationDownloadSuccessful, which makes perfect sense really. The Map can't simply re-post it, or just call it's local re-center method, because it doesn't know if the recenter is required.
I thought about having the kNotificationMapLocationSet set a flag in the Map, and then have the handler on kNotificationDownloadSuccessful look at it and recenter if desired. But then that would fail in the case where the messages are received in their current order.
So is there a way to order the notifications? IE, delay this one until that one fires?
I believe you can use addObserver to check when the NSNotification is done. Let me know if this works:
NotificationCenter defaultCenter] addObserver:self
selector:#selector(callNextNotification:) name:#"notificationSent" object:nil];
It looks like you subclassed NSNotification, but this should still work.

TableViewController property changing whilst app is in the background

I'm slowly working on my first simple timer app, which is starting to slowly come to life. At the moment I've done no saving of data for when my app enters the background, for the moment whilst writing my app it is always staying loaded during my testing, even if its in the background for a small time. All this is being done on the simulator.
My problem is that I have a Table View Controller with an NSInteger index property
#property NSInteger index;
that I use to manage the slow iteration step by step over an NSArray over time, by calling a doWork method.
Initial setup of my TVC and this property is performed as the view is prepared for segue from another view. More work is done on this property and other properties whenever my app receives local notifications or upon observing UIApplicationWillEnterForegroundNotifications.
All accessing of this property I've added debug logging around and I'm just confused with what I'm seeing
PrepareForSegue
# 19:38:29:058 - calls a method doWork which increments my property from -1 to 0, and sets a local notification. Logs show self=0x10bb661f0
I press the home button to put my app in the background for ~10 secs
The local notification fires and I tap the banner to bring my app back to the focus
My TVC's awakeFromNib function sets up a notification observer
as follows
NSOperationQueue *mainQueue = [NSOperationQueue mainQueue];
[[NSNotificationCenter defaultCenter] addObserverForName:UIApplicationWillEnterForegroundNotification
object:nil
queue:mainQueue
usingBlock:^(NSNotification *note) {
[self doWork];
}];
This notification fires twice! (for some reason?) each time causing my doWork method to be called
# 19:38:45:832 - doWork immediately shows my property is now back to -1 not the 0 it was set to before being backgrounded, it is incremented again to 0. Also it shows self as different at self=0x10974bbd0, a value it remains at now whilst the app is foregrounded.
# 19:38:45:836 - doWork is called again from this 2nd notification call, my property is correctly now still 0 from the last call, it is incremented again to 1.
after that my app delegate also gets it's didReceiveLocalNotification method called, which will also end up calling the same doWork method via another block in a Notification Center observer setup the same as the one detailed above.
# 19:38:45:857 - doWork is called and again the property has gone from the 1 that was set, back to 0... and is again incremented back to 1.
I just can't understand what's going on. Most of the properties in my TVC are still presumably fine as the rest of my logic showing the TVC contents continues to work fine. Why is my NSInteger getting mangled so?
I thought perhaps there could be some issues with threading and the local notification handling occurring perhaps simultaneously, I hoped that adding the mainQueue would help with this, I'd previously got this set to nil. Sadly it didn't make any difference.
I wonder why self changes after being backgrounded for only a short amount of time. Naively I presumed that as everything seemed to be working on coming back to the foreground (the view still displayed, with all its data seemingly intact aside from this NSInteger property) that the self ptr and object would be the same. It's not too much to imagine that perhaps its been relocated by the OS somehow, but still this shouldn't have caused the property to change.
I'm using cocoa lumberjack for logging, and I've turned off ASYNC logging as follows
#define LOG_ASYNC_ENABLED NO
Which should at least mean that log calls block till they've logged. I could appreciate that if i did have some threading issues perhaps the logging order would be slightly in doubt. But for the initial corruption of the property described above after the app enters the background there is ~10 seconds from when I write 0 to the property, and later read -1 back out of it. This is clearly not a threading timing issue at that point.
Why am I notified about entering the foreground twice? If my properties were left where I set them it wouldn't matter much, but still strikes me as a little odd.
Is there a problem with my using self within this block, I've seen that sometimes you may want to use a weak self pointers for these blocks, but I've seen examples where self is used directly as well.
Any help on understanding and hopefully fixing this would be really appreciated. I'm a little stuck and can't see what I've done wrong!
Cheers
So the answer to my problem as figured out by Woofbeans and Phillip Mills is that my awakeFromNib was being called each time i entered this TVC, and also I was failing to removeObserver on these notifications causing the stale undisplayed TVCs to stay around indefinitely. I'd not realised this key part of my repro of the problem.
Go into the TVC, come out, go back in and then you would have a duplicate of alerts in the app being caused by the fact that the original TVC was still around, being held onto by the strong reference to self within my own blocks.
I'm going to refactor this doWork functionality into my model so that it can persist and be handled better, independently of whatever view happens to be being displayed.
I also changed my block code to use a weakself pointer within the block, to hopefully stop that block from causing any object to persist purely because of the block being left behind.
Cheers everyone!

Differentiate when UIApplicationDidBecomeActiveNotification is being called

In my app, if UIApplicationDidBecomeActiveNotification is dispatched I present a modal security unlock view.
Everything is working fine except UIApplicationDidBecomeActiveNotification seems to get called whenever the iOS shows the "The App would like to use your current location" popup.
Is there a way to differentiate when UIApplicationDidBecomeActiveNotification is being called?
I need to show the security unlock view when the app returns from the background.
Look at using the UIApplicationWillEnterForegroundNotification notification instead.

The correct way to dismiss a MFMailComposeViewController

I'm at my wit's end. I need to dismiss an MFMailComposeViewController when my app transitions to the background and I can't do it. It ends up creating an awkward application state.
Is there a way to handle this programmatically? Perhaps force the view controller to put the email into the Drafts folder and dismiss without animating?
EDIT:
Calls to - dismissModalViewControllerAnimated: don't work as expected.
The awkward application state I'm talking about is my main view being redrawn over top of the email composer when the application returns from the background. The modal is never dismissed and that email composer is never accessible again.
EDIT:
Code in my initializer:
// For compatibility with iOS versions below 4.0
if (&UIApplicationDidEnterBackgroundNotification != NULL)
{
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(applicationDidEnterBackgroundNotification:) name:UIApplicationDidEnterBackgroundNotification object:nil];
}
Code in my background-entry handler:
- (void) applicationDidEnterBackgroundNotification:(NSNotification *)note {
// Do some other stuff here
// According to the docs, calling the method like this closes all
// child views presented modally
[self dismissModalViewControllerAnimated:NO];
}
I have reproduced a simple application with the code you have above. The mail composer is dismissed properly when the application enters the background.
I can only assume therefore that the //Do some other stuff here section of your code is doing too much stuff and the OS is shutting you down before you have chance to dismiss the composer.
According to the docs:
You should perform any tasks relating to adjusting your user interface before this method exits but other tasks (such as saving state) should be moved to a concurrent dispatch queue or secondary thread as needed. Because it's likely any background tasks you start in applicationDidEnterBackground: will not run until after that method exits, you should request additional background execution time before starting those tasks. In other words, first call beginBackgroundTaskWithExpirationHandler: and then run the task on a dispatch queue or secondary thread.
Perhaps you should move your other stuff to a different thread or request extra time? If you remove the other stuff, does the composer dismiss correctly?

Resources