I would like to perform certain cleanup tasks when the app shuts down. I use an observer like:
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(appWillResignActiveNotif:) name:UIApplicationWillResignActiveNotification object:nil];
to get notified when the app goes to background.
The problem is that if the app crashes, there is no notification for me to do something.
I saw that testflight.com use a hook to recover crash information, I was wondering if it was possible to also detect crashes and perform some tasks.
My concern is regarding the call to:
CLLocationManager.stopMonitoringSignificantLocationChanges
not being done when app crashes, leaving users with a constant location icon on top. I know that crashes are not supposed to be frequent, but would like to clean as much as possible if I can under these circumstances.
you could install a global exceptionHandler or even a signalHandler
http://www.cocoawithlove.com/2010/05/handling-unhandled-exceptions-and.html
but remember: dont continue running after a crash. it is NOT safe :D
Related
For an iCloud plugin I'm writing, I subscribe my iCloud manager class to these iCloud NSMetaDataQuery observers:
// Add a predicate for finding the documents
NSString* filePattern = [NSString stringWithFormat:#"*.%#", #"*"];
self.metadataQuery = [[NSMetadataQuery alloc] init];
// Before starting to query, it is required to set the search scope.
arrayWithObject:NSMetadataQueryUbiquitousDocumentsScope]];
// It is also required to set a search predicate.
[self.metadataQuery setPredicate:[NSPredicate predicateWithFormat:#"%K LIKE %#", NSMetadataItemFSNameKey, filePattern]];
// Listen for the second phase live-updating
[[NSNotificationCenter defaultCenter] addObserver:self
selector:#selector(queryDidReceiveNotification:) name:NSMetadataQueryDidUpdateNotification object:nil];
// Listen for the first phase gathering
[[NSNotificationCenter defaultCenter] addObserver:self
selector:#selector(queryIsDoneGathering:) name:NSMetadataQueryDidFinishGatheringNotification
object:nil];
[self.metadataQuery startQuery];
The problem is that none of these selectors are actually ever called back, not even once, and I especially need the NSMetaDataQueryDidUpdateNotification to track upload/download progress of files in the cloud.
An odd thing is that I had this working the other day, but somehow it just stopped working and I've starred myself blind in trying to figure out what the problem actually is. By subscriping to the NSMetadataQueryDidStartGatheringNotification I can see that it does start, but it's like it never finishes. It is quite weird.
I was wondering if anyone have any clue at all as what to be wrong with the above code? Or where else I can look for the problem.
Thank you for your time :)
Make sure you start the NSMetadataQuery in the main thread. Back then this requirement was not documented.
This was quite the secret to dig up. I don't know if you've given up by now, but at long last, I might be able to help.
It turns out that for some (all?) C++ app configurations, there is a message pump that doesn't run automatically. In my app, I finally started getting my expected callbacks after placing a loop like this following my [m_query startQuery] call:
// make it look syncronous for now
while( m_state != finished )
{
Check_For_Interruption();
[[NSRunLoop currentRunLoop] runUntilDate:[NSDate dateWithTimeIntervalSinceNow:timeslice]]; //<--**this line is the key**
}
where the callbacks are setup to correctly update the m_state variable.
My example just blocks, using it's own thread timeslice to run the loop (unless interrupted by a timeout being reached), but this could also be set up in an asynchronous way.
Another thing to note for those who go overboard on legacy support:
This method did cause the app to start leaking mach ports on OS X 10.9 and older. Haven't seen any problems on anything newer than that, though.
I want to create a background application that keep track of the current active application (active window). I manage to get the active application by using GetFrontProcess but now I need to keep track of it so I can know when it changes.
I thought of two ways
1. Setting a Timmer and recheck the variable each time the timer triggers (Should I create a thread?)
2. Creating a dedicated thread, check the variable in a loop and sleep for a few Milliseconds after every iteration to avoid overhad.
Efficiency is important to me as this should be a non disturbing background thread.
Do you think those methods are right? Any efficiency considerations? Would love to hear about different ideas that are more efficient.
Thanks for your help
Gil
You could observe the NSWorkspace notification NSWorkspaceDidActivateApplicationNotification.
eg:
[[[NSWorkspace sharedWorkspace] notificationCenter] addObserver:self
selector:#selector(appActivated:)
name:NSWorkspaceDidActivateApplicationNotification
object:nil];
and have a method:
-(void)appActivated:(NSNotification*) notification {
NSRunningApplication* currentApp = [notification.userInfo valueForKey:NSWorkspaceApplicationKey];
....
}
I am wanting to create a timer or something of sorts to auto logout the user after x minutes of inactivity. I would like to do it the same way the Bank of America application does it. The way the BofA app does it is even when the application is put into the background it still keeps track of the time. When the time limit is reached a notification will popup stating you are being logged out.
How can this be done without the timer being suspended when the application goes into the background?
I think maybe the simplest thing you can do is register your AppDelegate with the NSNotificationCenter to listen for all events from all (or maybe specific) senders.
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(resetTimer) name:nil object:nil];
You need to take care with passing nil to the name and object parameters as you will get a ton of notifications (even some not originating from the application, i.e., memory warnings). If you know of or have the list of specific event names and/or objects I would observe on those instead.
In your resetTimer method, you will simply invalidate the previous timer and create a new one that will call some logout method AND set an iVar to the current date/time (i.e., timerStart = [NSDate now];)
The above steps will take care of your app while it is in the foreground.
When the app is backgrounded, the timers will quit working. However, when the app returns to the foreground, you can calculate the delta between [NSDate now] and your timerStart iVar. If the delta is greater than some interval, you invoke your logout method. If not, you can just call resetTimer to start your timers again.
EDIT
If you want the backgrounded app to alert that the user is about to be logged out, you can use a UILocalNotification. You can schedule one to alert when the application enters the background. When the application enters the foreground, you can cancel that notification (and perform the steps I mention above).
I want to save my data before terminating, so my AppControll class conforms the NSApplicationDelegate protocol, and declared the method; and in the interface builder I bound the window's delegate outlet to AppController, but I cannot get the method invoked.
Where I am wrong, what should I do?
Are you terminating the app from Xcode? Alternatively, is sudden termination enabled in your Info.plist?
Either of these will cause a SIGTERM signal to be sent to the application, terminating it immediately, with no chance for the NSApplication instance to send its delegate an applicationWillTerminate: message. (This is the point of sudden termination: Your app dies instantly. You can turn it off and on programmatically for times when this would be bad.)
Try quitting your application within itself (the Quit menu item in your Application menu), or using the Dock to quit it (right-click on your application's tile and choose “Quit”). As long as sudden termination is disabled (or never was enabled), either of these will cause your application object to send the applicationWillTerminate: message.
Also check that your delegate is getting sent other application-delegate messages, such as applicationWillFinishLaunching:, and make sure you hooked up the outlet in the correct nib (your MainMenu nib).
Did you remember to add the handler to the application?
UIApplication *app = [UIApplication sharedApplication];
[[NSNotificationCenter defaultCenter]
addObserver:self
selector:#selector(applicationWillTerminate:)
name:UIApplicationWillTerminateNotification object:app];
Is multitasking still enabled? That could be the problem - tapping the home button doesn't cause applicationWillTerminate: to be called if the app goes into the background.
I'm assuming this question applies to macOS apps (as it mentions NSApplicationDelegate).
By default, Xcode 11 (and maybe earlier versions?) includes the NSSupportsSuddenTermination property in new applications' Info.plist file:
...
<key>NSSupportsSuddenTermination</key>
<true/>
...
This property is associated with the enableSuddenTermination and disableSuddenTermination pair of methods in the ProcessInfo (NSProcessInfo) class.
The relevant part of ProcessInfo documentation states:
Sudden Termination
macOS 10.6 and later includes a mechanism that allows the system to log out or shut down more quickly by, whenever possible, killing applications instead of requesting that they quit themselves.
Your application can enable this capability on a global basis and then manually override its availability during actions that could cause data corruption or a poor user experience by allowing sudden termination. Alternately, your application can just manually enable and disable this functionality.
In other words, when NSSupportsSuddenTermination is true, when the user tries to quit the application (directly or indirectly), macOS terminates it, instead of requesting it to quit. This bypasses any events that would otherwise be triggered during a regular quit request.
The good new is that you can either disable that in the Info.plist file, or manually override it, according to your application's needs, by calling ProcessInfo.processInfo.disableSuddenTermination().
In applicationWillFinishLaunching: add:
[[NSNotificationCenter defaultCenter]
addObserver:self
selector:#selector(applicationWillTerminate:)
name:UIApplicationWillTerminateNotification object:nil];
I have some code that needs to be run when the application terminates. I register my controller for the NSApplicationWillTerminateNotification as follows:
[[NSNotificationCenter defaultCenter]
addObserver: self
selector: #selector(applicationWillTerminate:)
name: NSApplicationWillTerminateNotification
object: nil];
Now, if I start my app and quit it within the first 20 seconds or so, applicationWillTerminate gets called. If I quit the application later, it doesn't. What in my application could cause this behaviour? I have also tried to set up my controller as NSApplication's delegate with same results. Any ideas?
Thanks.
Oh, and this is XCode 3.2, Snow Leopard 10.6.1, using 10.5 SDK. Happens in both Debug and Release builds.
There are several reasons why this might be happening.
If you are running GC'd, does your observer get collected and finalized before the termination happens? (I should test this and file a bug if it does as that at least needs to be documented)
Is your app silently crashing or calling exit() directly?
In general, you cannot count on the termination notification being received as the user might go for a force quit for the heck of it.
Also, in Snow Leopard, there is a feature called sudden termination that allows your app to let the system know that it is safe to kill the app instead of going through the normal termination rigamarole. It is documented in the NSProcessInfo documentation.