What I want
I want a helper app user agent (LSUIElement in Info.plist is True) to add itself to the login items on terminate.
The Problem
I can't get any code to run before the helper app process terminates.
My "add to login items" code runs fine.
Background
I've got a helper app user agent process that runs all the time
On first launch, it adds itself to login items
If the app was moved, on next login, the helper app process can't be found and so isn't launched
What I've Tried
I wonder if I should subclass NSApplication and override terminate: or stop: and put my code in there, but that seems overkill. Surely there's a better way?
I've tried all these different things in the NSApp delegate:
-(void)applicationWillTerminate:(NSApplication *)sender {
[self addHelperAppToLoginItems]
}
-(void)applicationDidTerminate:(NSApplication *)sender {
[self addHelperAppToLoginItems]
}
-(NSApplicationTerminateReply)applicationShouldTerminate:(NSApplication *)sender {
[self addHelperAppToLoginItems]
}
-(void)applicationDidFinishLaunching:(NSNotification *)aNotification {
[[[NSWorkspace sharedWorkspace] notificationCenter] addObserver:self
selector:#selector(addHelperAppToLoginItems)
name:NSWorkspaceDidTerminateApplicationNotification
object:nil];
}
-(void)addHelperAppToLoginItems {
// This code never gets called!
}
Why do the NSApplication delegate methods not work with a user agent process?
And how can I get the process to check the login items on terminate?
I'd be very grateful for any help. Thanks!
UPDATE 1 6/2/11
After some further digging, the problem is more that processes never really get quit, it's more common for them to get killed by the OS.
This means when you choose to "Quit" a process in Activity Monitor or you shut down the computer, the terminate delegate methods don't get called.
When the following Applescript is run, the delegate methods do get called:
tell application "System Events"
tell application "LapsusHelperApp"
quit
end tell
end tell
After some further digging, the problem is more that processes never really get quit, it's more common for them to get killed by the OS.
This is because you have sudden termination enabled for your application. It's opt-in, so simply remove that key-value pair from your Info.plist and you will then start getting applicationWillTerminate: messages.
Also, the Terminate button in Xcode (at least in 3.x) always works the same way sudden termination does, so you will never get a applicationWillTerminate: message when terminating your app from Xcode.
By the way:
applicationWillTerminate: is a notification message, so its argument is an NSNotification object, not an NSApplication object.
There is no applicationDidTerminate:. A moment's reflection will reveal why. ☺
Related
I'm working on an OS X application after having worked almost exclusively on iOS apps. If the user quits, say by pressing Command-Q, how can I store some data locally and send some data to my server before the app truly goes out of memory?
On iOS I typically launch a background event when I detect the app going into the background. I've tried listening for "ApplicationWillTerminate" but it doesn't seem to afford me the time to send data.
Should I be intercepting the menu action and performing my work before quitting?
I realize this is a pretty basic question, but my Google-Foo has not led me to a straightforward answer.
applicationWillTerminate is indeed the method to clean up things before the app quits.
Alternatively for an asynchronous way implement applicationShouldTerminate, start you task to store and send the data asynchronously and return NSTerminateLater.
When the task is completed call
[NSApp replyToApplicationShouldTerminate:YES];
Here a snippet as example
- (NSApplicationTerminateReply)applicationShouldTerminate:(NSApplication *)sender
{
[self startTaskToSendDataWithCompletion:^() {
[NSApp replyToApplicationShouldTerminate:YES];
}];
return NSTerminateLater;
}
I have successfully enabled background processing for my Core Location app. In order to ensure I am on the right track here is a quick overview of how I implemented ENTERING background mode:
1: info.plist: Added key "Required background modes" with value "App registers for location updates"
2:
- (void)applicationDidEnterBackground:(UIApplication *)application
{
[self.locationManager stopUpdatingLocation];
[self.locationManager startMonitoringSignificantLocationChanges];
}
When EXITING background mode I did so with this code:
- (void)applicationWillEnterForeground:(UIApplication *)application
{
self.locationManager.delegate = self;
[self.locationManager stopMonitoringSignificantLocationChanges];
[self.locationManager startUpdatingLocation];
}
This seems to be working well as I am getting locations reported while my iPhone is off. Any advice on whether this approach is considered "best practice" would be appreciated.
My concern is that even when my app is explicitly terminated on the iPhone the Location Services arrow still shows on the status bar.
My question is: Is it possible to programatically enable/disable an application's background processing?
My reason for this is that if my user does not want to use the location awareness portion of my app, I would like to prevent the app from processing in the background, even if it is not receiving locations.
Thanks and much for any info!
Thank-you for your prompt answers. What seems to be happening is that even though I issue
[self.locationManager stopMonitoringSignificantLocationChanges];
and
[self.locationManager stopUpdatingLocation];
the Location Services arrow thingy on the status bar stays on, even when my app has exited. I have force terminated it as well, but the arrow stays on.
I also have no other apps that are running as suspended. I have looked in Settings > Location Services > MyApp and see that both myApp and the built-in Maps app indicate that they are currently using Location Services, however, neither is actually running.
Does anyone have any idea why this is happening? Could it be that I am not freeing up resources properly when my app either goes into the background or terminates?
All answers are much appreciated.
You could have a setting saved in NSUserDefaults and use that whether or not to enable your background location services.
NSUserDefaults *defaults = [NSUserDefaults standardDefaults];
someFlag = [defaults boolForKey:#"gpsOnOff"];
if (someFlag) {
// enable background services
}
What will happen if an app is either dead, dying, asleep, or in the process of exiting when an NSURLConnection async request comes back?
It seems to me I'd better at a minimum keep a flag that says "program is exiting" before my async function presumes that program data is valid.
Thanks.
In iOS 4.0 and above, when the user closes the app, it is put into a suspended state. Unless you tell it to do otherwise, the delegate callbacks won't fire until the user opens the app again. You can test yourself by writing some NSLogs in your callbacks. Then, run the app on a test device, and try closing the app just after you see the connection start. Wait a while and reopen the app (still 'running' in Xcode), and watch as the delegate callbacks get called.
//somewhere just after you start the async connection
NSLog(#"Connection started...");
- (void)connection:(NSURLConnection *)connection didFailWithError:(NSError *)error {
NSLog(#"Connection failed with error - %#",error);
}
- (void)connectionDidFinishLoading:(NSURLConnection *)connection {
NSLog(#"D- Connection finished loading");
}
If you'd like the connection(s) to continue to run after the app is closed by the user, then have a read through 'Executing Code in the Background' in the iOs Application Programming Guide.
Hmmm……
A question about UILocalNotification and the notificaton's alertLaunchImage.
My app uses UILocalNotifiaction(s) to get users' attention. As usual, an alert is presented with "Action" and "Close" buttons. When the user taps Action, the image specified by alertLaunchImage is presented. The alertLaunchImage is a screenshot of of one of the views of the app which is shown after the data is initialized when launched normally.
Here are the 3 cases when the notification is delivered:
App is running in foreground - no alert, no launchImage is shown as designed. No problems.
If my app is running in background when the notification is delivered, the launchImage works like a charm. No problems. The launchImage with no app-related data is shown and then the app fills up the data. This part works seamlessly.
However, if the app is not running when the notification is delivered, the sequence is confusing - or I missed something. The app gets launched and shows the alertLaunchImage instead of the Default image. Then is goes thru several other screens (as part of initialization and data processing) before the actual screen (live version of alertLaunchImage) is shown.
This can get very confusing to the user. My question comes in here. How can this be avoided?
R/-
Sam.!
you can try cleaning up the alert view settings in applicationWillTerminate:
According to the UIApplicationDelegate reference applicationWillTerminate::
"This method lets your application know
that it is about to be terminated and
purged from memory entirely. You
should use this method to perform any
final clean-up tasks for your
application, such as freeing shared
resources, saving user data,
invalidating timers, and storing
enough application state to
reconstitute your application’s
interface when it is relaunched"
HTH,
Oded
If your app is launched by a local notification, you will receive that notification in the options passed to -application:didFinishLaunchingWithOptions:. Based on that, you can write code that navigates to the correct screen without animations.
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
UILocalNotification *localNotification = [launchOptions valueForKey:UIApplicationLaunchOptionsLocalNotificationKey];
if (localNotification != nil) {
// startup by local notification
} else {
// normal startup
}
}
What method must I implement in my cocoa application’s delegate so that on launch, it’ll open a url? (http/https, in this case) I’ve already implemented the url schemes, I just need to know how I can get my application to open on a url notification.
Update: I’m sorry, I wasn’t very clear. My application IS a browser that support https/http urls, but can only open them when it’s already running. What can I do to implement support for open urls in my app on launch?
When an application finishes launching on OS X, NSApp (the global NSApplication instance for the program) sends its delegate the applicationDidFinishLaunching: message (via the notification system). You can implement that method in your delegate to handle the notification and open a browser window in response, using NSWorkspace. Something like the following would work:
// Your NSApp delegate
- (void)applicationDidFinishLaunching:(NSNotification *)aNotification
{
[[NSWorkspace sharedWorkspace] openURL:[NSURL URLWithString:#"http://www.example.com/"]];
}
It's not a delegate method. You need to implement an Apple Event handler for the getURL event.
As luck would have it, this is exactly the case Apple uses to demonstrate implementing an Apple Event handler.
I already had implemented the getURL event, so that alone isn’t enough to get the application to open a url on launch. The trick is that the AppleEvent must be installed in applicationWillFinishLaunching: not applicationDidFinishLaunching:. Otherwise, the event isn’t sent at all because the app hasn’t registered it in time.
To implement a protocol handler that you can select (in Safari preferences, for example) as the "default browser" and which will launch in response to HTTP / HTTPS, you need to do a few things.
Add .scriptSuite and .scriptTerminology files to your project resources. These will tell Mac OS X that you'll be handling the GetURL command.
Add a CFBundleURLTypes key to your Info.plist file listing the "URL Schemes" that your app will handle.
Also in Info.plist, add the NSAppleScriptEnabled key with the value YES.
Add a new class to your application as a subclass of NSScriptCommand and implement the -(id)performDefaultImplementation selector. From within this function you will find the clicked URL in [self directParameter]. Pass this on to your app's URL handler!
For the full details check out the article:
http://www.xmldatabases.org/WK/blog/1154_Handling_URL_schemes_in_Cocoa.item