The Keynote and Platforms State of the Union wrapped up a few hours ago, and I am still processing all the new features and improvements introduced, especially in SwiftUI.

I was looking forward to MusicKit 2.0, though. And the MusicKit team exceeded my expectations.

Last year, I started working on MusadoraKit, the ultimate companion to MusicKit, and it tried to fill in the gaps between MusicKit and Apple Music API. But, with the introduction of MusicKit 2.0, that gapped is now filled, and I can proudly say that MusadoraKit has been Sherlocked. I realized that when my build of Musadora failed because of ambiguous type lookups. Same names of the structures.

And it was pretty obvious because I built MusadoraKit following the MusicKit naming convention closely and expected the new structures to be named similarly.

Enough of the rant; let’s explore the new structures! I am familiar with most of the new structures as I have already implemented them in MusadoraKit.

AudioVariant

One of the Apple developer forums requests was accessing the different kinds of audio variants like Dolby Atmos, Lossless, Hi-Res Lossless, etc. Both Apple Music API and MusicKit support audio variants with the new release. And we will find them as an array in the Song and Album structure:

public var audioVariants: [AudioVariant]? { get }

Another variable that we have access to is the Apple Digital Masters. From the documentation:

Apple Digital Masters start from 24-bit files and are optimized to bring the best-sounding audio to Apple products.

It is a boolean value:

public var isAppleDigitalMaster: Bool? { get }

Charts

Another requested structure was accessing the catalog charts. We get access to the top played, city, and global charts. The response contains the album, music video, playlist, and song charts. And each chart contains its name and a collection of that music item.

Here is an example of fetching song and album charts:

let request = MusicCatalogChartsRequest(kinds: [.cityTop, .dailyGlobalTop], types: [Song.self, Album.self])
let response = try await request.response()

print(response.albumCharts) /// <-- [MusicCatalogChart<Album>]
print(response.songCharts)

guard let songChart = response.songCharts.first else { return }

print(songChart.items) /// <-- MusicItemCollection<Song>

Search Suggestions

To get the top results and search suggestions, we have a new struct MusicCatalogSearchSuggestionsRequest. For example, to get the top results of the type Song for the term weeknd:

let request = MusicCatalogSearchSuggestionsRequest(term: "weeknd", includingTopResultsOfTypes: [Song.self])
let response = try await request.response()

print(response.topResults)

The result is:

MusicItemCollection<TopResult>(
  items: [
    TopResult.song(Song(id: "1488408568", title: "Blinding Lights", artistName: "The Weeknd")),
    TopResult.song(Song(id: "1440870375", title: "Starboy (feat. Daft Punk)", artistName: "The Weeknd")),
    TopResult.song(Song(id: "1440870397", title: "I Feel It Coming (feat. Daft Punk)", artistName: "The Weeknd")),
    TopResult.song(Song(id: "1440826383", title: "The Hills", artistName: "The Weeknd")),
    TopResult.song(Song(id: "1579408008", title: "Take My Breath", artistName: "The Weeknd"))
  ]
)

Recently Played

To fetch the recently played songs, it is a simple structure like:

let request = MusicRecentlyPlayedRequest<Song>()
let response = try await request.response()

print(response.items)

To fetch albums, playlists or stations that the user has recently played:

let request = MusicRecentlyPlayedContainerRequest()
let response = try await request.response()

print(response.items) /// <-- Collection of recently played music item

MusicKit has a new enumeration, RecentlyPlayedMusicItem that has three cases:

case album(Album)
case playlist(Playlist)
case station(Station)

Recommendations

To fetch personal recommendations, we use the new structure like:

let request = MusicPersonalRecommendationsRequest()
let response = try await request.response()

print(response.recommendations)

Music Library

We can also create and edit playlists and add music to the user’s library using the new class MusicLibrary. Adding a song is as easy as a one-liner where we call the add method on the shared instance:

let request = MusicCatalogSearchRequest(term: "weeknd", types: [Song.self])
let response = try await request.response()

guard let song = response.songs.first else { return }

try await MusicLibrary.shared.add(song)

We create a new playlist by:

let playlist = try await MusicLibrary.shared.createPlaylist(name: "WWDC 2022", authorDisplayName: "Rudrank Riyam")

User Library

Like the catalog request we had last year, we now have the library request. We can use it to fetch the library items like:

let request = MusicLibraryRequest<Song>()
let response = try await request.response()

print(response.items)

Similarly, we can search on the user’s library:

let request = MusicLibrarySearchRequest(term: "weeknd", types: [Song.self])
let response = try await request.response()

print(response.songs)

I am not aware of the library section structure, and I have to explore it further.

Conclusion

While all the new changes are iOS 16.0+, you can still use MusadoraKit if your app supports iOS 15. I will update the existing structures to match the new implementation, and I do not think I can match it to the level of what MusicKit has offered us this year.

While I had rewritten my book on “Exploring MusicKit” to use the structures I created in MusadoraKit, I will have to rewrite it again and remove my structures and write in detail about the new iOS 16+ ones.

Lesson learned the hard way.

Let me know what you think of MusicKit 2.0 by tagging @rudrankriyam on Twitter! I love feedback and appreciate constructive criticism.

Thanks for reading, and I hope you’re enjoying it!