In Objective-c we can use NS_SWIFT_UI_ACTOR
to indicate that a method or a whole class should be executed on the main actor (as we would do with @MainActor
in Swift). This works correctly since, when invoking a method which returns a value from outside the main actor (using a Task.detached with background priority), the compiler forces us to call it using "await" (to allow for "actor switch") and, at runtime, I can see that the code is executed on the main thread.
However, when calling a callback based method on the same class (which gets an automatically "imported" async variant, as explained here) the NS_SWIFT_UI_ACTOR seems not working properly, and the code is executed on a background thread.
NS_SWIFT_UI_ACTOR
@interface ObjcClass : NSObject
- (NSString *)method1;
- (void)method2WithCompletion: (void (^)(NSString * _Nonnull))completion;
@end
Task.detached(priority: .background) {
let objcInstance = ObjcClass()
print(await objcInstance.method1()) <----- this is called on the main thread
print(await objcInstance.method2()) <----- this is called on a bg thread
}
While I understand what your expectation is I don't think as of now the way actor is implemented there is any way to resolve this. Actors in swift are reentrant, which means whenever you invoke any
async
method inside actor's method/actor task, actor suspends the current task until the method returns and starts processing other tasks. This reentrancy behavior is important as it doesn't just lock the actor when executing a long-running task.Let's take an example of a
MainActor
isolatedasync
method:But since swift can't infer from an objective c
async
method which part should run onMainActor
and for which part actor can just suspend the current task to process other tasks, the entireasync
method isn't isolated to the actor.However, there are proposals currently to allow customizing an actor's reentrancy so you might be able to execute the entire objc
async
method on the actor.