Conditions
Getting a notification (txt message)
or Opening/Closing Notification Center
or Switch to another app, come back
Basically, the app comes back from background for a short period.
Issue
Layout is ok, but some of my properties that I get from CoreData are empty after resuming from Background. This issue is there on pretty much all of my view controllers.
The project
This is an app that has a main tabbar controller, with two navigationcontrollers within the tabs and maybe two levels of viewcontrollers, that have themselves child UIViews (that use some of the informations). The back-end is made of Parse and CoreData.
The weird part
Back from background -> properties are ok on viewWillAppear (Create a backup of the id) -> they are nil seconds after -> I need to manually get them back (from the ID I just stored)
Here is a screenshot, when putting a break point within a function called every 5sec to check the current time (link to bigger) :
What I did
NSCoder implemented for state restoration and every view controllers have a restoration ID, however it doesn't get called when app is becoming active. I don't think NSCoder is the issue here since from the documentation it is used when the OS will kill it on its own, or a force quit from the user.
I tried to manually refresh the content in the appropriate ViewControllers from NSNotificationCenter if the NSManagedObjects are nil, but it is not future-proof, and is never the same logic on each view.
Any thoughts? Thank you!
My AppDelegate :
- (void)applicationWillResignActive:(UIApplication *)application
{
NSLog(#"Will Resign Active");
}
- (void)applicationDidEnterBackground:(UIApplication *)application
{
NSLog(#"Entered Background");
}
- (void)applicationWillEnterForeground:(UIApplication *)application
{
NSLog(#"Will enter Foreground");
}
- (void)applicationDidBecomeActive:(UIApplication *)application {
[[NSNotificationCenter defaultCenter] postNotificationName:#"needsRefresh" object:self userInfo:nil]; // Helpful in some viewcontrollers to save variables that are not nil.
[PF_FBSession.activeSession handleDidBecomeActive];
}
- (void)applicationWillTerminate:(UIApplication *)application
{
NSLog(#"Will Terminate");
}
For those finding this thread, here is the solution I found.
My issue and answer was specific to my project.
I had another controller, the one that takes care of all my database connections, that was listening to applicationDidBecomeActive.
This was calling up a refresh of my data, but also a "cleanup", that was deleting/editing some NSManagedObjects, then saving.
Conclusion : the memory address wasn't the same, the object wasn't considered the same, and therefore was empty on my current page.
I fixed it by stopping cleaning up on each AppDidBecomeActive, but move that logic to AppDidFinishLaunching instead.
Hope that helped!
Related
How can I ensure my table data function is called only once when app is displayed again (from background start, or launch)?
That is, I want to have my table data refreshed when the user clicks on the application icon, and only once. So the requirement would be:
a) update method in controller called only once after application icon is clicked by user
b) to be valid irrespective of whether the user is (i) starting app for the first time, (ii) coming back from background, (iii) any other means of the user requesting the app come back to the foreground.
The issue I currently have with my implementation is that the data update is being called twice in the case where the app is coming back from background, so I basically want to improve on this. The way I'm doing currently, which is flawed is:
- (void)updateEventData {
// UPDATE CODE HERE
}
- (void)becomeActive:(NSNotification *)notification {
[self updateEventData];
[self.tableView reloadData];
}
- (void)viewDidLoad {
[super viewDidLoad];
[self updateEventData];
// Auto Refresh Data - Handle Case where App becomes active from background & you want to refresh data
[[NSNotificationCenter defaultCenter] addObserver:self
selector:#selector(becomeActive:)
name:UIApplicationDidBecomeActiveNotification
object:nil];
}
You can implement it in following way:
1) Take one variable in NSUserDefault.
2) Set it's value FALSE when app is launch for the first time.
3) Get the value of NSUserDefault variable in respective view controller. And if it's value if FALSE then call update method.
4) Now, when app become Active, then set the variable value TRUE.
5) Get it's value in respective view controller. And if's value is TRUE then don't call update method.
6) So, update method will be called only once.
Hope it will be helpful to you. May be there is some minor changes.
Let me know in case of any difficulty.
Well what i understand from your question that you only want to update the table data when you start your application by clicking the app icon on the iPhone.Well First understand the execution of the app, the main function is called first and then in your app delegate
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
// Override point for customization after application launch.
[self postNotification];
[self.window makeKeyAndVisible];
return YES;
}
you can post the notification in this method as its called once.
And more over if you want to do this by your own way in the ViewDidLoad method, then i think you must also remove the observer of notification,i think that may be the reason that its being called twice,because you are not removing notification.
As part of writing this question and reviewing answers I think I've got probably a good solution...
Create an instance variable/property "initialLoad"
Set it to TRUE first thing in viewDidLoad
Then in the becomeActive method do the following:
Code:
- (void)becomeActive:(NSNotification *)notification {
if (!self.initialLoad) {
[self updateEventData];
[self.tableView reloadData];
}
self.initialLoad = false;
}
Seems to work ok...
When the iPhone application is running in background and it receive a remote notification. So it will execute the didReceiveRemoteNotification call back. In that I am going to push to a new UIViewController. But before that its noticed that its calling the applicationWillEnterForeground callback.
In that I am also doing some location update using a modal dialog. So when this notification arrives this both scenarios happens and the app is getting crashed. So is there any way to block the applictiaonWillEnterBackground processing on remote notification. As the moment is little bit hard cos this processing is done after applicationWillEnterBackground controller.
Thank you.
The callback application:didReceiveRemoteNotification: should only be invoked when the application is running in the foreground. When running in the background you should instead get a call to application:didFinishLaunchingWithOptions:.
Since you are asking the question and also using core location it might be that application:didReceiveRemoteNotification: is called when the application is in the background but I would think that would be a bug. At least according to Apple's documentation.
Anyway, NO, you can't block applicationWillEnterForeground:. Without knowing exactly what you are doing in the different callbacks I would recommend that you set a flag in applicationWillEnterForeground: if you are doing something there and then check that flag in application:didReceiveRemoteNotification:
- (void)applicationWillEnterForeground:(UIApplication *)application {
if (somehingHappend) {
somethingHappended = YES;
}
}
- (void)application:(UIApplication *)application didReceiveRemoteNotification:(NSDictionary *)userInfo {
if (!somethingHappened) {
// push your view controllers or whatever
}
}
Where somethingHappened is a BOOL defined in the same class as an ivar.
Using iPhone SDK 4.1. Sometimes when returning from the background state on iPhone 3GS device the view returned to has lost one of its images or labels. The viewDidAppear method
doesn't get called either when returning from the background. Is there any way to force
reloading the view so these methods are called?
This usually happens if your app receives a memory warning as a result of trying to store too much data in ram (in this case images).
To test whether this is the case you could either do an NSLog call inside the didReceiveMemoryWarning message or you could subclass UIImage and extend its dealloc and put an NSLog message inside it saying something like "Image being dealloced" and then check what gets written to the console. If you want to check it without being in the debugger (and therefore no console) you could create a debug UILabel inside the mainwindow xib at the very front (so its always visible) whose text value is set instead of writing to NSLog. This way youll be able to see what happened even after you return to your program.
Your best bet is the didReceiveMemoryWarning together with a UILabel object.
- (void)applicationDidReceiveMemoryWarning:(UIApplication *)application
{
debugLabel.text=#"Did receive memory warning";
}
To solve the problem (i.e. to reload the images) you could register that view to receive
the UIApplicationWillEnterForegroundNotification from the notification center and then call the necessary reloading calls i.e. check which images are nil (have been released) and should be reloaded.
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(viewNeedsReload) name:UIApplicationWillEnterForegroundNotification object:nil];
- (void) viewNeedsReload
{
//Check validity of each image here and reload if necessary
}
I am looking to save some settings when my application exits and I am a little confused about the 2 different versions below. My feeling is that to better fit the MVC pattern I should be using the version in the viewController. I am just curious as most folks don't seem to do much in the appDelegate when that call would be used?
AppDelegate
-(void)applicationWillTerminate:(UIApplication *)application {
NSLog(#"_deli: applicationWillTerminate");
}
ViewController
-(void)applicationWillTerminate:(NSNotification *)notification {
NSLog(#"_view: applicationWillTerminate");
}
many thanks
EDIT_001:
Sorry, I should claifiy, you would also need to add (see below) to the ViewController to make the above work.
UIApplication *app = [UIApplication sharedApplication];
[[NSNotificationCenter defaultCenter] addObserver:self
selector:#selector(applicationWillTerminate:)
name:UIApplicationWillTerminateNotification
object:app];
gary
Use whichever one has access to the data you want to save. So if the ViewController can see the data but the AppDelegate can't, use that.
Well, to flog my personal hobby horse, I would say that settings are a form of preferences that should be saved in a dedicated data model. NSUserDefaults, for example, is a data model built on the singleton pattern. You could, of course roll your own. There is no problem with having multiple data models in the same app if they manage unrelated information.
The key is to save defaults/preference/state as they are made. Then when the application quits the defaults are already automatically saved. Remember that on the iPhone you never know when the app will be interrupted or quit. Save as you go is really the only option.
Also, in the code examples you provided, how will the view controller know when the application quits? IIRC, UIViewController does not have a applicationWillTerminate: method and does not automatically receive a specific app will quit message. (Edit: In the comments, KennyTM points out that the view controller can register and listen for UIApplicationWillTerminateNotification) You would have to put this functionality in -viewWillUnload. Otherwise, you would have to track your view controllers from the app delegate have the delegate send the active view controller a message when the app quit.
I want to implement this function on my apps but i cant seem to figure out how to use this line of codes.
- (void)applicationWillResignActive:(UIApplication *)application {
//our app is going to loose focus since there is an incoming call
[self pauseGame];
}
- (void)applicationDidBecomeActive:(UIApplication *)application{
//the user declined the call and is returning to our app
[self resumeGame];
}
I've read that this must be placed in appdelegates but i cant seem to figure out how could i call my pause action when the game is currently in the viewcontroller.
Thank you.
Instead of sending the messages to self (which is the app delegate), you would send them to your view controller.
For example, if your app delegate had a property for your main game view controller named "gameViewController" (where the methods to pause and resume were implemented):
- (void)applicationWillResignActive:(UIApplication *)application {
// our app is going to loose focus since there is an incoming call
[self.gameViewController pauseGame];
}
- (void)applicationDidBecomeActive:(UIApplication *)application {
// the user declined the call and is returning to our app
[self.gameViewController resumeGame];
}
I know that this was answered a long time ago. But I wanted to add that another (more scalable) solution is to have interested parties (e.g., UIViewControllers) register an interest in UIApplicationDidEnterBackgroundNotification and UIApplicationWillEnterForegroundNotification.
This approach has the advantage that the Application Delegate doesn't need to have direct knowledge of the objects that might need to respond to entering background/foreground.