Exploring Freelancing
Navigate freelancing as a developer; find clients, manage contracts, ensure timely payment, and learn from experiences!
- Exploring MusicKit
- Being Wrong
- Investigation
- Encoding and Decoding PlayParameters
- Setting the Queue Directly
- Conclusion
Exploring MusicKit
I wrote this book to fill in the gap for the documentation, filled with examples so that you do not have to spend time experimenting yourself.
I hope this book provides value to you and your app, and you enjoy working with MusicKit and integrating Apple Music into your app!
Use the discount code “early” for a massive 75% discount!
I searched for questions to answer on Stack Overflow and stumbled upon this question on Cannot convert value of type PlayParameters? to expected argument type MPMusicPlayerPlayParameters.
While PlayParameters
is a part of MusicKit for Swift, MPMusicPlayerPlayParameters
belongs to MediaPlayer
framework.
The author wants to use the parameter fetched from the latest API and use it to queue an application music player.
let player = MPMusicPlayerController.applicationMusicPlayer
let queue = MPMusicPlayerPlayParametersQueueDescriptor(playParametersQueue: heavyRotation[album].playParameters)
player.prepareToPlay()
player.play()
They printed the data, and it returns:
Optional(MusicKit.PlayParameters(id: l.vvLd05C, kind: "album", dictionary: ["isLibrary": true (Bool), "kind": "album" (String), "id": "l.vvLd05C" (String)]))
While both are somewhat incompatible, you’ve to find a way to make them work together.
Being Wrong
As I was excited to answer this question, I told them a wrong solution by making assumptions about how PlayParameters
works.
While MusicKit’s PlayParameters
is a structure, MPMusicPlayerPlayParameters
initializers expect a dictionary of [String: Any]
.
So, I answered as following:
let player = MPMusicPlayerController.applicationMusicPlayer
let parameters = heavyRotation[album].playParameters.dictionary
let queue = [MPMusicPlayerPlayParameters(parameters)]
let queue = MPMusicPlayerPlayParametersQueueDescriptor(playParametersQueue: queue)
player.prepareToPlay()
player.play()
But.
From the documentation, PlayParameters
is an opaque object that represents parameters to initiate playback of a playable music item using a music player.
So, you cannot just directly use the dot syntax and access the dictionary
value even if it prints in the console.
Investigation
And as expected, they responded that the playParameters
have no value of dictionary
.
I answered them wrongly.
I felt it was my duty to investigate further.
There were hours of back and forth while feeling helpless because of the lack of documentation.
Encoding and Decoding PlayParameters
At the end (with the help of my close friend), I realized that I could encode the value of structure and then decode it to MPMusicPlayerPlayParameters
.
In the end, the underlying properties are still the same!
So, after testing The Weeknd’s Dawn FM album, here’s the solution:
do {
/// Request and Response
let request = MusicCatalogResourceRequest<Album>(matching: \.id, equalTo: "1603171516")
let response = try await request.response()
/// Fetching Album
guard let album = response.items.first else { return }
/// Encoding parameters
let data = try JSONEncoder().encode(album.playParameters)
/// Decoding Parameters to `MPMusicPlayerPlayParameters`
let playParameters = try JSONDecoder().decode(MPMusicPlayerPlayParameters.self, from: data)
/// Creating the Queue
let queue = MPMusicPlayerPlayParametersQueueDescriptor(playParametersQueue: [playParameters])
let player = MPMusicPlayerController.applicationMusicPlayer
/// Setting the Queue
player.setQueue(with: queue)
try await player.prepareToPlay()
/// Finally, playing the album!
player.play()
} catch {
print(error)
}
And it sets the queue!
Setting the Queue Directly
I don’t understand the use of play parameters in the first place. From what I’ve seen, you can directly set the queue if you’re using ApplicationMusicPlayer
:
let request = MusicCatalogResourceRequest<Album>(matching: \.id, equalTo: "1603171516")
let response = try await request.response()
guard let album = response.items.first else { return }
let player = ApplicationMusicPlayer.shared
player.queue = [album] /// <- directly add the whole album to the queue
try await player.prepareToPlay()
try await player.play()
Conclusion
In this post, we learned about using PlayParameters
and MPMusicPlayerPlayParameters
.
While the author was looking for a way to play their local albums, this one works for Apple Music Catalog.
Every library album has a library ID instead of a normal ID. So, you cannot use it directly. As a workaround, I call another request to check if it exists on the catalog and then play it.
I’m still exploring how to use it in a better way because I’ve seen third-party Apple Music apps replicating it, so there’s something that I’m missing. A post for some other day!
Also, if you know how to do it, please let me know on Twitter! I hope you enjoyed reading!
Exploring Freelancing
Navigate freelancing as a developer; find clients, manage contracts, ensure timely payment, and learn from experiences!