I'm writing a basic music player app but having some problems when it comes to handling the app state transitions.
I'm using Swift 3 and MPMusicPlayerController.systemMusicPlayer()
The goal is this:
1) Keep music playing when user taps the Home button and app enters bg (works)
2) Stop the player ( myMP.stop() ) if the user the quits the app (works sometimes, throws error other times)
I traced the flows using print statements based on possible actions and got this:
Flow 2 is what I would expect, but Flow 1 throws an error when the app is closed - I would expect "will terminate" here.
EDIT: The main issue is that when exiting the app using Flow 1, "will terminate" is never called - therefore "myMP.stop()" is never called and the player continues to play after the app has exited.
There is a distinct difference in behavior if you click Home once (Flow 1) or double (Flow 2) while the app is active.
Why do I get two different responses from what should be the same actions?
EDIT: Most importantly, how do I stop the MediaPlayer for Flow 1 if it never gets to "will terminate" ?
EDIT:
Here is some sample code that replicates the issue:
AppDelegate.swift
// // AppDelegate.swift // Jumbo Player // import UIKit //import MediaPlayer //doesn't matter where this is declared - here or in ViewController - same results //let myMP:MPMusicPlayerController = MPMusicPlayerController.systemMusicPlayer() @UIApplicationMain class AppDelegate: UIResponder, UIApplicationDelegate { var window: UIWindow? func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> 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. print("applicationWillResignActive") } 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. print("applicationDidEnterBackground") } func applicationDidReceiveMemoryWarning(_ application: UIApplication) { print("applicationDidReceiveMemoryWarning") } 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. print("applicationWillEnterForeground") } 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. print("applicationDidBecomeActive") } func applicationWillTerminate(_ application: UIApplication) { // Called when the application is about to terminate. Save data if appropriate. See also applicationDidEnterBackground:. print("applicationWillTerminate") myMP.stop(); } }
ViewController.swift
// // ViewController.swift // junkplayer // import UIKit import MediaPlayer let myMP:MPMusicPlayerController = MPMusicPlayerController.systemMusicPlayer() class ViewController: UIViewController { @IBOutlet weak var xxx: UIButton! override func viewDidLoad() { super.viewDidLoad() // Do any additional setup after loading the view, typically from a nib. let qrySongs = MPMediaQuery.songs() myMP.setQueue(with: qrySongs) } @IBAction func playbut(_ sender: UIButton) { myMP.play() } }
Download the project here: www.NextCoInc.com/public/junkplayer.zip
The "terminated due to signal 9" message just means your app was terminated by a SIGKILL signal. The OS sends that signal whenever your app is terminated involuntarily, whether it's because of memory pressure (or several other reasons not relevant to this discussion), or the user explicitly killing your app by double tapping the Home button and swiping it away.
In your case, the user is explicitly killing your application, so the "Terminated due to signal 9" message is completely expected. If your application is the current foreground application, your applicationWillTerminate method will get called, as shown in your logic flow outline above (Flow 2). If your application is NOT the current foreground application (Flow 1), your applicationWillTerminate
method will NOT get called if your application is in a suspended state. This is expected behavior. Also note the distinction between "background state" and "suspended state". They are not the same thing.
So if I'm understanding you correctly, it sounds like the problem is that the audio continues playing after your application is terminated by the user (Flow 1). That means you are doing something wrong in your handling of the MPMusicPlayerController
, because it should handle that state transition automatically.
Make sure you've set the correct UIBackgroundMode for your app. Setting the wrong background mode can cause your application to misbehave because the OS only allows certain operations while in background, depending on what background mode you've set. Setting the wrong mode (or trying to do things that are explicitly disallowed in the mode you've set) will cause your app to be suspended or terminated.
Make sure you've set up your audio session correctly.
Make sure you are responding correctly to the music player notifications - in particular, make sure you're calling beginGeneratingPlaybackNotifications
and endGeneratingPlaybackNotifications
appropriately, and that you are handling those notifications correctly. Check your notification handlers to make sure you aren't doing anything silly in there. Make sure your controller doesn't go out of scope or otherwise get released before you've called endGeneratingPlaybackNotifications
.
If you've done everything else correctly, an MPMusicPlayerController
pretty much manages itself, so you shouldn't have to do anything special to make it work when your app goes to the background (aside from setting the correct UIBackgroundMode
, of course). As a last resort, start commenting out code until your app is just a barebones "open-an-audio-file-and-play-it" application, and see if it exits properly then. If it does, you can start uncommenting code piece-by-piece until it fails - then you know what part of your app is causing the problem and you can narrow it down from there.
Changes in Settings
app for app permissions like Camera
or Photo
usage, can result in crash(hard refresh) with this signal.
You can find related behaviors in the link below:
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With