At this stage, I’m just riffing on different ways to ‘break’ swift concurrency.
I wondered if protocols would provide an opportunity, and indeed they do!
Take this scenario. We have a simple protocol – but the class implements it with an @MainActor annotation.
If you do this all within the main class definition, then you get a warning
However, you can declare conformance in an extension and make it @MainActor
No warning here
Or perhaps more confusingly – you can declare your whole class @MainActor, then just conform ‘normally’ in the extension
– in this last case, I wasn’t sure whether it would actually be treated as an @MainActor variable – but I tested, and indeed it is.
So – we have three ways of conforming to a protocol which _doesn’t_ require @MainActor – but in each case, we have required @MainActor in our implementation
Swift Concurrency will use the MainThread for an instance which it recognises as Bar, and won’t bother for an instance it recognises as Thing
I can see the logic here – but once again, @MainActor isn’t guaranteeing anything. The actual call depends subtly on what kind of thing the compiler recognises.
So – we have three subtly different ways of generating exactly the same code.
One of them generates a compile-time warning, the other two do not.
I generally prefer declaring conformance to a protocol in an extension – I had no idea that meant I would get different compile-time warnings.
Sometimes the method is called on Main – Sometimes not!
@MainActor func makeBar() -> Bar { return Bar() } func breakWithProtocol() { Task { var bar:Bar = await makeBar() //The compiler recognises a Bar and insists we call asynchronously //It will call on the main thread _ = await bar.thing } Task { let thing:Thing = await makeBar() //The compiler just sees a Thing here - so it calls directly on a background thread _ = thing.thing } }