Reputation: 15247
My app needs a reference to the SCNSceneRenderer
for hit testing. I thus use SCNSceneRenderer
delegate to get and store the reference as property in my ViewModel:
@MainActor
@Observable
final class ViewModel: NSObject, @preconcurrency SCNSceneRendererDelegate {
private(set) weak var renderer: (any SCNSceneRenderer)?
//…
func renderer(_ renderer: any SCNSceneRenderer, updateAtTime time: TimeInterval) {
self.renderer = renderer
}
}
With this code, I get in func renderer
the warning:
warning: data race detected: @MainActor function at …/ViewModel.swift:10 was not called on the main thread
As a bug fix, I changed func renderer
to:
func renderer(_ renderer: any SCNSceneRenderer, updateAtTime time: TimeInterval) {
Task {
await MainActor.run {
self.renderer = renderer
}
}
}
Now, the warning within func renderer
is gone. However, the Issue Navigator shows the following error:
warning: data race detected: @MainActor function at …/ViewModel.swift:1023 was not called on the main thread
Line 1023 is the line where func renderer
is defined.
My question is:
How can there be a data race, when the ViewModel is @MainActor
, and the body of the function is executed on the MainActor? And how could I fix the bug?
Upvotes: 0
Views: 75
Reputation: 15247
I still don't understand the problem, but I found a workaround.
I think this workaround is terrible, and there should be a better solution, but I did not find any up to now.
In the viewModel
, I use now a set function for var renderer
:
weak var renderer: (any SCNSceneRenderer)?
func setRenderer(_ renderer: (any SCNSceneRenderer)?) {
self.renderer = renderer
}
Then, I created a new class SceneRendererDelegate
with a local var renderer
. This local var
is set by the SCNSceneRendererDelegate
function. When it was set, it starts a Task
that sets var renderer
in the viewModel
:
class SceneRendererDelegate: NSObject, SCNSceneRendererDelegate, @unchecked Sendable {
let viewModel: ViewModel
var renderer: (any SCNSceneRenderer)? {
didSet {
Task { @MainActor in
viewModel.renderer = renderer
}
}
}
init(viewModel: ViewModel) {
self.viewModel = viewModel
}
func renderer(_ renderer: any SCNSceneRenderer, updateAtTime time: TimeInterval) {
self.renderer = renderer
}
}
Upvotes: 0