Structured concurrency + preconcurrency API (SFAuthorizationPluginView)

I'm having trouble dealing with concurrency with the SFAuthorizationPluginView. Does anybody know how this can be solved? https://developer.apple.com/documentation/securityinterface/sfauthorizationpluginview

The crux of it is: If I inherit an object as part of an API, and the API is preconcurrency, and thus is nonisolated (but in reality is @MainActor), how do I return a @MainActor GUI element?

https://developer.apple.com/documentation/securityinterface/sfauthorizationpluginview/firstresponder()

The longer story:

  1. I made my view class inherit SFAuthorizationPluginView.
  2. The API is preconcurrency (but not marked as preconcurrency)
  3. I started using concurrency in my plugin to retrieve data over XPC. (https://developer.apple.com/documentation/xpc/xpcsession + https://developer.apple.com/documentation/swift/withcheckedthrowingcontinuation(isolation:function:_:))
  4. Once I retrieve the data over XPC, I need to post it on GUI, hence I've set my view class as @MainActor in order to do the thread switch.
  5. Swift compiler keeps complaining:
    override func firstResponder() -> NSResponder? {
        return usernameField
    }    

"Main actor-isolated property 'usernameField' can not be referenced from a nonisolated context; this is an error in the Swift 6 language mode"

    override func firstResponder() -> NSResponder? {
        MainActor.assumeIsolated {
            return usernameField
        }
    }

"Sending 'self' risks causing data races; this is an error in the Swift 6 language mode"

I think fundamentally, the API is forcing me to give away a @MainActor variable through a nonisolated function, and there is no way to shut up the compiler.

I've tried @preconcurrency and it has no effect as far as I can tell. I've also tried marking the function explicitly as nonisolated.

The rest of the API are less problematic, but returning a GUI variable is exceptionally difficult.

Answered by DTS Engineer in 850667022

I don’t think there’s a good solution here. There are two issues in play here:

  • SFAuthorizationPluginView, despite the name, is not a subclass of NSView, and thus it doesn’t benefit from the main actor isolation that NSView subclasses get.
  • Furthermore, SFAuthorizationPluginView has not been audited for concurrency [1], so it’s not itself marked being main actor isolated.

IMPORTANT Although it’s not documented anyway, you are correct that SFAuthorizationPluginView is a main actor isolated type.

Regardless of what else you do, please file a bug asking that it be decorated as main actor isolated and audited for nullability.

Please post your bug number, just for the record.

As to what you should do today, that depends on your project’s attitude towards warnings. If warnings aren’t a problem then you should assume that SFAuthorizationPluginView is main actor isolated, put your SFAuthorizationPluginView code into a module that builds in the Swift 5 language mode, and live with the concurrency warnings it generates.

OTOH, if your project can’t tolerate warnings then, yeah, things get trickier. For example, to implement firstResponder() you’d need to create an unsafe box that lets you move your view from its main actor home to the non-isolated-but-really-main-actor context of firstResponder().

Share and Enjoy

Quinn “The Eskimo!” @ Developer Technical Support @ Apple
let myEmail = "eskimo" + "1" + "@" + "apple.com"

[1] It’s not even been audited for nullability *sigh*

I don’t think there’s a good solution here. There are two issues in play here:

  • SFAuthorizationPluginView, despite the name, is not a subclass of NSView, and thus it doesn’t benefit from the main actor isolation that NSView subclasses get.
  • Furthermore, SFAuthorizationPluginView has not been audited for concurrency [1], so it’s not itself marked being main actor isolated.

IMPORTANT Although it’s not documented anyway, you are correct that SFAuthorizationPluginView is a main actor isolated type.

Regardless of what else you do, please file a bug asking that it be decorated as main actor isolated and audited for nullability.

Please post your bug number, just for the record.

As to what you should do today, that depends on your project’s attitude towards warnings. If warnings aren’t a problem then you should assume that SFAuthorizationPluginView is main actor isolated, put your SFAuthorizationPluginView code into a module that builds in the Swift 5 language mode, and live with the concurrency warnings it generates.

OTOH, if your project can’t tolerate warnings then, yeah, things get trickier. For example, to implement firstResponder() you’d need to create an unsafe box that lets you move your view from its main actor home to the non-isolated-but-really-main-actor context of firstResponder().

Share and Enjoy

Quinn “The Eskimo!” @ Developer Technical Support @ Apple
let myEmail = "eskimo" + "1" + "@" + "apple.com"

[1] It’s not even been audited for nullability *sigh*

Thank you for your excellent help as always, Quinn. I really appreciate it.

I'll probably just live with the warnings. I had Swift set to 6.0 before, and spent a lot of time spinning in circles trying to get the threading model to be compliant with structured concurrency checks. Thanks for pointing me in the right direction.

I've filed FB19093833 as asked.

Structured concurrency + preconcurrency API (SFAuthorizationPluginView)
 
 
Q