Where to update the UI of a notification widget just once - ios

In my Notification Center Today Extension/Widget I need to update a portion of the UI every time Notification Center is activated. It never needs to update while Notification Center is in use, nor while it's in the background. In what method should I place that code?
viewDidLoad and viewWillAppear are both called every time it will be displayed, for example if you scroll up and down they will be called again, so that's too often.
widgetPerformUpdateWithCompletionHandler is not called at all before it's displayed for the first time it seems (at least with iOS 8.2 beta), and this method is automatically called whenever iOS feels like it to update the UI even when it's in the background which is not appropriate either.
loadView is only called a single time, never to be called again unless the widget is removed from memory. So if you open Notification Center and view the widget then dismiss Notification Center and reopen it later, it may not call that method again depending on whether or not it's been cleared from memory.

I'd just use viewDidLoad and not worry about the possibility of multiple calls. Unless the method takes a long time to run, there's no reason not to do it that way. [And if it does take a long time to run, your today extension is going to suck, so fix that.]
If for some reason you only want it to happen once, add a BOOL ivar to the class. Set it to YES in initWithCoder: Then in viewDidLoad, check that value. If it's YES, do your update and set the value to NO. If it's already NO, skip your update.

Related

Cancel a UILocalNotification with repeat interval

I'm doing an alarm application with repeat interval. My problem is that even if the user clicks the local notification, returns to the app, and then goes back to the home screen of the phone the UILocalNotification stills fire every minute. I'm aware of [[UIApplication sharedApplication] cancelAllLocalNotifications]; and I put it in the my viewDidLoad method. Any advice how I can fix this?
Putting it in viewDidLoad is good for some circumstances just not yours. Think about when viewDidLoad is called:
Called after the controller's view is loaded into memory.
So in other words, the next time it gets called is after your little ARC friend deallocates it from memory. So, yes, eventually cancelAllLocalNotifications will get called again, just not when a user puts the app in the background and then returns it to foreground, because its still has a home in memory; it will be called next time that particular view gets loaded into the memory.
Additionally, that probably is not good logic, because that will happen for every user, even if they didn't want to cancel the repeats.
Ultimately, you will have to create additional logic to decipher which users have, say, hit 'snooze' or 'cancel' with whatever resource works for you and your project. Personally, I would guide you towards using a category based notification, that way you cancel it as needed, instead of 'just in case'. See here how to set those up.

App view seems to reload back to default after some time

I've come across a strange error while programming my iPhone application. Basically when I leave my application in the background and then access it after a long time (i.e. the entire night while I'm sleeping), the viewDidLoad method seems to be called again even though I did not exit the app (I only double tapped the Home button or I tapped the Home button once) but still left the app in the background. However, if I leave the app on for a short period of time (anytime between a few minutes to a few hours), the viewDidLoad method is not called again and everything is as it should be. After doing some research, I found that it is because the viewDidUnload method is called (after the OS finds that the app is suspended for a long time), which calls viewDidLoad again when we bring the app back up. I found this out through this link: view seems to reload itself but it doesn't seem that there's a way to prevent viewDidLoad from being called when the viewDidUnload is called. Is there any way to prevent this viewDidUnload method from being called again? The thing is I want my app to be running for a long time in the background (i.e. a few days in the background) to collect data. Or, is there no way around this? Any help would be appreciated. Thanks!
EDIT: I have realized that after iOS 5, viewDidUnload is deprecated but this phenomenon still occurs. Any ideas on how to fix it? Thanks!
If you want to do stuff in the background you should look into background tasks.

How to frequently update today widget in notification center?

I am finding that my Notification Center widget does not reload very often. Because of this, out of date data being presented. I have not been able to find a way to get the widget to refresh every time I open the Today view. I have seen apps such as NBA Gametime that are able to update every time I pull down to see the widget.
How can I get this functionality in my app?
Based on my own experimentation and also reports in this SO thread, it appears that the TodayViewController.viewDidLoad() function gets called every time the Today/Notifications area gets opened. More specifically, #Karl Monaghan reported in a comment that the iOS 8.1 release notes have the following to say:
The schedule and intended use of widgetPerformUpdateWithCompletionHandler: is intended as a convenient home for all data/model update logic. If implemented, the system will call at opportune times for the widget to update its state, both when Notification Center is visible, as well as in the background. An implementation is required to enable background updates. It’s expected that the widget will perform the work to update asynchronously and off the main thread as much as possible.
So, to answer your question:
I don't think we'll ever really know how often the widgetPerformUpdateWithCompletionHandler method gets called, and I think Apple prefers it that way
The viewDidLoad function does get called every time the widget is displayed, so it's possible that you could put a call to the update function inside of that method. Or you could just put whatever updates you need to be shown in the widget inside of that function, although I don't know what kind of impact that will have on app performance.
Hope this helps.
Update
I've also found that either one of initWithCoder or initWithNibName seems to get called every time the widget is displayed on the screen, so it's possible that you could put calls to update your widget in those files, but if I recall correctly the view is not actually instantiated at this time so it's better NOT to put your code to update your widget in these functions.
In your widgetPerformUpdateWithCompletionHandler you need to to let your widget know that it needs to update. Apple Docs
-(void)widgetPerformUpdateWithCompletionHandler:(void (^)(NCUpdateResult))completionHandler {
// Perform any setup necessary in order to update the view.
// If an error is encoutered, use NCUpdateResultFailed
// If there's no update required, use NCUpdateResultNoData
// If there's an update, use NCUpdateResultNewData
completionHandler(NCUpdateResultNewData);
}

Does delegate have function which is executed every time app comes from background

I have application with lot of view controllers and on every time when app comes from background I have to make some request to server and reinitialise some global variables. At the moment I am doing this by repeating code in every view controller in didViewLoad but I wonder is there way to this in delegate to avoid repeating on 10 places ? ( I check didFinishLuanchingWithOptions in delegate but it is called only first time not when app comes from background ).
I think you want to look at these two:
applicationDidBecomeActive
And
applicationWillEnterForeground
Check out the documentation for more details.

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!

Resources