How to display artwork images from MusicKit with UIKit?

In SwiftUI there is a built-in component for displaying album artworks called Artwork but there is no equivalent for UIKit.

My current approach is to use the .url() method to read image's URL and download the image or read it from the disk but the performance is much worse than it was previously with MPMediaItem's artworkImage method.


let artworkQueue = DispatchQueue(
    label: "MusicKit-ArtworkQueue",
    qos: .default,
    attributes: .concurrent
)

let artworkSemaphore = DispatchSemaphore(value: 5)

extension Song {
    
    func artworkImage(for size: CGSize, completion: @escaping (UIImage?) -> Void) {
        artworkQueue.async {
            artworkSemaphore.wait()
            defer {
                artworkSemaphore.signal()
            }
            
            let imageURL = artwork?.url(
                width: Int(size.width),
                height: Int(size.height)
            )
// I hate doing this as it might very well break in the future
            guard let imageURL, imageURL.scheme == "musicKit"
            else {
                return completion(nil)
            }
            
            guard let imageData = try? Data(contentsOf: imageURL),
                  let image = UIImage(data: imageData) else {
                return completion(nil)
            }
            completion(image)
        }
    }
    
}

I really dislike this approach because it feels hacky but somewhat works. You might ask what's the semaphore for? Well, without it I could notice that MusicKit was choking and after reading too many artworks at once.

Can someone from Apple please provide us with an example on how to use MusicKit with UIKit properly?

Ideally (IMO) we would have a method defined on Song and other MusicKit structures that returns the image for us, just like MPMediaItem had the .artwork() method. It would make our lives so much easier.

Replies

I've modified my method slightly to use more modern APIs for reading the files but the result is the same, after few hundreds of reads the MusicKit chokes and stops working completely. I cannot perform any requests like MusicLibraryRequest or play anything using MusicPlayer API

fileprivate let artworkQueue: OperationQueue = {
    let op = OperationQueue()
    op.underlyingQueue = .global()
    
    return op
}()

fileprivate let artworkCoordinator = NSFileCoordinator()

extension Song {
    
    func artworkImage(for size: CGSize, completion: @escaping (UIImage?) -> Void) {
        let imageURL = artwork?.url(
            width: Int(size.width),
            height: Int(size.height)
        )
        guard let imageURL, imageURL.scheme != "musicKit"
        else {
            return completion(nil)
        }
        let intent = NSFileAccessIntent.writingIntent(with: imageURL)
        artworkCoordinator.coordinate(
            with: [intent],
            queue: artworkQueue
        ) { error in
            guard error == nil,
                  let imageData = try? Data(contentsOf: imageURL),
                  let image = UIImage(data: imageData) else {
                return completion(nil)
            }
            completion(image)
        }
    }
    
}