Home > Enterprise >  Throwing in Combine map operator gives "Main actor-isolated property 'foo' can not be
Throwing in Combine map operator gives "Main actor-isolated property 'foo' can not be

Time:09-26

I have the following operator in a Combine pipeline, which must be run on the main thread, before returning to the global dispatch queue. That's because FooManager.shared.foo is on the MainActor:

extension AnyPublisher where Output == (Foo, Bar), Failure == Error {
  func checkForFoo() -> AnyPublisher<Output, Failure> {
    self
      .receive(on: RunLoop.main)
      .tryMap { x, y in
        let isFoo = FooManager.shared.foo // Error here
        if isFoo {
          guard FooManager.shared.bar == true else {
            throw MyError.cancelled
          }
        }
        return (x, y)
      }
      .receive(on: DispatchQueue.global(qos: .userInitiated))
      .eraseToAnyPublisher()
  }
}

Throwing from inside the guard causes the error Main actor-isolated property 'foo' can not be referenced from a non-isolated context. However, if I just return nothing from the guard statement, it doesn't do this. I need to be able to throw, but I don't understand why it's mentioning the main actor stuff, when I've switched to receive on the main runloop?

CodePudding user response:

First, the compiler doesn't know that you've switched to the main thread, you do that at runtime, but the compiler doesn't know that when it compiles your code.

Second, the MainActor and the main thread are two different things. The MainActor guarantees that, when it is running code, the thread it is running that code on will be the only thread running MainActor code. That does not guarantee your code will run on the main thread. But if it's running on another thread, the main thread will be suspended.

Finally the complaint you are seeing from the compiler is not that you're code is not on the main thread, but that you are using async code in a non async context. Because FooManager is (apparently) actor isolated (which actor is immaterial), any calls to it must be awaited and you can only use await in an asynchronous context. Here the closure you pass to tryMap is a synchronous context, it's not allowed to suspend, so the compiler complains.

  • Related