iosobjective-cavaudioplayermpnowplayinginfocenter

How to Implementing MPNowPlayingInfoCenter using AvAudioPlayer


I am in the midst of creating an iOS mobile app for a client that will play a variety of audio tracks.

One of the features that I wanted to implement was to display information about a currently-playing audio track on the lock screen and banner . This is one of those simple convenience to a mobile user and a must-have if your app has background audio playing. Personally, I use this feature all the time!


Solution

  • You should first observe the AVPlayerItem of AVAudioPlayer like so:

    [playerItem addObserver:self forKeyPath:@"timedMetadata" options:NSKeyValueObservingOptionNew context:nil];
    

    Then create some global variables:

    NSString *title;
    NSString *artist;
    UIImage *artwork;
    

    You would then probably need a function like the one below, so you observe the key path timedMetadata and update the InfoCenter through updateInfoCenterWithTitle:andArtist:andCover:.

    - (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary  *)change context:(void *)context
    {
        if ([keyPath isEqualToString:@"timedMetadata"])
        {
            for (int i = 0; i < [audioPlayer.currentItem.timedMetadata count]; i++)
            {
                AVMetadataItem *metaData = [audioPlayer.currentItem.timedMetadata objectAtIndex:i];
    
                if ([[metaData commonKey] isEqualToString:AVMetadataCommonKeyArtist]) {
                    artist = (NSString *)metaData.value;
                }
                else if ([[metaData commonKey] isEqualToString:AVMetadataCommonKeyTitle])
                {
                    title = (NSString *)metaData.value;
                    [self updateInfoCenterWithTitle:title andArtist:artist andCover:artwork];
                }
                else if ([[metaData commonKey] isEqualToString:AVMetadataCommonKeyArtwork])
                {
                    if ([metaData.keySpace isEqualToString:AVMetadataKeySpaceID3])
                    {
                        NSDictionary *dictionary = [metaData.value copyWithZone:nil];
                        artwork = [UIImage imageWithData:[dictionary objectForKey:@"data"]]];
                    } else if ([metaData.keySpace isEqualToString:AVMetadataKeySpaceiTunes]) {
                        artwork = [UIImage imageWithData:[metaData.value copyWithZone:nil]];
                    }
                }
                else {
                    NSLog(@"%@ --> %@", [metaData commonKey], metaData.value);
                }
            }
        }
    }
    

    This is where the magic happens:

    - (void)updateInfoCenterWithTitle:(NSString *)title andArtist:(NSString *)artist andCover:(UIImage *)cover
    {
        if (cover == nil) {
            cover = [UIImage imageNamed:@"defaultCover"];
        }
    
        MPNowPlayingInfoCenter *infoCenter = [MPNowPlayingInfoCenter defaultCenter];
        [infoCenter setNowPlayingInfo:@{MPMediaItemPropertyTitle:title,
                                        MPMediaItemPropertyArtist:artist,
                                        MPMediaItemPropertyArtwork:[[MPMediaItemArtwork alloc] initWithImage:cover]}];
    }