I have a Mac app that uses the old QuickLook protocols: QLPreviewPanelDataSource
and QLPreviewPanelDelegate
. You select a row in an NSTableView
, hit the spacebar, and you get the QuickLook preview. Standard stuff:
@MainActor
final class SomeController: NSTableViewDataSource, QLPreviewPanelDataSource
{
private var tableView: NSTableView
func numberOfPreviewItems(in panel: QLPreviewPanel!) -> Int {
return tableView.numberOfSelectedRows
}
}
I'm adopting Actors and Swift Concurrency in this app. SomeController
is now assigned to @MainActor
as it should be, since it controls UI. But this brings warnings for the -numberOfPreviewItems()
implementation:
Main actor-isolated instance method 'numberOfPreviewItems(in:)' cannot be used to satisfy nonisolated protocol requirement
QLPreviewPanelDataSource
is not decorated with @MainActor
, which would obviously solve the problem. I cannot simply mark the function nonisolated
, since it touches tableView
, which IS isolated to the Main Actor. And I cannot await an access, since the protocol method does not support concurrency.
Everything works fine, of course, but having 30-some spurious build warnings is a giant distraction.
What is the correct way to silence these warnings assuming:
Apple will never update the protocols with the @MainActor
decoration. (Radars to do so are unanswered for years.)
I want to keep the Strict Concurrency Checking
build setting set to complete
to catch other, legitimate issues.
For this answer to work in Xcode 16 with Swift 6 Language Mode, it is now necessary to decorate the import statement:
@preconcurrency import Quartz
In older versions of Xcode, the @preconcurrency
decorator was non-functional, but that appears to have changed in Xcode 16. Without this decorator, MainActor.assumeIsolated
will produce Sendable
errors.
As you’ve pointed out, Apple has not isolated some legacy Objective-C classes, and given the requests have been open a while, it doesn’t seem like they will. Given this, it doesn’t seem possible to isolate an entire object without access to the object or protocol definition.
To address this isolation issue, Apple has provided this. I found this after running across this question when I had a similar issue
MainActor.assumeIsolated {}
I was building an ARKit feature and needed to conform to ARSCNViewDelegate
, which is locked to the main thread in Objective-C, but considered nonisolated
when it came to Concurrency
. Since the delegate method returns a value, I couldn't wrap the body in a Task
.
This is what worked for me:
@MainActor
class MyViewController: UIViewController, ARSCNViewDelegate
{
// MARK: ARSCNViewDelegate methods
nonisolated func renderer(_ renderer: SCNSceneRenderer, nodeFor anchor: ARAnchor) -> SCNNode? {
return MainActor.assumeIsolated {
// Assumed to be isolated to MainActor
let node = SCNNode() // This line refuses to compile if not isolated since `SCNNode` is isolated to the MainActor.
// Configure node and return.
return node
}
}
}
No warnings, no errors.