What if app sleeping or dead before NSURLConnection asynchronous request comes back? - nsurlconnection

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.

Related

NSXPCConnection debugging interruption/invalidation

I am on XCode 9, OSX not iOS, Objective-C.
I have an XPC Service to talk to other applications.
XPC Services are completely new to me. I've read documentation and articles i found - still i'd need some help.
// NSXPC Connection stored as ivar
self.bridgeagent = [[NSXPCConnection alloc] initWithServiceName:#"com.myid.myapp.bridgeagent"];
self.bridgeagent.remoteObjectInterface = [NSXPCInterface interfaceWithProtocol:#protocol(bridgeagentProtocol)];
self.bridgeagent.exportedInterface = [NSXPCInterface interfaceWithProtocol:#protocol(bridgeagentProxyProtocol)];
self.bridgeagent.exportedObject = self;
[self.bridgeagent setInvalidationHandler:^{
NSLog(#"Bridgeagent invalidation handler!");
}];
[self.bridgeagent setInterruptionHandler:^{
NSLog(#"Bridgeagent interruption handler!");
}];
[self.bridgeagent resume];
The Service is called like this:
// openFile method is listed in corresponding protocol
[[self.bridgeagent remoteObjectProxyWithErrorHandler:^(NSError * _Nonnull error) {
NSLog(#"bridgeagent.openFile errorHandler: %#",error);
}] openFile:parameters withReply:^(NSDictionary *returnParameters) { // do something with result }];
The call works and the service does its job. However - now that the service works i want to dig into making it more stable (even if i don't face any issues right now).
Can someone explain to me
the difference between interruption and invalidation (don't get it when one or the other happens)
if there's a best practice to handle both cases
how to force both cases (for debugging)
Thank you for help
Answer to question 1:
[self.xpcConnection setInterruptionHandler:^{
// Connection interrupted. Backend (service) may have crashed.
// connection used to work but suddenly terminated
}];
[self.xpcConnection setInvalidationHandler:^{
// No one is listening. Is the backend running?
// connection cannot be established
}];
Answer to question 3:
interruption: make backend exit in the middle of a transaction (just before reply is sent)
invalidation: don't start backend (service) at all
Answer to question 2:
I have heard that in case "interruption" you should try to reestablish the connection. This can be useful when your service is a launch agent which gets restarted by launchd in case it crashed.
Actually in my program I don't act upon these cases but simply issue a warning message to the command line. My frontend is a cli program.
Alternatively you could log this warning in a logfile, for example with syslog. See 'man 3 syslog'. In my app I use my own logfile with configurable verbosity AND syslog.
kind regards,
Robert

How to send/save data when user quits OS X application

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;
}

What can you do in CoreBluetooth background delegate calls?

I am using Core Bluetooth in my project. I have included Session Backgrounding to avail its background mode functionality. I have observed that the delegate for peripheral disconnection,
- (void)centralManager:(CBCentralManager *)central didDisconnectPeripheral:(CBPeripheral *)peripheral error:(NSError *)error;
gets called in the background mode. However any code I write in this method is not executed except for NSLogs. Can somebody explain exactly what kind of code can be executed here?
My aim is to send this disconnection notification to my server.
Ok it seems it was some issue at my end. According the the documentation your app is woken (in the background) for around 10 seconds when it gets a bluetooth related delegate call.
You can use this time to perform any non view updating task and even request for additional time using beginBackgroundTaskWithExpirationHandler.
My code looks like this.
if ([UIApplication sharedApplication].applicationState == UIApplicationStateBackground) {
[self sendEmailInBackground:peripheral]; //Code to send a server request
return;
}
and its working in the background mode. This also works when the phone is in lock state.

App won't open after clicking on remote notification'

I have a Problem with remote notifications and OSx 10.8.
I enabled the remote push notifications successful. They also were delivered to my system, but when I try to click one nothing happens.
Also for this app update I had to enable sandboxing!
The Mac-Console log:
29.08.12 11:03:50,600 usernoted[194]: Cannot find originating application to launch for event action. file://localhost/Users/clueckler/Library/Developer/Xcode/DerivedData/mysms_OSX_Client-bcaianxrdhpztmdcnsrgzqdtqmvi/Build/Products/Debug/mysms_test.app/ is not the same app as the one that sent the original notification.
29.08.12 11:03:50,600 usernoted[194]: Error finding application com.mysms.osx.client.test.
I thought it was a problem of the AdHoc-Test or something. So I tried to submit it to the AppStore with success!
But the same problem appears with the AppStore Version (now I removed it from store ...)
Is there a solution for this problem?
EDIT (Code Information):
- (void)awakeFromNib {
// Register app for remote notification
[[NSApplication sharedApplication] registerForRemoteNotificationTypes:(NSRemoteNotificationTypeBadge | NSRemoteNotificationTypeSound | NSRemoteNotificationTypeAlert)];
}
- (void)application:(NSApplication *)application didRegisterForRemoteNotificationsWithDeviceToken:(NSData *)deviceToken {
//Post token to server
}

How do I run code when a user agent process terminates?

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. ☺

Resources