How to play MPMediaItem in MPMusicPlayerController in iOS?
Asked Answered
Y

5

5

I am trying to play MPMedaiItem in MPMusicPlayerController in iOS. In my case , i have a UITableView that show songs from playlist. When i tap cell on UITableView , i want to play that song with MPMusicPlayerController. And i also want to skip next songs from playlist when i tap Next Button. How can i play it?

Here is some of my codes that write in didSelected Method of UITableView. That doesn't play anything.

    MPMediaItemCollection *songs = [self.arrayOfSongs objectAtIndex:indexPath.row ];

    MPMediaItem *item = [songs representativeItem ];

    NSLog(@"%@",[item valueForProperty:MPMediaItemPropertyTitle ]);

    [self.player setNowPlayingItem:[item valueForProperty:MPMediaItemPropertyAssetURL]];

    [self.player play ];
Yuu answered 9/1, 2013 at 6:9 Comment(0)
G
11

I know this is a little late, but your problem is that MPMusicPlayerController's nowPlayingItem property expects a MPMediaItem object, and you're passing it an NSString containing the URL of the asset. Here's an example of how this could be accomplished.

MPMusicPlayerController *controller = [MPMusicPlayerController iPodMusicPlayer];

MPMediaItemCollection *collection = [[MPMediaItemCollection alloc] initWithItems:arrayOfMediaItems];
MPMediaItem *item = [collection representativeItem];

[controller setQueueWithItemCollection:collection];
[controller setNowPlayingItem:item];

[controller prepareToPlay];
[controller play];
Gereld answered 19/12, 2013 at 5:29 Comment(1)
iPodMusicPlayer is long outdated. One alternative is MPMusicPlayerController.systemMusicPlayer.Anode
B
4

I find it easier to use AVPlayer in an instance like this.

in your header file declare the AVPlayer object;

AVPlayer *audioPlayer;

Then in your method file use something like:

if(!audioPlayer){
   audioPlayer = [[AVPlayer alloc] initWithURL:[item valueForProperty:MPMediaItemPropertyAssetURL]];
} else {
   [audioPlayer replaceCurrentItemWithPlayerItem:[AVPlayerItem playerItemWithURL:itemURL]];
}
[audioPlayer play];
Bequest answered 25/1, 2013 at 18:15 Comment(1)
Not sure this will work if item URL is nil (not downloaded)Unkenned
R
0

Adding to Mick and Tom's responses:

iOS

For iOS and iPadOS, you must use MPMusicPlayerController.systemMusicPlayer or MPMusicPlayerController.applicationMusicPlayer:

https://developer.apple.com/documentation/mediaplayer/mpmusicplayercontroller/1624179-systemmusicplayer

https://developer.apple.com/documentation/mediaplayer/mpmusicplayercontroller/1624156-applicationmusicplayer

On iOS, the assetURL will always be nil for DRM protected content, even if the file is downloaded locally.

It’s easier to just pass all MPMediaItems into a .applicationMusicPlayer.

macOS

For macOS, the MPMusicPlayerController.systemMusicPlayer is broken, so you need to use AVPlayer passing the assetURL:

https://developer.apple.com/documentation/avfoundation/avplayer

https://developer.apple.com/documentation/mediaplayer/mpmediaitem/1621707-asseturl

On macOS, if the file is DRM protected, with a .m4p extension, you need to use MPMusicPlayerController.systemMusicPlayer to play the DRM content, even though ITLibrary exposes the URL:

https://developer.apple.com/documentation/ituneslibrary/itlibrary

Unfortunately, AVPlayer cannot decode DRM content.

Additionally, as of June 3rd, 2024, MPMusicPlayerController.systemMusicPlayer on macOS will launch the Apple Music app in order to play the songs.

There is no current way to play DRM protected songs without launching Apple Music on macOS.

Hopefully Apple fixes this in future versions of macOS.

Retrench answered 3/6 at 22:25 Comment(0)
R
0

Here is all the code required to play songs from the media library on iOS using UIKit.

You must add NSAppleMusicUsageDescription in the Info.plist to access the MPMediaLibrary.

https://developer.apple.com/documentation/bundleresources/information_property_list/nsapplemusicusagedescription

Note: The code in this response is 100% royalty free. It utilizes Apple's MPMusicPlayerController.systemMusicPlayer, which is the only way to play iTunes DRM protected content:

https://developer.apple.com/documentation/mediaplayer/mpmusicplayercontroller/1624179-systemmusicplayer

macOS: Currently, as of July 25th, 2024, the MPMusicPlayerController.systemMusicPlayer will always open the Apple Music app to play songs. This code only seems to work properly on iOS and iPadOS.

iTunes Music Player

import UIKit
import MediaPlayer

class ViewController : UIViewController {
    
    // MARK: - Properties -
    
    @IBOutlet var tableView : UITableView?
    var dataSource : Array<MPMediaItem> = []

    // MARK: - Lifecycle -
    
    override func viewDidLoad() {
        
        super.viewDidLoad()
        
        // Do any additional setup after loading the view.
        
        self.requestAuthorization()
        
        self.getAllSongs()
        
        self.tableView?.reloadData()

    }
    
    // MARK: - iTunes Authorization -

    func requestAuthorization () {
        
        if MPMediaLibrary.authorizationStatus() != .authorized {

            MPMediaLibrary.requestAuthorization(
                { (status) in

                    switch status {
                    
                        case .notDetermined:
                            print("notDetermined")
                            
                        case .denied:
                            print("denied")
                            
                        case .restricted:
                            print("restricted")
                            
                        case .authorized:
                            DispatchQueue.main.async {
                                
                                self.getAllSongs()
                                
                                self.tableView?.reloadData()
                                
                            }
                            
                            print("authorized")
                            
                        @unknown default:
                            print("unknown")

                    }

                }
                
            )

        }

    }

    // MARK: - MPMediaQuery -
    
    func getAllSongs() {
        
        let mediaQuery = MPMediaQuery.init()
        
        mediaQuery.addFilterPredicate(
            MPMediaPropertyPredicate.init(
                value: MPMediaType.music.rawValue,
                forProperty: MPMediaItemPropertyMediaType
            )
        )
        
        self.dataSource = mediaQuery.items ?? []
        
    }

}

// MARK: - TableView -

extension ViewController : UITableViewDelegate,  UITableViewDataSource {
    
    func numberOfSections(
        in tableView: UITableView
    ) -> Int {
        
        return 1
        
    }
    
    func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
        
        return dataSource.count
        
    }
    
    func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
        
        let cell = UITableViewCell(style: .value1, reuseIdentifier: "Cell")

        let mediaItem = dataSource[indexPath.row]
        
        let title = mediaItem.title ?? "Unknown Title"
        let artist = mediaItem.artist ?? "Unknown Artist"

        cell.textLabel?.text = "\(title) - \(artist)"
        
        cell.detailTextLabel?.text = "\(mediaItem.playbackDuration)"

        cell.imageView?.image = mediaItem.artwork?.image(at: CGSize(width: 100, height: 100))

        return cell
    }
    
    func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
        
        let mediaItem = dataSource[indexPath.row]
        
        var playlistItems : Array<MPMediaItem> = []
                                
        playlistItems.append(
            mediaItem
        )
        
        let collection = MPMediaItemCollection(
            items: playlistItems
        )
        
        let collectionItem = collection.representativeItem

        MPMusicPlayerController.systemMusicPlayer.setQueue(
            with: collection
        )
        
        MPMusicPlayerController.systemMusicPlayer.nowPlayingItem = mediaItem
        
        MPMusicPlayerController.systemMusicPlayer.prepareToPlay()

        MPMusicPlayerController.systemMusicPlayer.currentPlaybackTime = 0
        
        MPMusicPlayerController.systemMusicPlayer.play()

    }
    
}
Retrench answered 5/6 at 12:43 Comment(0)
R
-1

Here's a SwiftUI version:

MediaStore

import SwiftUI
import Combine
import MediaPlayer

class MediaStore : ObservableObject {
    
    // MARK: - Properties -

    @Published var dataArray : [MPMediaItem] = [] {
        didSet {
            didChange.send(self)
        }
    }
    
    @Published var songArray : [MPMediaItem] = [] {
        didSet {
            didChange.send(self)
        }
    }

    @Published var albumArray : [MPMediaItem] = [] {
        didSet {
            didChange.send(self)
        }
    }

    var didChange = PassthroughSubject<MediaStore, Never>()
    
    // MARK: - Functions -

    func getAllArtists() {
        
        let query : MPMediaQuery = MPMediaQuery.artists()
        let allArtists = query.collections
        
        dataArray = []

        guard allArtists != nil else {
            
            return

        }

        // loop
        
        for collection in allArtists! {

            let item : MPMediaItem? = collection.representativeItem

            if item != nil,
                item?.playbackStoreID == "" {
            
                dataArray.append(item!)
                
            }
            
        }

    }
    
    func getAllSongs(
        artistId : NSNumber?
    ){
                        
        let everything : MPMediaQuery = MPMediaQuery.init()
        
        everything.groupingType = MPMediaGrouping.albumArtist
        
        // remove Cloud Items
                
        let predicate = MPMediaPropertyPredicate.init(
            value: artistId,
            forProperty: MPMediaItemPropertyArtistPersistentID
        )

        everything.addFilterPredicate(
            predicate
        )

        // songArray
        
        songArray = everything.items ?? []
        
        // albumArray
        
        for song in songArray {
            
            if albumArray.contains(
                where: { mediaItem in
                
                    mediaItem.albumPersistentID == song.albumPersistentID
                    
                }
            ) {
                
                // do nothing
                
            } else {
                
                albumArray.append(song)

            }
            
        }

    }
    
}

AudioPlayerViewModel

import SwiftUI
import AVFoundation
import MediaPlayer

class AudioPlayerViewModel : ObservableObject {
    
    // MARK: - Properties -
    
    var audioPlayer : AVAudioPlayer?
        
    @Published var isPlaying = false
    
    @Published var currentItem : MPMediaItem?

    // MARK: - Init -

    init() {
                
    }
    
    func setSound(
        url: URL
    ) {
        
        print("setSound: url: \(url)")
            
        do {
            
            self.audioPlayer = try AVAudioPlayer(
                contentsOf: url
            )
            
        } catch {
            
            print("AVAudioPlayer could not be instantiated.")
            
        }
        
    }
    
    func playOrPause() {
        
        guard let player = audioPlayer else {
            
            return
            
        }
        
        if player.isPlaying {
                        
            player.pause()
            
            isPlaying = false
            
        } else {
            
            player.play()
            
            isPlaying = true
            
        }
        
    }
    
}

ContentView

import SwiftUI
import MediaPlayer

struct ContentView : View {
    
    // MARK: - Properties -
    
    @StateObject var mediaStore : MediaStore = MediaStore()
    
    @StateObject var audioPlayerViewModel = AudioPlayerViewModel()

    // MARK: - View -

    var body : some View {

        // MARK: Player
            
        HStack {
            
            Spacer()
            
            VStack {
                
                HStack {
                    
                    Spacer()

                    // title
                    
                    Text(
                        audioPlayerViewModel.currentItem?.title ?? "No Song Has Been Selected"
                    )
                    
                    Spacer()

                }
                
                HStack{
                    
                    Spacer()

                    // Rewind
                    
                    Button {
                        
                        
                    } label: {
                        
                        Image(
                            systemName: "backward.fill"
                        )
                        .font(
                            .title
                        )
                        
                    }
                    
                    // Play / Pause
                    
                    Button {
                        
                        audioPlayerViewModel.playOrPause()
                        
                    } label: {
                        
                        if audioPlayerViewModel.isPlaying {
                            
                            Image(
                                systemName: "pause.fill"
                            )
                            .font(
                                .title
                            )
                            
                        } else {
                            
                            Image(
                                systemName: "play.fill"
                            )
                            .font(
                                .title
                            )
                            
                        }
                        
                    }
                    
                    // FastForward
                    
                    Button {
                        
                        
                    } label: {
                        
                        Image(
                            systemName: "forward.fill"
                        )
                        .font(
                            .title
                        )
                        
                    }
                    
                    Spacer()

                }
                
            }
           
            Spacer()

        }.frame(
            height: 100
        )
        .padding(
            .top,
            0.0
        )
        .background(
            .clear
        )
        .onChange(
            of: audioPlayerViewModel.currentItem
        ) { oldValue, newValue in
            
            audioPlayerViewModel.setSound(
                url: audioPlayerViewModel.currentItem!.assetURL!
            )
            
            audioPlayerViewModel.playOrPause()

        }
                
        // MARK: NavigationView

        NavigationView {
            
            ScrollView {
                
                LazyVStack (
                    alignment: .leading,
                    spacing: 8.0
                ) {
                    
                    ForEach(
                        mediaStore.dataArray,
                        id: \.self
                    ) { mediaItem in
                        
                        // push view
                        
                        let artistId = mediaItem.value(
                            forProperty: MPMediaItemPropertyArtistPersistentID
                        ) as! NSNumber
                        
                        NavigationLink(
                            destination: SongView(
                                currentItem: $audioPlayerViewModel.currentItem,
                                artistId: artistId
                            )
                        ) {
                            
                            HStack {
                                
                                // image
                                
                                let img : UIImage = mediaItem.artwork?.image(
                                    at: CGSize(
                                        width: 50,
                                        height: 50
                                    )
                                ) ?? UIImage.init()
                                
                                Image.init(
                                    uiImage: img
                                )
                                .resizable()
                                .scaledToFit()
                                .frame(
                                    width: 50,
                                    height: 50,
                                    alignment: .leading
                                )
                                .padding(
                                    EdgeInsets(
                                        top: 0,
                                        leading: 0.0,
                                        bottom: 0,
                                        trailing: 16.0
                                    )
                                )
                                
                                // artist / album
                                
                                VStack(
                                    alignment: .leading
                                ) {
                                    
                                    Text.init(
                                        mediaItem.albumArtist ?? ""
                                    ).bold()
                                    
                                }
                                
                                // space
                                
                                Spacer()
                                
                            }.padding(
                                EdgeInsets(
                                    top: 0,
                                    leading: 16.0,
                                    bottom: 0,
                                    trailing: 16.0
                                )
                            )
                            
                        }
                        .font(
                            .system(
                                size: 16,
                                weight: .light
                            )
                        )
                        .foregroundColor(
                            .white
                        )
                        
                        // divider
                        
                        Divider()
                        
                    }
                    
                }
                
            }
            
        }
        .navigationBarTitleDisplayMode(
            .inline
        )
        .navigationTitle(
            ""
        )
        .navigationBarHidden(
            true
        )
        .onAppear(
            perform: {
                fetch()
            }
        )
        
    }
    
    // MARK: - Media -

    private func fetch() {
        
        mediaStore.getAllArtists()

    }

}

struct ContentView_Previews : PreviewProvider {
    
    static var previews: some View {
        
        ContentView()
        
    }
    
}

SongView

import SwiftUI
import MediaPlayer
import AVKit

struct SongView : View {
    
    // MARK: - Properties -
    
    @StateObject var mediaStore : MediaStore = MediaStore()
    
    @Binding var currentItem : MPMediaItem?

    var artistId : NSNumber?

    // MARK: - Lifecycle -

    var body : some View {
        
        ScrollView {
                   
            ForEach(
                mediaStore.albumArray,
                id: \.self
            ) { albumItem in
                
                // MARK: Title
                
                HStack {

                    // Artwork
                    
                    let img : UIImage = albumItem.artwork?.image(
                        at: CGSize(
                            width: 200,
                            height: 200
                        )
                    ) ?? UIImage.init()
                    
                    Image.init(
                        uiImage: img
                    )
                    .resizable()
                    .scaledToFit()
                    .frame(
                        width: 200,
                        height: 200,
                        alignment: .leading
                    )
                    .padding(
                        EdgeInsets(
                            top: 0,
                            leading: 16.0,
                            bottom: 0,
                            trailing: 16.0
                        )
                    )

                    // Title
                    
                    Text(
                        albumItem.albumTitle ?? "Unknown"
                    )
                    .font(
                        .system(
                            size: 36.0
                        )
                    )
                    
                    Spacer()
                    
                    // Release Date
                    
                    if #available(macCatalyst 15.0, *) {
                        
                        Text(
                            albumItem.releaseDate?.formatted() ?? ""
                        )
                        .font(
                            .system(
                                size: 16.0
                            )
                        )
                        .padding(
                            EdgeInsets(
                                top: 0,
                                leading: 16.0,
                                bottom: 0,
                                trailing: 16.0
                            )
                        )
                        
                    } else {
                        
                        // Fallback on earlier versions
                        
                    }
                    
                }

                // MARK: Album Count
                                
                Text(
                    "\(albumItem.discCount) ALBUM, \(albumItem.albumTrackCount) SONGS"
                )
                .font(
                    .system(
                        size: 16.0
                    )
                )
                .foregroundColor(
                    .gray
                )

                Divider()
                
                // MARK: Songs
                
                LazyVStack (
                    alignment: .leading,
                    spacing: 8.0
                ) {
                    
                    ForEach(
                        mediaStore.songArray,
                        id: \.self
                    ) { mediaItem in
                        
                        if mediaItem.albumPersistentID == albumItem.albumPersistentID {
                            
                            HStack {
                                
                                // MARK: Thumbnail
                                
                                // image
                                
                                let img : UIImage = mediaItem.artwork?.image(
                                    at: CGSize(
                                        width: 50,
                                        height: 50
                                    )
                                ) ?? UIImage.init()
                                
                                Image.init(
                                    uiImage: img
                                )
                                .resizable()
                                .scaledToFit()
                                .frame(
                                    width: 50,
                                    height: 50,
                                    alignment: .leading
                                )
                                .padding(
                                    EdgeInsets(
                                        top: 0,
                                        leading: 0.0,
                                        bottom: 0,
                                        trailing: 16.0
                                    )
                                )
                                
                                // MARK: Play Button
                                
                                Button {
                                    
                                    if mediaItem.isCloudItem == false {
                                        
                                        print("mediaItem.assetURL: \(String(describing: mediaItem.assetURL))")
                                        
                                        // access security
                                        
                                        let gotAccess = mediaItem.assetURL!.startAccessingSecurityScopedResource()
                                        
                                        if gotAccess {
                                            
                                            print("got access!")
                                            
                                            // create player
                                            
                                            currentItem = mediaItem

                                        } else {
                                            
                                            // warn
                                            
                                        }
                                        
                                    } else {
                                        
                                        print("mediaItem.playbackStoreID: \(mediaItem.playbackStoreID)")
                                        
                                        print("mediaItem.assetURL: \(String(describing: mediaItem.assetURL))")

                                    }
                                    
                                } label: {
                                    
                                    if mediaItem.isCloudItem {
                                        
                                        Image(
                                            systemName: "cloud"
                                        )
                                        .font(
                                            .title
                                        )
                                        
                                    } else {
                                        
                                        Image(
                                            systemName: "play.circle"
                                        )
                                        .font(
                                            .title
                                        )
                                        
                                    }
                                    
                                }
                                .padding(
                                    EdgeInsets(
                                        top: 0,
                                        leading: 0.0,
                                        bottom: 0,
                                        trailing: 16.0
                                    )
                                )
                                
                                // MARK: Track Number
                                
                                Text.init(
                                    "\(mediaItem.albumTrackNumber)"
                                )
                                .foregroundColor(
                                    .white
                                )
                                .padding(
                                    EdgeInsets(
                                        top: 0,
                                        leading: 0.0,
                                        bottom: 0,
                                        trailing: 16.0
                                    )
                                )
                                
                                // MARK: Artist / Album
                                
                                // artist / album
                                
                                VStack(
                                    alignment: .leading
                                ) {
                                    
                                    Text.init(
                                        mediaItem.albumArtist ?? ""
                                    ).bold()
                                    
                                    Text.init(
                                        mediaItem.albumTitle ?? ""
                                    )
                                    
                                }
                                
                                // space
                                
                                Spacer()
                                
                                // title
                                
                                Text.init(
                                    mediaItem.title ?? ""
                                )
                                
                            }.padding(
                                EdgeInsets(
                                    top: 0,
                                    leading: 16.0,
                                    bottom: 0,
                                    trailing: 16.0
                                )
                            )
                            .opacity(
                                
                                mediaItem.isCloudItem ? 0.7 : 1.0
                                
                            )
                            .gesture(
                                
                                TapGesture().onEnded(
                                    { value in
                                        
                                        print("tap")
                                        
                                                                                
                                    }
                                    
                                )
                                
                            )
                            
                            // divider
                            
                            Divider()
                            
                        }
                        
                    }
                    
                }
                
            }
            
        }.onAppear(
            perform: {
                fetch()
            }
        )
        
    }
    
    // MARK: - Media -

    private func fetch() {
        
        mediaStore.getAllSongs(
            artistId: artistId
        )

    }
    
}
Retrench answered 7/7 at 14:27 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.