MPMusicPlayerController: iPod stops sending notifications, when iPod App is terminated in background - crash

I am using a music player property for iPod player controller.
// .h
#property (nonatomic, retain) MPMusicPlayerController *ipodPlayer;
// .m
ipodPlayer = [MPMusicPlayerController iPodMusicPlayer];
NSNotificationCenter *notificationCenter = [NSNotificationCenter defaultCenter];
[notificationCenter addObserver:self selector:#selector(changedPlaybackState:) name:MPMusicPlayerControllerPlaybackStateDidChangeNotification object:ipodPlayer];
[notificationCenter addObserver:self selector:#selector(changedNowPlayingItem:) name:MPMusicPlayerControllerNowPlayingItemDidChangeNotification object:ipodPlayer];
[ipodPlayer beginGeneratingPlaybackNotifications];
During background processing, if iPod player app is terminated, the console prints out:
MediaPlayer: Message playbackState timed out.
If it doesn't crash(or freezes, slowing performance), the notification is not being sent to my observing methods anymore. I can still send messages like:
[ipodPlayer pause];
[ipodPlayer play];
[ipodPlayer skipToNextItem];
[ipodPlayer skipToPreviousItem];
but can't receive any notifications
My questions are:
Is there are way to reassign, reload pointers during runtime? How can I restore the property to be just like when it's first launched?
How can I catch the messge:"MediaPlayer: Message playbackState timed out." in console output? This is not like using NSLog.
Thank you for helping me.
UPDATED:
It seems like using assign or weak for ipodPlayer property was the solution. Also, accessing it is done with assumption that the property may not be there. After many trials and a year of actually using it in my app, I think this was the right solution.

I had similar issue with my MpMoviePlayerController in iOS 5. I Found a fix that took care of it. It might work here as well.
Add:
[[UIApplication sharedApplication] beginReceivingRemoteControlEvents];
in viewDidLoad.
More my other post

Related

Code on window closing in App Delegate?

I have a Mac application where I would like to save some values when the window closes. However, I can't figure out how to do this. I have the application delegate controlling the main window (which may not be the way you are supposed to do it, and I probably shouldn't have done it this way if what I've read previously is correct) and I can't figure out for the life of me how to do this! I believe it can be done using a NSWindowController, but can you do it in the app delegate? Thanks!
Use NSWindowWillCloseNotification.
- (void)applicationWillFinishLaunching:(NSNotification *)notification {
NSNotificationCenter *nc = [NSNotificationCenter defaultCenter];
[nc addObserver:self selector:#selector(windowWillClose:) name:NSWindowWillCloseNotification object:window];
}
- (void)windowWillClose:(NSNotification *)notification {
// Your code for saving data
}

UIKeyboardWillChangeFrame notification not sent when moving undocked keyboard

I am trying to make a toolbar that is always above the keyboard, pretty much like in Apple's native Messages app. If you take a closer look at that app on the iPad, try undocking the keyboard and the dragging it around by holding than button in its bottom right. You will notice that Apple's toolbar tracks the keyboard's position quite nicely.
Trying to replicate that behavior, I subscribed to both the keyboard-will-change-frame and keyboard-did-change-frame notifications. However, to my disappointment I found that the first one is sent when the user starts the dragging, and the latter is sent when the keyboard snaps to its final position, but there are no notifications I could find that are sent in between. Am I missing something? Here's what I am talking about:
This is the code I use to subscribe to notifications:
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(keyboardWillChangeFrame:) name:UIKeyboardWillChangeFrameNotification object:nil];
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(keyboardDidChangeFrame:) name:UIKeyboardDidChangeFrameNotification object:nil];
These are my listener functions:
- (void)keyboardWillChangeFrame:(NSNotification *)notification{
NSLog(#"will change");
// [self alignToolbarForKeyboard:notification action:#"will_change_frame"];
}
- (void)keyboardDidChangeFrame:(NSNotification *)notification{
NSLog(#"did change");
// [self alignToolbarForKeyboard:notification action:#"did_change_frame"];
}
I commented out the call both listener functions make to make sure it's not blocking anything. Is there a notification type I do not know of? I did look at the UIWindow reference but couldn't find anything beyond what I already have. Just to make sure, I also checked whether the show/hide notifications are sent during the movement. They aren't.
Any ideas? Thanks!

UIImagePickerController and application background modes

I’m building an application which supports both video playback and recording (not simultaneously, it’s just two separate features it provides). In order to make the videos play after the app enters background and gets back, I had to add an App plays audio item to Required background modes in the plist (I’m using MPMoviePlayerController for playback).
This, however, causes a problem with my video recording (I’m using UIImagePickerController for it). Basically, even after the picker is dismissed (either by the Cancel button or when it’s finished picking media), the app still keeps the audio recording session running.
If I remove the App plays audio item from the plist, the ImagePickerController’s audio session stops misbehaving, but then I can’t resume the playback of MPMoviePlayerViewController on switching to app from background mode.
Is there a way I can customise the handling of the audio session so that both the MPMoviePlayerController and UIImagePickerController can work properly?
yes, there is a way you can customize the handling of the audio session for what you desire: don't try to set the App plays audio setting.
instead, in your AppDelegate code (which is usually in AppDelegate.m from the simple wizard projects provided), supply code in the applicationWillResignActive: method that will stop the playback in your MPMoviePlayerController, and then use applicationDidBecomeActive: to resume the playback at the point at which you paused it if so desired.
this will not only allow the video to be resumed after a temporary pause, but it will allow you to save the state so that the video can be resumed in case the app is removed from memory or in case the user causes it to be quit.
You can scratch the background modes and instead use notifications to pause/resume your player. See UIApplicationDidBecomeActiveNotification and UIApplicationWillResignActiveNotification in the UIApplication class reference.
You can snag some code and see this implemented in this class. Here is some of the relevant code from that class:
[[NSNotificationCenter defaultCenter] addObserver:self
selector:#selector(_didBecomeActive:)
name:UIApplicationDidBecomeActiveNotification
object:nil];
[[NSNotificationCenter defaultCenter] addObserver:self
selector:#selector(_willResignActive:)
name:UIApplicationWillResignActiveNotification
object:nil];
- (void) _didBecomeActive:(NSNotification*)notification {
if (_wasPlaying) {
[_movieController play];
}
}
- (void) _willResignActive:(NSNotification*)notification {
_wasPlaying = _movieController.currentPlaybackRate != 0.0;
if (_wasPlaying) {
[_movieController pause];
}
}

Stop CLLocationManager when user stops app using Fast App Switcher

I am developing an iOS Application which uses CLLocationManager to get GPS Data.
What's a proper way to stop CLLocationManager when the user presses the home button and then kills the app using fast app switcher?
How can I stop the CLLocationManager then to stop polling for GPS data? Does anybody knows a good way to achieve this, cause normally I can't execute any code when the user kills the application...
EDIT:
When I am sending the application to background, it should still get significantLocationChanges, so I can't stop CLLocationManager when the application is sent to the background!
In the AppDelegate there is a method called applicationWillResignActive. This method runs when your app is about to be sent to the background. If you have access to the CLLocationManager from your AppDelegate you can stop it right there. Otherwise you can post a Notification from applicationWillResignActive and whatever class has your CLLocationManager can subscribe to that Notification and handle it from there.
Post a Notification like this:
[[NSNotificationCenter defaultCenter] postNotificationName:#"appWillResignActive" object:nil];
And subscribe like this:
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(handleResign:) name:#"appWillResignActive" object:nil];
Then some method like this:
- (void)handleResign: (NSNotification *)notification
{
// stop CLLocationManager
}
EDIT:
If you want to run code when the app is being killed then you can run that code in this method in the AppDelegate:
- (void)applicationWillTerminate:(UIApplication *)application

How to simulate applicationWillResignActive message?

How can one simulate applicationWillResignActive to be called?
Locking screen, going to main menu, simulating phone call - none seemed to have helped.
In case I expect things that won't happen, let me tell you more: I subscribe to this message and when that happens hope that notification is sent to a method as listed below:
UIApplication *app = [UIApplication sharedApplication];
[[NSNotificationCenter defaultCenter] addObserver:self
selector:#selector(applicationWillResignActive:)
name: UIApplicationWillResignActiveNotification object:app];
Your code is valid, it works for me. And locking the screen or hitting the home button will cause this notification to be posted.
*(One caveat to this is if your device does not support multitasking or if you have the "Application does not run in background" property set to yes in your *info.plist. In which case it will go straight to the "UIApplicationWillTerminateNotification" notification)
So barring that there there are two possiblities:
1) Your addObserver code is not being called, ie. It's in the wrong method.
To test try this:
UIApplication *app = [UIApplication sharedApplication];
[[NSNotificationCenter defaultCenter] addObserver:self
selector:#selector(applicationWillResignActive:)
name:UIApplicationWillResignActiveNotification
object:app];
NSLog(#"Observer added");
2) The observer method is not being called properly. Which requires the same method as the at selector.
To test try this:
-(void)applicationWillResignActive:(NSNotification *)notification{
NSLog(#"applicationWillResignActive");
}
Just as an additional point, if you want to see in action which of these notifications are called and when. In your AppDelegate class put the line NSLog(#"%#", NSStringFromSelector(_cmd)); in each of the -application... methods and it will log them when they are called. It's a nice hands on way to get to know them.