Hey all!
in my personal quest to make future proof apps moving to Swift 6, one of my app has a problem when setting an artwork image in MPNowPlayingInfoCenter
Here's what I'm using to set the metadata
func setMetadata(title: String? = nil, artist: String? = nil, artwork: String? = nil) async throws {
let defaultArtwork = UIImage(named: "logo")!
var nowPlayingInfo = [
MPMediaItemPropertyTitle: title ?? "***",
MPMediaItemPropertyArtist: artist ?? "***",
MPMediaItemPropertyArtwork: MPMediaItemArtwork(boundsSize: defaultArtwork.size) { _ in
defaultArtwork
}
] as [String: Any]
if let artwork = artwork {
guard let url = URL(string: artwork) else { return }
let (data, response) = try await URLSession.shared.data(from: url)
guard (response as? HTTPURLResponse)?.statusCode == 200 else { return }
guard let image = UIImage(data: data) else { return }
nowPlayingInfo[MPMediaItemPropertyArtwork] = MPMediaItemArtwork(boundsSize: image.size) { _ in
image
}
}
MPNowPlayingInfoCenter.default().nowPlayingInfo = nowPlayingInfo
}
the app crashes when hitting
MPMediaItemPropertyArtwork: MPMediaItemArtwork(boundsSize: defaultArtwork.size) { _ in
defaultArtwork
}
or
nowPlayingInfo[MPMediaItemPropertyArtwork] = MPMediaItemArtwork(boundsSize: image.size) { _ in
image
}
commenting out these two make the app work again.
Again, no clue on why.
Thanks in advance
I was gonna say that this looks remarkably like an issue I’ve been working on a different thread, but then I noticed that you’re the OP on that thread too.
So, yeah, this is a similar issue and, as I mentioned over there, I’m still not 100% sure I fully understand all of these cases. However, I do have a suggestion for you.
Consider this snippet:
@MainActor class AudioPlayerProvider: ObservableObject {
func setMetadataQ() {
let defaultArtwork = UIImage(systemName: "fireworks")!
let nowPlayingInfo = [
MPMediaItemPropertyTitle: "Hello",
MPMediaItemPropertyArtist: "Cruel World!",
MPMediaItemPropertyArtwork: MPMediaItemArtwork(boundsSize: defaultArtwork.size) { _ in
defaultArtwork
},
] as [String: Any]
MPNowPlayingInfoCenter.default().nowPlayingInfo = nowPlayingInfo
}
}
@MainActor
func test() {
let o = AudioPlayerProvider()
o.setMetadataQ()
}
I put this into a small test project, built it with Xcode 16.0 in Swift 6 mode, and ran on an iOS 18.0 device. It crashed exactly like your code. I resolved the crash by changing { _ in
to { @Sendable _ in
.
In this case the @Sendable
really does make sense. MPMediaItemArtwork
holds on to the closure you pass in and it calls in that ‘promise’ on an arbitrary thread. Thus, the closure has to be sendable. The only annoying thing is that the compiler doesn’t tell you that.
Share and Enjoy
—
Quinn “The Eskimo!” @ Developer Technical Support @ Apple
let myEmail = "eskimo" + "1" + "@" + "apple.com"