Home > database >  Class-constrained protocols don't adopt MainActor
Class-constrained protocols don't adopt MainActor

Time:06-11

If I declare this protocol and model:

protocol SubScene: UIViewController {
  func doSomething()
}

struct Model {
  let subScene: SubScene

  func longCalculation() async {
    // Some long-running operation...
    await subScene.doSomething()
  }
}

class MyVC: UIViewController, SubScene {
  func doSomething() {
    performSegue(withIdentifier: "segue", sender: nil)
  }
}

let myVC = MyVC()
let model = Model(subScene: myVC)
Task.detached {
  await model.longCalculation()
}

I've declared a protocol that is restricted to UIViewController, and a model which calls the protocol's doSomething() method from an async context. Since UIViewController is a MainActor, my expectation is that doSomething() should get called on the main thread.

However, it does not. In fact the await keyword results in a warning from Swift that it is not calling an async method. And empirical testing shows that doSomething() will often end up being called on some other thread.

I can fix that by annotating doSomething() as a MainActor:

protocol SubScene: UIViewController {
  @MainActor func doSomething()
}

This gets rid of the Swift warning and also results in doSomething() getting called on the correct thread.

Is this the correct approach, or is there another way that I could be doing this that avoids having to sprinkle all of my protocol methods with @MainActor?

CodePudding user response:

You could, alternatively, mark the entire protocol as @MainActor, e.g.,

@MainActor protocol SubScene: AnyObject {
    func doSomething()
}

That avoids the need to declare each conforming method individually.

  • Related