applicationWillTerminate, delegate or view? - iphone

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.

Related

App resumed from Background, nil property of NSManagedObject

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!

didFinishLaunching is never called

I created a new window-based project and couldn't figure out why it wasn't doing anything. Eventually I put an NSLog right after didFinishLaunching and it's never logged when I run it. Here is all of the code I have written:
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
NSLog(#"didFinishLaunching");
// Get the device object and turn proximity monitoring on
UIDevice *d = [UIDevice currentDevice];
[d setProximityMonitoringEnabled:YES];
// Get the NSNotificationCenter object
NSNotificationCenter *nc = [NSNotificationCenter defaultCenter];
// Sign up to receive notifications when the proximity state changes
[nc addObserver:self selector:#selector(proximityChanged:) name:UIDeviceProximityStateDidChangeNotification object:d];
NSLog(#"Observing...");
[self.window makeKeyAndVisible];
return YES;
}
- (void)proximityChanged:(NSNotification *)note {
// Print out the changes of proximity state
NSLog(#"Proximity Changed: %d", [[note object] proximityState]);
}
That's the entirety of what I've written and nothing is logged when I run it on the simulator or on my device. Any thoughts?
Well, you are talking about Application Delegate. The obvious reason - your object is not set as an application delegate.
Looking at Apple documentation there is quite a few ways to accomplish it:
Remove application delegate binding in Interface Builder (.xib file for the window)
Set 4th parameter of UIApplicationMain in main.h to something else than nil.
Check you nib file in Interface Builder and see if the App Delegate is setup.
Reference to documentation
Core Application Design
The application delegate is a custom object that you provide at
application launch time, usually by embedding it in your application’s
main nib file. The primary job of this object is to initialize the
application and present its window onscreen. The UIApplication object
also notifies this object when specific application-level events
occur, such as when the application needs to be interrupted (because
of an incoming message) or moved to the background (because the user
tapped the Home button).
The fourth parameter identifies the class of the application delegate.
The application delegate is responsible for managing the high-level
interactions between the system and your custom code. Specifying nil
tells UIKit that the application delegate object is located in the
application’s main nib file (which is the case for applications built
using the Xcode templates).
Try removing doing a Build/Clean All Targets, remove the Build/ directory and delete the app from the simulator and/or the device. Then run it again.
As another check, try logging something in applicationDidBecomeActive:. This method will be called whether on the initial launch or resuming from the background.

Where does UIApplication's handleOpenURL 'Return' to exactly?

I'm working on a handling a custom URL Scheme in an app and am trying to sort out:
- (BOOL)application:(UIApplication *)application handleOpenURL:(NSURL *)url
I'm passing and successfully parsing a URL into an NSDictionary in my app but wondering "what now?" handleOpenURL returns a BOOL but to what? It's hard for me to debug as I haven't figure out how to have debugger running on device when it fires.
All I do know is that applicationDidFinishLaunching completes before handleOpenURL and it appears as though my rootViewController is on screen.
- (void)applicationDidFinishLaunching:(UIApplication *)application {
// Load data
[self initializeData];
// Configure and show the window
[window addSubview:[navigationController view]];
[window makeKeyAndVisible];
}
Anyway, so, now I have this NSDictionary object in my appDelegate, how would you pass it to the rootViewController so it can do something with it in its detail view? Would I call
[[navigationController topViewController] addItemWithDictionary:theDictionary];
before handleOpenURL's return YES; Or, should I create an NSDictionary property in my appDelegate and then after "Return YES;" retrieve it from my rootViewController (or detailViewController - haven't worked out which yet). If so what's the trigger? It's not clear to me where handleOpenUrl's returns are heading...and what, if any, value they have to me.
Thanks!
Take a look at the suggestion here about didFinishLaunchingWithOptions
http://www.iphonedevsdk.com/forum/iphone-sdk-development/31742-handleopenurl-not-called.html
There are a couple of approaches I've used to pass data around, and depending on the conditions I mix it up.
Keep a global around so you don't have to worry about passing.
Register/post your data using the Observer pattern with a Notification object using the notification message center.
Save URLs to NSUserDefaults (also a dictionary, but you don't have to manage it).
Recently I had to do something similar on a UIWebView and the handling of filtering some URL data. I had to subclass WebViewCache and setSharedCache on the NSURL. I strongly suspect this would apply to your problem as well by retrieving the data with shouldStartLoadWithRequest.
a handy tip for debugging openurl from other apps or mobile safari (non jailbroken device) is to subclass UIApplication and override a few undocumented methods, sorry i dont remember names offhand but you can dig them up at ericasadun.com, the names will be fairly obvious.
when you launch the app through xcode and hit home springboard doesnt kill the process because it was started by xcode (afaik) so you can launch other apps and call openurl while still being attached to the debugger.
I think I may have answered my own question but if you have other ideas I'd love to hear them!
I think I need to continue processing in handleOpenURL and turn the dictionary into an object that is then added to my appDelegate's array that rootViewController is using to build it's table view. Obviously need to work out some validation & confirmation on user's part before auto populating array. I guess that would also happen within confines of handleOpenURL?

iPhone: Access a class from another class

I want to make a program that parses an XML file and then updates labels on 2 different tab bar views. On both of these views, I have a refresh button to update the results. What I'd like to do is update both of the views labels from either view. I figure the AppDelegate is probably a good choice to make this happen, but I tried creating classes that the AppDelegate can access but since they're instances of a class they contain no values. I get no errors but the labels don't update even though the data changes. This is the method in my AppDelegate that is called after the XML is parsed:
-(void)callUpdateValues {
NSLog(#"Calling Update from AppDelegate");
home *homeController;
data *dataController;
[homeController updateValues];
[dataController updateValues];
}
One of the update methods looks like:
- (void)updateValues {
NSLog(#"Call Home");
[label1 setText: [[[[totalData objectAtIndex:0] objectForKey:#"nodeChildArray"] objectAtIndex:7] valueForKey:#"nodeContent"]];
[label2 setText:[[[[totalData objectAtIndex:0] objectForKey:#"nodeChildArray"] objectAtIndex:1] valueForKey:#"nodeContent"]];
}
So the view calls the AppDelegate method "callUpdateValues" and then that method should call the individual "updateValues" methods in each view. I am not an expert on this by any means, and I'm really just trying to get an idea of how programming on the iPhone works. I'm probably just not understanding something here, and if someone could give me some sort of answer I'd appreciate it.
Cocoa has a number of classes available for notifying interested parties of changes. Directly calling methods as you describe makes things much more closely coupled than you need to.
In your method that generates the update you'd have:
[[NSNotificationCenter defaultCenter] postNotificationName:#"IGotSomeNewData"
object:newData
userInfo:nil];
And in the classes that want to hear about updates you'd register for the notification:
[[NSNotificationCenter defaultCenter] addObserver:self
selector:#selector(newStuff:)
name:#"IGotSomeNewData" object:nil];
And then implement the method that gets called when something happens:
- (void) newStuff: (NSNotification *)notification {
id newData = [notification object];
// Do stuff
}
There's some really great stuff getting done by Apple for XML on the iPhone: XML Reading Material
The first snippet is out of place. I think what you're missing is that you need to create your instances within the AppDelegate.h, expose them using properties (and synthesizing them in the .m). Then you're update structure should fit better.
If you're just picking up iPhone programming, start digging into the guides that apple provides, and even if you're not into that, start pulling down at least 5 sample code projects a day. The beauty of them is that you can build them (even onto your iphone) and if you like a feature, you can see how it's done. Alternatively, get the grapefruit book from APRESS. Beginning iPhone.
Hope this helped.
In the example you gave, homeController and dataController are not properly initialized. If I understand your project correctly, you would have created instances of the homeController and dataController classes in your main XIB file, and connected them up to the appropriate views (label1 and label2). Your AppDelegate should, then, look something like this:
...
#class homeController;
#class dataController;
#interface AppDelegate
{
IBOutlet homeController * home;
IBOutlet dataController * data;
}
...
#end
With this in place, you would add (in your application XIB file), links from your homeController and dataController instances to the appropriate outlets (labeled home and data) in your application delegate.
Then, you could simply reference them by name in your callUpdateValues method:
-(void)callUpdateValues {
NSLog(#"Calling Update from AppDelegate");
[home updateValues];
[data updateValues];
}
On a side note, Cocoa coding standards usually specify that class names are capitalized. This is, of course, up to your personal taste, but if you're just getting started in Cocoa, it may be worth drinking one more cup of kool-aid at this point, just so your code will "fit in" with what most other developers are doing. Again, totally up to you!

How to react to applicationWillResignActive from anywhere?

What's the code to subscribe to an event like applicationWillResignActive in any place in your iphone application?
[UPDATE]
Let me rephrase my question. I don't want to respond to this in my application delegate, but rather listen to this event from another class. Is that possible or I need to pass the event from the application delegate to the concerning class?
Looks like you are looking for this code.
- (void) applicationWillResign {
NSLog(#"About to lose focus");
}
- (void) myMethod {
[[NSNotificationCenter defaultCenter]
addObserver:self
selector:#selector(applicationWillResign)
name:UIApplicationWillResignActiveNotification
object:NULL];
}
Take a look at the documentation for the method you're talking about:
applicationWillResignActive:
Tells the delegate that the application will become inactive. This method is optional.
- (void)applicationWillResignActive:(UIApplication *)application
[...]
Discussion
[...]
Just before it becomes inactive, the application also posts a UIApplicationWillResignActiveNotification.
Implement the method below in your application delegate:
-(void)applicationWillResignActive:(UIApplication *)application
This allows you to react when the application becomes inactive - when this is the case, it is executing but not dispatching incoming events. This happens, for example, when an overlay window pops up or when the device is locked.
Just before it becomes inactive, the application also posts a UIApplicationWillResignActiveNotification.
Your topic and question are asking slightly different things.
Your application will receive applicationWillResignActive, along with applicationWillTerminate, automatically. No subscription is necessary, just implement the function in your app.
As to how to respond, this is down to the application. While you can choose to do nothing the recommended behavior is that you cease or slow any non-critical functionality. E.g. if you were a game you would stop updating the display and/or pause the game.