swiftmultithreadingarkitrealitykitswift-concurrency

Call to main actor-isolated instance method in a synchronous nonisolated context


I’m working on an ARKit project in Swift where I’m trying to receive AR anchors and place a small sphere at each detected anchor in the AR scene. I have a class AppModel that conforms to ARSessionDelegate, and it manages the AR session and scene content.

I get the following error:

Call to main actor-isolated instance method ‘createSphereEntity(radius:)’ in a synchronous nonisolated context

How to solve?

import Foundation
import RealityAR
import SwiftUI
import ARKit
import RealityKit

@MainActor
@Observable
class AppModel: NSObject, ARSessionDelegate {
    
    let session = ARSession()
//    let sceneRecostruction = SceneReconstructionProvider()
//    let handTracking = HandTrackingProvider()
    
    var arView: ARView!
    var objectInScene = Entity()
    
    override init() {
        super.init()
        setupARView()
    }
    
    func setupARView() {
        // Initialize the ARView
        arView = ARView(frame: .zero)
        arView.session.delegate = self  // Set ARSessionDelegate
        
        // Create AR session configuration
        let configuration = ARWorldTrackingConfiguration()
        configuration.planeDetection = [.horizontal, .vertical]
        
        // Enable scene reconstruction if needed
        if ARWorldTrackingConfiguration.supportsSceneReconstruction(.mesh) {
            configuration.sceneReconstruction = .mesh
        }
        
        // Run AR session
        arView.session.run(configuration)
        print("Session running")
    }
    
    func loadModel() async {
        // Add the initial RealityKit content
        if let person = try? await Entity(named: "person", in: realityARBundle) {
            let radians = 180.0 * Float.pi / 180.0
            person.transform.rotation = simd_quatf(angle: radians,axis: SIMD3<Float>(0,1,0))
            person.scale = SIMD3(0.5, 0.5, 0.5)
            person.components.set(GroundingShadowComponent(castsShadow: true))
            let anchor = AnchorEntity(.plane(.horizontal, classification: .table, minimumBounds: SIMD2(0.5, 0.5)))
            anchor.addChild(person)
            arView.scene.addAnchor(anchor)
        } else {
            print("Couldn't load the character")
        }
    }
   
    // Helper function to create a sphere entity
    func createSphereEntity(radius: Float) -> ModelEntity {
        let mesh = MeshResource.generateSphere(radius: radius)
        let material = SimpleMaterial(color: .blue, isMetallic: false)
        return ModelEntity(mesh: mesh, materials: [material])
    }
    
    nonisolated func session(_ session: ARSession, didAdd anchors: [ARAnchor]) {
        let spere = createSphereEntity(radius: 0.1) // <------ error here 
        
    }
    
}


Solution

  • If ARSessionDelegate methods, such as session(_:didAdd:), are called on the main thread, you can inform the compiler of this with assumeIsolated, and it will do a runtime check for you:

    nonisolated func session(_ session: ARSession, didAdd anchors: [ARAnchor]) {
        MainActor.assumeIsolated {
            let sphere = createSphereEntity(radius: 0.1)
            …
        }
    }
    

    The ARSession documentation is a bit light with respect to threading, but its delegateQueue documentation says:

    If this value is nil (the default), the session calls your delegate methods on the main queue.

    So, I suspect that this will be running on the main thread (unless you overrode the delegateQueue somewhere). In which case, the assumeIsolated would be the correct approach.