How to detect when a user terminates an app - Swift
Asked Answered
S

2

7

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???

Suffolk answered 24/8, 2021 at 17:44 Comment(12)
No, there's no bullet proof way to know when the app will be terminated. And you should never rely on any termination functions to be called. Why? because app termination may not be graceful. For example the phone may be out of memory and the app is killed urgently. Or battery died. So best is to assume that app can be killed any moment without any prior warning, and thus do things you wanted to do on termination using other triggers, e.g. on state change, or just periodicallyJeaninejeanlouis
Well good to know. Is there a way to see when an app is closed out after it was previously in the background? For example, if I exit an app (but don't terminate it), and then later go to my App Switcher and terminate the app? Alarmy is able to do this to notify people when they terminate their app if an alarm is set.Suffolk
I don't know what they did, so I can just speculate that they may have used applicationWillTerminate in combination with other methods, e.g. background fetch (will cause the app to get started if it's not running, but limited to 30 min or so I believe), and maybe scheduled local notifications...Jeaninejeanlouis
I may be sensing an issue with your question. In iOS, users do not typically close an app. (They once may have, but most have learned they don't need to. But in your question you specifically addressed this three times. In iOS, typically it's the OS that terminates an app for a variety of reasons. Once you understand this, maybe you can be a bit more specific about why your app needs to care about this. You may find a b better way to handle your specific issue without caring about who or what terminated things - maybe like saving your apps state, recalling it when your app launches?Falchion
What are you trying to do? Alarmy is probably posting a local notification on a schedule, and just before it's about to fire, postponing it if the app is still running.Icky
Please see my EDIT. func sceneDidDisconnect(_ scene: UIScene) was the solution after all.Suffolk
@Icky I originally thought Alarmy was following your solution proposed solution, but then I realized there's really no way for them to postpone the notification if the app is still in the background (in which case the notification would be triggered incorrectly).Suffolk
@dfd Yeah I think you are right that I am a little confused on the whole "termination" thing, but what I'm essentially going for is how to detect when the user basically enters the app switcher and up-swipes on the app to "terminate" it or close it out. For that, I've realized sceneDidDisconnect does the job.Suffolk
@Suffolk Instead of editing the answer into your question, you should post it as an answer and then accept it so that your question doesn't look unanswered. I think you might even get a badge the first time you answer your own question.Harquebus
@Harquebus Thanks, I will do that!Suffolk
I upvoted your answer because it will help others. I come from a, uh, mainframe background followed by Windows and web - so yes iOS, while elegant with it's memory usage, is a bit of a paradigm change. It's gotten worse in ways with XIBs becoming Sotryboards and AppDelegates becoming SceneDelegates. Glad you figured out a solution! (BTW, my favorite iPad game features those annoying full screen 30 second ads at times - so I just force-quit and restart because it takes less time than watching a stupid ad.)Falchion
Haha clever "ad-free" solution. Sometimes turning on airplane mode can work too.Suffolk
S
4

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)."

Suffolk answered 24/8, 2021 at 22:14 Comment(0)
T
0

I'm using React Native 73 so SceneDelegate isn't available for me. I have to use AppDelegate.mm. The way I'm able to detect when the user terminates the app (somewhat reliably) is by using applicationWillTerminate. For whatever reason, in XCode, it only gets logged once per app initialization, but it actually does fire every time the user swipes away the app once it's built.

@implementation AppDelegate
    - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
  [FIRApp configure]; // If you're using react-native firebase
  logger = os_log_create("com.uchenkadi.swoltime", "AppDelegate");

  self.moduleName = @"main";
  // You can add your custom initial props in the dictionary below.
  // They will be passed down to the ViewController used by React Native.
  self.initialProps = @{};
  
  bool didFinish = [super application:application didFinishLaunchingWithOptions:launchOptions];
  
   [RNSplashScreen show];  // this needs to be called after [super application:application didFinishLaunchingWithOptions:launchOptions];
   
   return didFinish;
}

- (void)applicationWillTerminate:(UIApplication *)application {
  // do stuff in here!
}
@end
Tillo answered 2/5 at 16:55 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.