I've been building iOS Music application and
I'm using MPMusicPlayerController.systemMusicPlayer() to play music from the iTunes library of the device.
I tried applicationWillTerminate to stop the music player, and It didn't work, after doing some research that I found out After iOS 4 applicationWillTerminate method will *almost never get called, and we must do all the work in applicationDidEnterBackground method.
But my point is, I want my Music player to keep playing music when it enters to the background, and only stop the music when user force quit the application, How can I achieve this behavior ?
Important: The applicationWillTerminate: method is not called if your app is currently suspended.
Even if you develop your app using iOS SDK 4 and later, you must still be prepared for your app to be killed without any notification. The user can kill apps explicitly using the multitasking UI. In addition, if memory becomes constrained, the system might remove apps from memory to make more room. Suspended apps are not notified of termination but if your app is currently running in the background state (and not suspended), the system calls the applicationWillTerminate: method of your app delegate. Your app cannot request additional background execution time from this method.
https://developer.apple.com/library/content/documentation/iPhone/Conceptual/iPhoneOSProgrammingGuide/TheAppLifeCycle/TheAppLifeCycle.html
Related
In iOS, we all know that there is AppDelegate method applicationWillTerminate, and it is called when my app is closed by user when it is currently running(i.e. not in background). But I want to do something(save data, for example) when my app is terminated(closed by user or killed by OS) when it runs in background.
PS: my app can run in background.
Do you have any solutions? thanks.
Sorry but you should use applicationWillTerminate:
This method lets your app 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 app, such as freeing shared resources,
saving user data, and invalidating timers. Your implementation of this
method has approximately five seconds to perform any tasks and return.
If the method does not return before time expires, the system may kill
the process altogether.
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.
So if you need to save data ALSO when user manually kill the app use applicationDidEnterBackground that it's called if your app support background mode.
If you need to execute code when your app isn’t running, there are
several options open to you depending on what you’re trying to do.
Background fetch will let your app run in the background for about 30 seconds at scheduled intervals. The goal of this is to fetch data
and prepare your UI for when the app runs next.
Push notifications let your app fetch fresh data from your server. You can make a message appear on the device if you want, but it’s not
required – silent push notifications let you skip that part.
Local notifications let you display an alert to the user, along with any media attachments you want and some options for the user to
select from. If they choose those options then your app can be
launched in the foreground or background to handle them.
From:
https://www.hackingwithswift.com/example-code/system/how-to-run-code-when-your-app-is-terminated
You can also use Silent Push Notification
as I mentioned in a similar question: https://stackoverflow.com/a/57245980/6157415
In the GCM client guide for iOS, it shows you to call [[GCMService sharedInstance] connectWithHandler:...] in -applicationDidBecomeActive:, and later it shows you to call [[GCMService sharedInstance] disconnect]; in -applicationDidEnterBackground:.
The placement of connect and disconnect seems asymmetric to me. Is there a reason it is done this way? I feel that it should either be
Connect in -applicationDidBecomeActive: and disconnect in -applicationWillResignActive:, or
Connect in -applicationWillEnterForeground: and disconnect in -applicationDidEnterBackground:
If you look at the Execution States for Apps in Apple's App Programming Guide for iOS, it states that:
applicationWillResignActive:—Lets you know that your app is transitioning away from being the foreground app. Use this method to put your app into a quiescent state.
Compare to
applicationDidEnterBackground:—Lets you know that your app is now running in the background and may be suspended at any time.
For example, if you press the Home button of your device twice, the applicationWillResignActive method will be call immediately, your app is in a transitioning state now. see the image below (Your app leave foreground, but not yet in background):
If you select other app, the applicationDidEnterBackground of your app will be called, and your app enters background. So it is better to call GCMService.sharedInstance().disconnect() when your app is actually in a background state compared to a transitioning state.
Similarly, applicationWillEnterForeground vs applicationDidBecomeActive.
If you are in other app now, and you switch back to your own app, the applicationWillEnterForeground will be called immediately, but your app is might still in an inactive state(which is currently not receiving events).
It might only last for less than half a second, and applicationDidBecomeActive will called immediately after. Because applicationWillEnterForeground is more a transitioning state. So it is better to call GCMService.sharedInstance().connectWithHandler() when your app is actually in an active state(app is running in the foreground and is receiving events).
You might think applicationDidBecomeActive and applicationDidEnterBackground are asymmetric, but these two methods all have a word Did. But two other methods have the word Will, which means a transitioning state.
You can also see the Life Cycle picture from this StackOverflow answer.
Edit after #user102008's comment:
If you do connect in the applicationWillEnterForeground method, then connect method wont be called when the app is first launched. Also, the GCM doc says that Call disconnect when the client app is in the background or when it needs to stop exponential background connection retry, so I think its better to call disconnect when the app is in background, instead of when phone call comes? I think you can use enum with 3 different states connecting , not connected and connected, when the applicationDidBecomeActive method is called, you can check if its not connected or connecting, then you call the connect method again.
I was wondering which event in my AppDelegate is called when I swipe out my running app from within the app switcher? My target iOS is >= 7.0 (i.e. with multitasking support).
Maybe I just don't understand the multitasking paradigm itself... When I swipe out my app from within the app switcher, the app is going to be terminated, right? Or does it continue running in the background?
EDIT-1: Why I am asking: During startup of the app, I connect to my Linux game server by plain sockets. I want to disconnect the socket when the app terminates.
EDIT-2: I am not sure if I am using background execution. I register my network socket within a run-loop object to asynchronously receive inputs from the server socket. Moreover I have a kind of network service installed that periodically tests for network connectivity. This service is using SCNetworkReachabilityScheduleWithRunLoop.
There are situations where applicationWillTerminate: will be called and sometimes not.
More can be read up from the Apple documentation here
Excerpt:
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's interesting to note is that
After calling this method, the app also posts a
UIApplicationWillTerminateNotification notification to give interested
objects a chance to respond to the transition.
It is - (void)applicationWillTerminate:(UIApplication *)application
I have tried various samples from the web (the last one being this one) in order to get a better understanding of NSUrlSession.
What I was hoping to see: file downloads will continue even if the app that triggered them gets killed (for instance by the user through the task manager). However this does not seem to happen.
Is this a configuration issue or does background file transfer not work if the app gets terminated?
I thought the whole idea was that iOS will restart the app.
If the system kills your app and your background session has active downloads, your downloads will continue and the system will launch your app when the downloads complete. However, if a user force quits your app, all tasks get cancelled.
Documentation for backgroundSessionConfigurationWithIdentifier:
If an iOS app is terminated by the system and relaunched, the app can
use the same identifier to create a new configuration object and
session and retrieve the status of transfers that were in progress at
the time of termination. This behavior applies only for normal
termination of the app by the system. If the user terminates the app
from the multitasking screen, the system cancels all of the session’s
background transfers. In addition, the system does not automatically
relaunch apps that were force quit by the user. The user must
explicitly relaunch the app before transfers can begin again.
No - the app is not relaunched for background downloads when the user has force quit.
The iOS8 documentation for application:didReceiveRemoteNotification:fetchCompletionHandler: says:
Use this method to process incoming remote notifications for your app.
Unlike the application:didReceiveRemoteNotification: method, which is
called only when your app is running in the foreground, the system
calls this method when your app is running in the foreground or
background. In addition, if you enabled the remote notifications
background mode, the system launches your app (or wakes it from the
suspended state) and puts it in the background state when a push
notification arrives. However, the system does not automatically
launch your app if the user has force-quit it. In that situation, the
user must relaunch your app or restart the device before the system
attempts to launch your app automatically again.
I have created background nsurlsession to perform download task. It worked well when the app was in background. However, download task seems to be canceled and failed when I closed the app (double click "Home" button and swipe up), and it made me to download from the beginning again when I relaunched the app. According to Apple document, background transfer works even the app is no longer running. Am I doing anything wrong?
From the NSURLSessionConfiguration Class Reference:
If an iOS app is terminated by the system and relaunched, the app can use the same identifier to create a new configuration object and session and retrieve the status of transfers that were in progress at the time of termination. This behavior applies only for normal termination of the app by the system. If the user terminates the app from the multitasking screen, the system cancels all of the session’s background transfers.
So, while background transfers will continue if iOS itself closes your app during the normal course of things, if you force the quit from the multitasking screen, it will kill all your transfers.
The app is not relaunched for background downloads when the user has force quit.
The iOS8 documentation for application:didReceiveRemoteNotification:fetchCompletionHandler: says:
Use this method to process incoming remote notifications for your app.
Unlike the application:didReceiveRemoteNotification: method, which is
called only when your app is running in the foreground, the system
calls this method when your app is running in the foreground or
background. In addition, if you enabled the remote notifications
background mode, the system launches your app (or wakes it from the
suspended state) and puts it in the background state when a push
notification arrives. However, the system does not automatically
launch your app if the user has force-quit it. In that situation, the
user must relaunch your app or restart the device before the system
attempts to launch your app automatically again.
In the first paragraph of NSURLSession documentation, we can observe:
This API provides a rich set of delegate methods for supporting
authentication and gives your app the ability to perform background
downloads when your app is not running or, in iOS, while your app is
suspended.
Now notice where it states:
or, in iOS, while your app is suspended.
It looks like only OS X applications have the ability to finish background tasks while your app isn't running.