Search code examples
swiftswiftuirealitykitreality-composer

How to load Reality Composer scenes asynchronously on SwiftUI?


On my project, I load reality files synchronously like this:

struct: DemoView: View {
    @State private var arView = ARView(frame: .zero)
    @State private var Scene1 = try! Experience1.loadScene1()

    var body: some View {
       ZStack {
         ARViewContainer(arView: $arView, Scene1: $Scene1)
           .ignoresSafeArea()
         Button("play") {
           Scene1.notifications.replay.post()
         }
       }
    }
}

struct ARViewContainer: UIViewRepresentable {
    @Binding var arView: ARView
    @Binding var Scene1: Experience1.Scene1

    func makeUIView(context: Context) -> ARView {
        DispatchQueue.main.asyncAfter(deadline: .now() + 2.0) {
            arView.scene.anchors.append(Scene1)
        }
     
        return ARView
    }
}

Since my model in Reality Scene has a lot of polygons, can I turn to load reality files asynchronously?


Solution

  • Async loading method for Reality Composer files

    Try the following code when you need to load a Reality Composer scene asynchronously:

    import SwiftUI
    import RealityKit
    
    struct ContentView : View {
        var body: some View {
            ARViewContainer().ignoresSafeArea()
        }
    }
    

    struct ARViewContainer : UIViewRepresentable {
        
        let arView = ARView(frame: .zero)
        let anchor = AnchorEntity()
        
        func makeUIView(context: Context) -> ARView {
            
            Experience.loadBoxAsync(completion: { result in
                do {
                    let heavyBoxScene = try result.get()
                    arView.scene.anchors.append(heavyBoxScene)
                } catch {
                    print("Error: \(error.localizedDescription)")
                }
            })
            return arView
        }
        func updateUIView(_ vue: ARView, context: Context) { }
    }
    

    Async loading method for USDZ files

    Try the following code when you need to load .usdz model asynchronously:

    import SwiftUI
    import RealityKit
    import Combine
    
    struct ARViewContainer : UIViewRepresentable {
        
        @State var cancellable: AnyCancellable? = nil
        let anchor = AnchorEntity()
        let arView = ARView(frame: .zero)
        
        func makeUIView(context: Context) -> ARView {
    
            DispatchQueue.main.async {
    
                cancellable = Entity.loadModelAsync(named: "character.usdz").sink(
                    
                    receiveCompletion: { completion in
                        if case let .failure(error) = completion {
                            print("Unable to load a model due to \(error)")
                        }
                        self.cancellable?.cancel()
                        
                    }, receiveValue: { [self] (model: Entity) in
                        anchor.addChild(model)
                        anchor.position.z = -1.0
                        arView.scene.anchors.append(anchor)
                    })
            }
            return arView
        }
        func updateUIView(_ vue: ARView, context: Context) { }
    }
    

    P. S.

    Answer to your next question.