Should awakeFromNib() be manually marked as @MainActor?

Hi there,

Enabling concurrency warning with Xcode 13.3RC, it's produced hundreds of error as expected but I learned a ton of them were only because override'ed methods weren't isolated.

For example,

Add '@MainActor' to make instance method 'awakeFromNib()' part of global actor 'MainActor'

While this is pretty easy to fix, is it expected to do? Should I manually mark all those awakeFromNib() as @MainActor?

1 Like

Searching forums, I found @bjhomer already asked below a year ago - [Pitch] Global actors - #4 by bjhomer

And @Douglas_Gregor replied.

So, it wasn't used for those UIKit methods as of Xcode 13.3... seems the case.

Since it's UIKit stuff, maybe it's not something we should discuss here but this is extremely unfortunate because the concurrency warning flag is practically unusable for its main purpose when it produces hundreds of warnings because of those override methods :disappointed:

This is one of the reasons the concurrency warnings were disabled by default for 5.6: Apple's frameworks just aren't ready to support it. We'll probably see much more progress with 5.7 at WWDC and this fall.

2 Likes

Actually, UIViewController and friends already are marked with @MainActor, and it should already be inherited by overridden methods. If you look at UIViewController.h in Xcode 13.3, you'll see it's tagged with NS_SWIFT_UI_ACTOR. That is a macro that expands to __attribute__((swift_attr("@UIActor"))). (I assume that @UIActor is mapped for compatibility with @MainActor.)

So UIViewController is indeed annotated to be isolated to the Main Actor. The problem here is that awakeFromNib is not declared by UIViewController—it's inherited from NSObject. And NSObject is not tied to the main actor.

You can see the difference here:

class ViewController: UIViewController {

    override func viewDidLoad() {
        super.viewDidLoad()
        mainThreadThing() // This is fine
    }

    override func awakeFromNib() {
        super.awakeFromNib()
        
        mainThreadThing() 
        // error: Call to main actor-isolated global 
        //        function 'mainThreadThing()' in a 
        //        synchronous nonisolated context
    }
}

@MainActor
func mainThreadThing() {
    assert(Thread.isMainThread)
}
2 Likes

Awwwww, that makes sense. So, awakeFromNib() needs to be actually explicitly annotated. Hmm...

Since most of awakeFromNib() out there must have this issue and will produce a ton of errors by the concurrency warning flag, I'd guess Apple needs to annotate it before enabling the flag by default. But that means we can't practically use the flag to find actual bugs today or I have to manually annotate all of them now. :thinking:

Found a solution/workaround here:

Basically use MainActor.assumeIsolated

1 Like