Huh, maybe this has been true since June 2023? Am I really so behind the times? (NSImage being Transferable makes sense, but Sendable is surprising) https://developer.apple.com/documentation/updates/appkit?#Swift-and-SwiftUI
AppKit updates | Apple Developer Documentation

Learn about important changes to AppKit.

Apple Developer Documentation
@cocoaphony My understanding is that NSImage is just a really awkward fit for sendability checking because developers often do a bunch of thread-confined mutation right after creating an instance and then pass it freely between threads without further mutation. It’s safe when used that way, but there’s no way to programmatically stop you from using the mutation APIs that would be unsafe; you just need to be careful with it in the way you always have been.
@beccadax I think you're right. The situation we've gotten ourselves into is trying to port UIImage code over with a (possibly ill-advised) platform-dependent typealias. UIImage is truly Sendable, and it's just not safe to assume you can do everything with NSImage you can do with UIImage. In other products, I've just jumped to CGImage, but you do give up a lot of UIImage performance features when you do that. Tough to make things work cross-platform (when SwiftUI on macOS is still a bit weak).

@beccadax @davedelong I think this is definitely a bug somewhere, probably in AppKit, maybe in the compiler. I can build Swift 6 code with no warnings that reliably crashes with EXC_BAD_ACCESS. Need to think of where to send the bug report.

https://gist.github.com/rnapier/afbc91266212eb667ea02e3945e91be8

nsimage-is-not-sendable.swift

GitHub Gist: instantly share code, notes, and snippets.

Gist

@cocoaphony @beccadax @davedelong oof yeah, that's a weird change -- previously we had to fake its sendability with unchecked-sendable boxes and could see that something weird is going on.

But this API now is both more convenient to pass static images along that have already been fully loaded into memory, and oddly dangerous.

I recall @helge and @mattiem chiming in ~1y ago when I mused about this. CGImage sending was state of the art then 🤔

@ctietze @cocoaphony @beccadax @davedelong @mattiem I don't quite remember the context, but a difference between UIImage and NSImage is that the latter can have multiple representations, and the representations can actually even be closures that draw on demand.
@helge @ctietze @cocoaphony @davedelong @mattiem The important thing from a concurrency safety perspective is that a UIImage is immutable, while an NSImage is mutable (for example, you can add representations to an existing instance).

@cocoaphony I don’t think so. The bullet point I think(!) you’re quoting only talks about NSImage being Transferable and some other types being Sendable, doesn’t it? I can’t find any other relevant NSImage reference in that document.

“AppKit more fully integrates with Swift and SwiftUI with Sendable (NSColor, NSColorSpace, NSGradient, NSShadow, NSTouch) and Transferable (NSImage, NSColor, NSSound) types.”

@ole yeah, I think it’s a bug. It is being treated as Sendable and can use that to violate thread safety and crash. I’m going to put together a bug report.
@cocoaphony Yeah, I saw your crashing code snippet. I only wanted to comment on the “since June 2023” question. I don’t think the AppKit updates page says this, so you aren’t years behind.
@cocoaphony @ole We know about it. The Transferable protocol gained a Sendable conformance and that made NSImage Sendable unintentionally. It is not concurrency-safe (but works fine on arbitrary threads if isolated). The only tenable fix at this point will be to make it safe, but that’s easier said than done on a reference type with mutable state inside.
@jnadeau @ole it seems at least a documentation fix is warranted. If it weren’t for me saying “there’s no way that’s right” and writing tests to prove it, we definitely would have used it unsafely, but probably couldn’t have discovered the problem through normal unit testing, just field crashes.
@cocoaphony haha I found out in November while interviewing - during live coding I expected UIImage with errors when turning on Swift6 compilation mode, only to find out that it is Sendable now 😃
@lvalenta UIImage definitely makes sense as Sendable since it’s immutable like CGImage. I didn’t realize there was a time when it wasn’t Sendable. But NSImage is explicitly not thread-safe. It’s “transferable,” and can be accessed on any theead, but only one thread at a time (like most mutable Foundation types).