Experimenting with MusicKit for Swift - Artist Artwork

I’ve been working on Musadora, an Apple Music client app that uses the latest MusicKit for Swift.

Last week, I started working on the ArtistDetailView. While using the Artist object, I realized that the autocomplete didn’t suggest any Artwork object. That was confusing, as the artist’s detail will look completely bland without their image.

After searching for a while, I realized that you cannot fetch the artist’s image. I asked a question on the developer forum and got the answer that it is due to licensing issues. But, I’ve seen many live apps on the App Store fetching this data, especially for the featured artists.

I knew I had to figure it out somehow, and here’s my journey!

The first client app that I encountered fetching the artist image had the same exact ones displayed in Apple Music. That meant they’re getting the images from somewhere, but not Apple Music API. My friend Shashank sent me a gist by Karl Ding on grabbing iTunes image artwork from the DOM using the Open Graph meta tags.

The Music website uses Open Graph meta tags, which embeds a meta tag with an image property attribute value set to og: image. This is similar to the image used in Apple Music artwork.

For example, consider The Weeknd -

https://music.apple.com/in/artist/the-weeknd/479756766

The link consists of the country code and the artist id. Inspecting the element fro the meta properties provides us the following link of the image -

https://is4-ssl.mzstatic.com/image/thumb/Music115/v4/64/cb/5e/64cb5e65-fbbd-0de3-1627-4a3ee4e4b6d3/pr_source.png/1200x630cw.png

The URL structure is similar to the url method on the Artwork where we’ve width and height at the end of the URL. One interesting thing to note was the suffix after the dimension, like 1200x630cw. I don’t know what these mean, but after experimenting, I’ve concluded the following -

  • cw: returns an image clipped in a circle with white background
  • sr: returns the full image size
  • cc: Rahul found out that this return the square image
  • bf: - returns the square image with black background

To use this in my app, I used a Swift wrapper for the Open Graph protocol. This helps me to fetch the OpenGraph and access the image attribute of the website.

I prefer the latest async/await syntax, so I decided to wrap the completion handler method from the OpenGraph framework to an async API.

Here are the extensions that I created -

import MusicKit

extension Artist {
    func artwork(width: Int = 1024, height: Int = 1024) async -> URL? {
        let id = self.id.rawValue

        let countryCode = try? await MusicDataRequest.currentCountryCode
        let url = URL(string: "https://music.apple.com/\(countryCode ?? "us")/artist/\(id)")!

        return await withCheckedContinuation { continuation in
            OpenGraph.fetch(url: url) { result in
                switch result {
                    case .success(let og):
                        let image = og[.image]?.resizeArtwork(width: width, height: height)

                        if let image = image, let url = URL(string: image) {
                            continuation.resume(returning: url)
                        } else {
                            continuation.resume(returning: nil)
                        }
                    case .failure(_):
                        continuation.resume(returning: nil)
                }
            }
        }
    }
}

extension String {
    func resizeArtwork(width: Int, height: Int) -> String? {
        do {
            let regex = try NSRegularExpression(pattern: "/\\d+x\\d+cw", options: .caseInsensitive)
            let newImage = regex.stringByReplacingMatches(in: self, options: [], range: NSRange(0..<self.utf16.count), withTemplate: "/\(width)x\(height)cc")
            return newImage
        } catch {
            return nil
        }
    }
}

I created an extension on Artist with an artwork(width:height:) method so that it is easy to get the artwork URL. I get the rawValue from the MusicItemID and then the current country code for the user’s Apple Music account. Based on these two values, I initialized the artist URL. Then, I call the fetch method of the OpenGraph framework, and on success, get the image property of the open graph.

I created a resizeArtwork(width:height:) method on the String that replaces the dimensions according to the provided ones. Do note that I replaced cw with cc to get a square image.

The URL is returned asynchronously, and the AsyncImage can use it to display the artist’s artwork!

There may be apps using this in the App Store, but I’m aware of the licensing issues. So, if you use this way of fetching the image, do your due diligence.

For any issues, do let me know on Twitter.