Experimenting with MusicKit for Swift - ArtworkImage

While building an Apple Music client, you’ll display artworks on almost every screen. Be it the home screen showing the album art, the music player, or the search results. Visuals are essential, and MusicKit provides an asynchronous image loading view called ArtworkImage.

As the name suggests, it is a view that displays an image for an instance of Artwork.

Artwork

Artwork represents the artwork of the music item, whether it is an album art, a single or a playlist. It has many instance properties such as the maximum height and width available, the (average) background colour, and textual description for the image.

It also has an instance method url(width:height:) that returns the URL to request the image asset for the particular width and height.

Now, you can either use the new AsyncImage for asynchronously loading and displaying this artwork by providing the URL from the above instance method or use the ArtworkImage.

ArtworkImage

ArtworkImage makes it easier for us to display the image. You initialise the view in three ways by providing it with the artwork and -

  • the height,
  • the width,
  • both height and width
init(_ artwork: Artwork, height: CGFloat)  

init(_ artwork: Artwork, width: CGFloat)  

init(_ artwork: Artwork, width: CGFloat, height: CGFloat)  

It derives the URL of the image from the Artwork. If you provide either height or width only, the other dimension is calculated as a proportional length according to the aspect ratio of the artwork. (Reference)

And for the placeholder, while the image is fetched, it uses the average background colour of the artwork using the backgroundColor property to display the placeholder.

A trivial example from the sample project -

private var header: some View {
    VStack {
        if let artwork = album.artwork {
            ArtworkImage(artwork, width: 320)
                .cornerRadius(8)
        }
        Text(album.artistName)
            .font(.title2.bold())
        playButtonRow
    }
}

Now, this works great on bigger devices. But when I tested it on my iPhone 7, the album artwork was cropped as we provided a hardcoded width. Because of this, the button also gets cropped. As the ArtworkImage is not an Image but a View instead, we cannot apply resizable() nor scaledToFit() modifier.

Prompt to access Apple Music in Musadora

I’m still figuring out a solution to this problem. As it is in a List, the default inset style takes up space horizontally. One solution I came up with was using GeometryReader. I provided the height in percentage if it is an iPhone and a hardcoded value if it is an iPad, so it doesn’t look comical on the huge screen.

@Environment(\.horizontalSizeClass) var horizontalSizeClass: UserInterfaceSizeClass?

var body: some View {
    GeometryReader { geometry in
        let width = horizontalSizeClass == .compact ? geometry.size.width * 0.75 : 350

        List {
            header(width: width)
                .listRowBackground(Color.clear)
                .buttonStyle(PlainButtonStyle())

            // Rest of the content
        }
    }
}

And the header takes width as a parameter -

private func header(width: CGFloat) -> some View {
    VStack {
        if let artwork = album.artwork {
            ArtworkImage(artwork, width: width)
                .cornerRadius(8)
        }
        Text(album.artistName)
            .font(.title2.bold())
        playButtonRow
    }
}

This solves the UI problem on a small device like iPhone 7 or SE, with the image shrinking according to the screen’s width.

Prompt to access Apple Music in Musadora

If you’ve a better solution, I would love to hear your thoughts!

Conclusion

ArtworkImage is an excellent view for displaying the artwork of music items. If you want more control over the phase of the image, you can use ArtworkImage. Using the network conditioner with a very bad network profile, Apple Music shows a blank placeholder. So, a placeholder with background colour is adequate for most use cases in Musadora.