I have released almost same app in AppStore.
It plays songs using [MPMusicPlayerController applicationMusicPlayer].
Yes, MPMusicPlayerControllerPlaybackStateDidChangeNotification did not came to background app.
So, I used the NSTimer to determine when the song maybe ended.
You set NSTimer to 1sec. I set NSTimer to the period of the song's play-time. i.e. when the song has 3 minutes 15 secs time, I set the NSTimer to 3 minutes 15 secs.
When user pause the music, I pause the NSTimer.
And when user resume the music, I set the NSTimer to the rest time of the song.
It works very well.
I hope this may help you.
Sorry for my cheep English.
Step you may need;
- Set audio background mode in Capabilities Setting. (You done)
- Use beginBackgroundTaskWithExpirationHandler in App Delegate.
- (void)applicationWillResignActive:(UIApplication *)application
{
_bgid = [[UIApplication sharedApplication] beginBackgroundTaskWithExpirationHandler:^(void)
{
[[UIApplication sharedApplication] endBackgroundTask:_bgid];
_bgid = UIBackgroundTaskInvalid;
}];
}
- (void)applicationDidBecomeActive:(UIApplication *)application
{
if ( _bgid != UIBackgroundTaskInvalid )
{
[[UIApplication sharedApplication] endBackgroundTask:_bgid];
}
}
3.Use NSTimer in non-repeated mode.
#import "NMCountdownTimer.h"
@interface NMCountdownTimer ()
{
@private
NSTimer *_timer;
NSTimeInterval _rest;
NMMethodCompletion _completion;
}
@end
@implementation NMCountdownTimer
- (void)start:(NSTimeInterval)secs completion:(NMMethodCompletion _Nullable)completion
{
_completion = completion;
if ( secs > 0.0f )
{
NSLog(@"[ ] start (secs:%f)", secs);
_timer = [NSTimer scheduledTimerWithTimeInterval:secs target:self selector:@selector(fired) userInfo:nil repeats:FALSE];
_rest = 0.0f;
}
}
- (void)pause
{
if ( _timer && ( _rest == 0.0f ) )
{
_rest = [[_timer fireDate] timeIntervalSinceReferenceDate] - [[NSDate date] timeIntervalSinceReferenceDate];
NSLog(@"[ ] pause (rest:%f)", _rest);
[_timer invalidate];
_timer = nil;
}
}
- (void)resume
{
if ( ( ! _timer ) && ( _rest > 0.0f ) )
{
NSLog(@"[ ] resume (rest:%f)", _rest);
_timer = [NSTimer scheduledTimerWithTimeInterval:_rest target:self selector:@selector(fired) userInfo:nil repeats:FALSE];
_rest = 0.0f;
}
}
- (void)kill
{
[_timer invalidate];
_timer = nil;
_rest = 0.0f;
_completion = nil;
NSLog(@"[ ] kill");
}
- (void)fired
{
[_timer invalidate];
_timer = nil;
_rest = 0.0f;
if ( _completion )
{
_completion ( nil );
}
NSLog(@"[ ] fired");
}
- (void)dealloc
{
#if ( Namera_SHOW_DEALLOC && FALSE )
NSLog(@"[DEA] %@ NMCountdownTimer", self );
#endif
_timer = nil;
_completion = nil;
}
@end
somewhere in your code like this;
NSTimeInterval playback_duration = [[song valueForProperty: MPMediaItemPropertyPlaybackDuration] doubleValue];
....
MPMediaItemCollection *collection = [MPMediaItemCollection collectionWithItems:[NSArray arrayWithObject:song]];
[_musicCtr setQueueWithItemCollection:collection];
[_musicCtr play];
[_countdown_timer start:playback_duration completion:^(id _Nullable object)
{
[weakSelf execute_next_song];
}];