Not sure why this is so challenging for me, but how can I detect when the user terminates the app? Because from what I'm seeing, Apple provides a lot of functions that might or might not be called when an app closes.
func applicationWillTerminate(_ application: UIApplication): Called when (most) apps are terminated, but not for apps that support background execution.
func sceneDidEnterBackground(_ scene: UIScene): Called when the scene enters the background, but can't differentiate between the app entering the background versus terminating completely.
func sceneDidDisconnect(_ scene: UIScene): I confirmed this is called if the user directly terminates the app, but if the app is put in the background and then terminated, it isn't called..
// EDIT: So I realized the above method (sceneDidDisconnect) is indeed the function I was looking for. I previously thought it wasn't being called in the latter case described above, but in actuality, it was. See the (soon to be) accepted answer as well.
Is there a function that's called every time a user terminates an app???
func sceneDidDisconnect(_ scene: UIScene) ended up being the function I was looking for. It's called whenever the user manually "terminates" the app...
...although as #dfd commented
"In iOS, typically it's the OS that terminates an app for a variety of reasons (not necessarily the user)."
Related
2020-01-18 18:03:02.316685-0500 Watch Extension[529:813076] Execution of the command buffer was aborted due to an error during execution. Insufficient Permission (to submit GPU work from background) (IOAF code 6)
I started getting this console message printing repeatedly when testing 3 HealthKit Apps but I can't figure out what it is related to and previous SO questions are only regarding the iPhone. Specifically it seems like I can trigger it when I simulate workout movements (i.e. jogging). Any idea what could cause this message on the Watch?
EDIT: I believe the problem is an SKScene that I am using to show an animation on the watch app. When I comment out the below, I am not seeing the console warnings anymore:
#IBOutlet var spriteKitScene1: WKInterfaceSKScene!
#IBOutlet var spriteKitScene2: WKInterfaceSKScene!
HealthKit must be using Metal, or something in your app is. Metal does not allow background processing.
To get rid of the warning, you will need to pause or suspend any processes that use Metal.
In your AppDelegate.swift file, you can implement these two methods:
func applicationWillResignActive(_ application: UIApplication) {
//Pause or suspend any operations using Metal
}
func applicationDidBecomeActive(_ application: UIApplication) {
//Resume or start operations using Metal
}
The other way to start/stop operations when entering background/foreground is with notifications. If you prefer that pattern I will post examples.
Note that what you are seeing is a warning indicating that Metal processing does not occur in the background. If your app is working as expected you can ignore the warning.
Is there A way to guarantee that the applicationWillTerminate method in the AppDelegate delegate will be hit? Something like a key in the info.plist file, etc..?
My goal: I'm working in a beacon app, the piece of code is in this article. My problem is that the message from the didEnterRegion keeps poping even when i'm beside the beacon. To solve that I'm setting a flag to control the message. My code below:
if(!UserDefaults.standard.bool(forKey: Constants.EnterZoneMsgShowName)){
let notification = UILocalNotification()
notification.alertBody = "Hi, you are about to arrive at CIDMA's office. Please open de demo app and turn on the bluetooth on your device to enrich your experience. "
UIApplication.shared.presentLocalNotificationNow(notification)
UserDefaults.standard.set(true, forKey: Constants.EnterZoneMsgShowName)
}
I want to set this flag to false when I close the app. I tried to put it at the applicationWillTerminate but this method is not hit every time.
I would like to know how to guarantee that this code will be hit or if there is a better place to put the code: UserDefaults.standard.set(false, forKey: Constants.EnterZoneMsgShowName)
applicationWillTerminate(_:) - Tells the delegate when the app is about
to terminate.
For apps that do not support background execution or are linked against iOS 3.x or earlier, this method is always called when the user quits the app.
For apps that support background execution, this method is generally not called when the user quits the app because the app simply moves to the background in that case. However, this method may be called in situations where the app is running in the background (not suspended) and the system needs to terminate it for some reason.
What you want to call is applicationDidEnterBackground if your application supports background execution, this method is called instead of applicationWillTerminate: when the user quits.
Assume that I am build an iPhone application which accepts user name and password in one of the screens. While I am entering my username, I get an high priority event like a phone call.
My app will transit from being active to an inactive state at this point.
My question here is: What are the steps that I need to do in my app so that I can save the current application state (and entered info) so that I can restore the same when my app becomes active later on?
This question was asked in one of iOS interviews.
My answer was that handle the active to inactive state transition in the applicationWillResignActive delegate method to save the partially entered user info details and restore it in applicationDidBecomeActive method. The App UI state, we don't have to handle as the OS will take care of it.
It looked like the interviewer was not convinced with my answer. He kept asking me as to how would you handle (steps that you would take in your app) the transition from an active state to inactive state and then to active state in your app so that app state is restored/intact?
Your answer is correct, the interviewer was probably looking for you to name each of these methods and you most likely only named the two you stated. There are a few other delegate methods for certain App UI states, such as applicationDidEnterBackground and applicationWillEnterForeground
If you read the default description of these methods in the comments you will get a better understanding of what each is used for. For example, applicationWillResignActive specifically refers to incoming phone calls or SMS messages:
// Sent when the application is about to move from active to inactive state. This can occur for certain types of temporary interruptions (such as an incoming phone call or SMS message) or when the user quits the application and it begins the transition to the background state.
// Use this method to pause ongoing tasks, disable timers, and throttle down OpenGL ES frame rates. Games should use this method to pause the game.
And applicationDidEnterBackground is for saving user data when a user closes your app:
// Use this method to release shared resources, save user data, invalidate timers, and store enough application state information to restore your application to its current state in case it is terminated later.
// If your application supports background execution, this method is called instead of applicationWillTerminate: when the user quits.
Check out your AppDelegate.m file of a new project and the comments really are very, very useful for understanding exactly which ones will handle what states and when you should use each.
Also, be sure to read up on Apple's documentation on handling application states: The App Life Cycle
Apple has already provided decent documentation on your question:
link.
And for interruptions:
link for apple developer site on handling interruptions
I think you are talking much more about restore state.
First of all, implement App delegate protocol:
func application(application: UIApplication, shouldRestoreApplicationState coder: NSCoder) -> Bool {
return true
}
func application(application: UIApplication, shouldSaveApplicationState coder: NSCoder) -> Bool {
return true
}
Then in your ViewController, implement UIStateRestoring protocol. Here you should notice that if you want archive your own complicated object, don't forget to implement NSCoder.
Please go deep into "State Restoration".
Besides, when your app go to background, you need to do some work such as stop timer, pause ongoing task.
When go to foreground, you need to do something like start the paused task, and prepare to refresh page.
I am using swift 2 and Xcode7 for iOS9. I want to know if I can maintain a function (that checks for something to delete) running "forever" even if the user kills the app?
I am deleting contacts from the contact list according to some rules and time. It is running ok, but just with the app opened or in second plan. I want to make this app capable to delete those contacts even when the user kills it.
You can use background thread when user opens the app. But if the app will be terminated, there is no option to run functions.
Look for the app lifecycle here and redesign your architecture: https://developer.apple.com/library/ios/documentation/iPhone/Conceptual/iPhoneOSProgrammingGuide/TheAppLifeCycle/TheAppLifeCycle.html
If the user kills the app it is no longer running, therefore your code is no longer running. There is no such state that your code/app can be in where this is possible.
By "kill", I don't mean "background". Backgrounding an app is different. Check Apple's docs on the different app states (see m.albin's answer) as well as various strategies for handling those app states.
func applicationWillTerminate(_ application: UIApplication) {
// Called when the user discards a scene session.
// If any sessions were discarded while the application was not running, this will be called shortly after application:didFinishLaunchingWithOptions.
// Use this method to release any resources that were specific to the discarded scenes, as they will not return.
print("Application Will Terminate")
}
I have background mode on for location services and aiming to send out location (latitude and longitude) to the server every 30 minutes. For now I am printing the same in the console. It seems to work for a while but I am wondering how do I work with NSTimer in this case. And from where should I be calling it?
import UIKit
import CoreLocation
#UIApplicationMain
class AppDelegate: UIResponder, UIApplicationDelegate, CLLocationManagerDelegate {
var window: UIWindow?
var locationManager = CLLocationManager()
func application(application: UIApplication, didFinishLaunchingWithOptions launchOptions: [NSObject: AnyObject]?) -> Bool {
// Override point for customization after application launch.
return true
}
func applicationWillResignActive(application: UIApplication) {
// Sent when the application is about to move from active to inactive state. This can occur for certain types of temporary interruptions (such as an incoming phone call or SMS message) or when the user quits the application and it begins the transition to the background state.
// Use this method to pause ongoing tasks, disable timers, and throttle down OpenGL ES frame rates. Games should use this method to pause the game.
}
func applicationDidEnterBackground(application: UIApplication) {
// Use this method to release shared resources, save user data, invalidate timers, and store enough application state information to restore your application to its current state in case it is terminated later.
// If your application supports background execution, this method is called instead of applicationWillTerminate: when the user quits.
self.locationManager.delegate = self
self.locationManager.startUpdatingLocation() // I know i should be using signification location option here. this is just for testing now.
}
func locationManager(manager: CLLocationManager!, didUpdateToLocation newLocation: CLLocation!, fromLocation oldLocation: CLLocation!) {
self.sendBackgroundLocationToServer(newLocation);
}
func sendBackgroundLocationToServer(location: CLLocation) {
var bgTask = UIBackgroundTaskIdentifier()
bgTask = UIApplication.sharedApplication().beginBackgroundTaskWithExpirationHandler { () -> Void in
UIApplication.sharedApplication().endBackgroundTask(bgTask)
}
println(location.coordinate.latitude)
if (bgTask != UIBackgroundTaskInvalid)
{
UIApplication.sharedApplication().endBackgroundTask(bgTask);
bgTask = UIBackgroundTaskInvalid;
}
}
func applicationWillEnterForeground(application: UIApplication) {
// Called as part of the transition from the background to the active state; here you can undo many of the changes made on entering the background.
application.beginBackgroundTaskWithExpirationHandler{}
}
func applicationDidBecomeActive(application: UIApplication) {
// Restart any tasks that were paused (or not yet started) while the application was inactive. If the application was previously in the background, optionally refresh the user interface.
application.beginBackgroundTaskWithExpirationHandler{}
}
func applicationWillTerminate(application: UIApplication) {
// Called when the application is about to terminate. Save data if appropriate. See also applicationDidEnterBackground:.
application.beginBackgroundTaskWithExpirationHandler{}
}
}
Maybe calling application.beginBackgroundTaskWithExpirationHandler{} is a bad idea? What options do I go with here?
The idea of beginBackgroundTask... is to start a finite length task so that if the user leaves the app, it will keep running in the background task for some short, finite period of time (3 minutes, I believe). And before the time runs out, you have to call endBackgroundTask or else the app will be summarily terminated.
So, sadly, the background task mechanism is not really suited for your desired intent. There are, though, a narrow set of special background modes designed for continued background operation outside a narrow set of functions (VOIP, audio, etc.). For more information, see the Implementing Long-Running Tasks section of the App Programming Guide for iOS: Background Execution.
Now, one of those background modes is for a "location" service. So, if that is a central feature of your app, essential for proper function, then you can register for the location background mode, and your app will continue to run in the background. From there, you can monitor for location updates, and if a sufficient amount of time has elapsed, trigger some process. But if this background location mode is not an essential feature of your app, Apple is likely to reject your app for requesting a background mode that it doesn't need.
By the way, you should be aware that starting standard location services may drain the device battery. You might consider using the battery efficient "significant change" location service. This also has the virtue of automatically waking your app every time the user moves some significant distance (e.g. measured in km; I believe it's triggered by moving to different cell tower).
Couple of notes, since we've been fighting the location issues and struggling with background applications that don't seem to wake us up.
NSTimer is only good as long as your application is not suspended. Sure, it's in the background, but onlyinasmuchas it is capable of receiving notifications from the iOS (APN, location, ...). Eventually, your timer WILL stop firing while you run in the BG. So, you are relying on one of the BG modes to kick you.
If you ARE using CLLocationManager.startUpdatingLocation, you're going to notice that you quit getting those after a bit. Especially when the phone relaxes and tries to sleep. This is because of a poorly documented feature called CLLocationManager.pausesLocationUpdatesAutomatically ... which decides to stop sending those when set to "true" (the DEFAULT setting). You can set this to "false", and you'll continue to get your position updates. Which will pretty much make sure your app does what you are wanting.
As noted above, you must make sure when you get your locationManager.didUpdateLocations callback that you begin a backgroundTask to keep your application working while you process that information and send your network data. But you seem to understand that.
Just waking up long enough to "record" a location update isn't so bad. Just make sure you don't spawn your network code unless you've clearly hit your 30 min expectation.
try this:
Make a singleton for Location Service. Registered it with a NSNotification
In AppDelegate's willEnterBackGround, you send a notification via NSNotificationCenter
then, your singleton will start updatingLocation and send it to server when location's data received.