Protocol implementation of a MainActor delegate requires await keyword on initializer in async code

Why does implementing a delegate that has @MainActor declared on it require the object being created to have an "await" keyword?

Here's an example:

class MyClass : NSObject, UIScrollViewDelegate {
    override init() {
        super.init()
    }
}

class MyClass2 : NSObject {
    override init() {
        super.init()
    }
}

class MyTest : BLTestCase {
    func xtest_getBrandDataFails() async {
        let obj = MyClass().  // expression is async but not marked with await.
        let obj1 = await MyClass()
        let objc2 = MyClass2()
    }
}

UIScrollViewDelegate is declared with @MainActor. And it causes the initialization to require await. That seems odd to me because my init methods aren't marked as async.

It's also odd seeing an await or "suspension point" as stated by the swift concurrency chapter in the swift book, but init is synchronous. Would swift concurrency ever suspend execution here? It seems wrong that it would when I know I don't want a suspension point.

The short answer here is that it's a bit complicated.

Because of the delegate conformance, MyClass is inferred to be a "global-actor isolated type". In this context, "isolated" means "protected by the actor safety machinery in Swift".

Since you have not specified otherwise, MyClass.init() is an isolated initializer of that global-actor isolated type. Since Swift 5.7, an isolated initializer of an isolated type does not require an await when called, but an isolated initializer of a global-actor isolated type does require an await. The reasons for this are kind of subtle, and they're deeply buried in SE-0327, if you wish to explore all the gory details.

In this case, it looks like you have an "out" — declare the initializer as non-isolated:

class MyClass : NSObject, UIScrollViewDelegate {
    nonisolated override init() {
        super.init()
    }
}

Also, please note that in general you need await to call any isolated method of an actor type, not just async methods. That's because entering an isolated method is a potential suspension point, so the caller needs to be asynchronous too.

Protocol implementation of a MainActor delegate requires await keyword on initializer in async code
 
 
Q