Exploring MusicKit Book

This chapter is from the upcoming update of the book “Exploring MusicKit and Apple Music API”. You can grab the book and get all the updates for iOS 16 for FREE.

Use the discount code “early” for 50% discount

Buy the book


(BETA) Audio Variants

While the Apple Music API for the actual Apple Music had the audio variants available like Dolby Atmos, Dolby Audio, Hi-Res Lossless, Lossless, and Lossy stereo, it was not available in the Apple Music API for third-party developers nor in the MusicKit framework.

Newly announced in WWDC 2022, we now have full support for accessing the audio variants! To use it in our app, we have the enumeration AudioVariant that indicates the audio quality available for a music item like a Song or an Album.

enum AudioVariant

It has the following enumeration cases. I have directly copied the wordings from the documentation, so I do not mess up any technical details:

  • case dolbyAtmos Dolby Atmos is an immersive audio experience that surrounds you with sound from all sides, including above.

  • case dolbyAudio Dolby Audio is a surround sound format that includes Dolby 5.1 and 7.1.

  • case highResolutionLossless Hi-Res Lossless uses Apple Lossless Audio Codec (ALAC) for bit-for-bit accuracy up to 24-bit/192 kHz.

  • case lossless Lossless uses Apple Lossless Audio Codec (ALAC) for bit-for-bit accuracy up to 24-bit/48 kHz.

  • case lossyStereo Lossy stereo uses compression used to store sound data.

This property is now available for the MusicPlayer under the observable class State. We can use it to access the active variant that indicates the audio quality for the current entry.

Note that it is only a readable property, and we cannot set it in our app. The user provides in the Music app settings if they want Dolby Atmos and/or Hi-Res Lossless, and then the app automatically switches based on the network conditions.

@available(iOS 16.0, tvOS 16.0, *)
public var audioVariant: AudioVariant? { get }


Also, AudioVariant conforms to the CaseIterable protocol so that we can access all the cases as an array:

AudioVariant.allCases /// <-- [AudioVariant]

Loading as Extended Attributes

As mentioned earlier, we can get the list of audio variants for a particular song or album. We get these extended attributes using the with(_:) method on a song or album instance.

@available(iOS 16.0, macOS 13.0, tvOS 16.0, watchOS 9.0, *)
extension PartialMusicProperty where Root == Album {
    public static let audioVariants: MusicExtendedAttributeProperty<Album, [AudioVariant]>
}

@available(iOS 16.0, macOS 13.0, tvOS 16.0, watchOS 9.0, *)
extension PartialMusicProperty where Root == Song {
    public static let audioVariants: MusicExtendedAttributeProperty<Song, [AudioVariant]>
}

For example, while writing this chapter, I am listening to the song AMAZING by Rex Orange County. The album WHO CARES? has the unique identifier 1606962225. Let us see what all audio variants are available for this particular album:

let request = MusicCatalogResourceRequest<Album>(matching: \.id, equalTo: "1606962225")
let response = try await request.response()

guard let album = response.items.first else { return }

let detailedAlbum = try await album.with(.audioVariants)

print(detailedAlbum.audioVariants)

The console prints:

Optional([.dolbyAtmos, .lossless, .lossyStereo])

We can also use it for individual songs. For example, the song Leywole from WWDC 2022 is something I cannot get out of my mind. If we try to get the audio variants for it using the song’s identifier and extended attributes:

let request = MusicCatalogResourceRequest<Song>(matching: \.id, equalTo: "1575285202")
let response = try await request.response()

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

let detailedSong = try await song.with(.audioVariants)

print(detailedSong.audioVariants)

The console gives the following output:

Optional([.lossless, .lossyStereo])

Example

In the NowPlayingView, we can switch over the values of the audio variants and then set the image for it accordingly:

struct NowPlayingView: View {
  @ObservedObject private var state = ApplicationMusicPlayer.shared.state

  var body: some View {
    VStack(spacing: 0) {      
      switch state.audioVariant {
        case .dolbyAtmos:
          AudioVariantImage("dolby-atmos-logo")
        case .highResolutionLossless:
          AudioVariantImage("hi-res-lossless-logo")
        case.lossless:
          AudioVariantImage("lossless-logo")
        default:
          EmptyView()
      }
    }
  }
}

struct AudioVariantImage: View {
  var name: String
  
  public init(_ name: String) {
    self.name = name
  }
  
  var body: some View {
    Image(name)
      .resizable()
      .scaledToFit()
      .frame(width: 100)
      .padding(.top, 12)
  }
}

You can replace the image names with those you have and let your users know the audio variant they get in your app!

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!