Search code examples
swiftswiftuirealitykitreality-composer

Loading scene asynchronously in ContentView and binding to ARViewContainer


From my question here, I would like to use the scene with notifications in Reality Composer like

Scene1.notifications.replay.post()

Therefore, I load the scene in ContentView and binding to the ARViewContainer for appending the scene, but it didn't work.

How can I do it?

Here's my code:

struct: ContentView: View {
    @State private var arView = ARView(frame: .zero)
    @State private var Scene1 = Experience.loadSceneAsync(completion: { result in
        do {
            let Scene = try result.get()
        } catch {
            print("Error")
        }
    })

    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 {
        
        arView.scene.anchors.append(Scene1)
     
        return ARView
    }
}

Solution

  • Async-loaded models from Reality Composer scene

    In order to implement the Combine's publisher/subscriber logics, we need to create a class with a @Published property wrapper, in which we will place the RealityKit's asynchronous loading method.

    @Published and @ObservedObject wrappers used here to manage state from external objects.


    enter image description here


    import SwiftUI
    import RealityKit
    
    class Catcher: ObservableObject {
            
        @Published var scene = Experience.Box()
        
        func sceneLoader() {
            Experience.loadBoxAsync(completion: { result in
                do {
                    self.scene = try result.get()
                    self.scene.steelBox?.scale *= 4
                } catch {
                    print("Error: \(error.localizedDescription)")
                }
            })
        }
    
        func notificationLoader() {
            if scene.notifications.allNotifications.count > 0 {
                self.scene.notifications.replay.post()
            }
        }
    }
    

    struct ContentView : View {
        
        @State private var boolean: Bool = false
        
        var body: some View {
            ZStack {
                ARViewContainer(boolean: $boolean)
                    .ignoresSafeArea()
                VStack {
                    Spacer()
                    Button("Play Animation") {
                        boolean = true
                    }
                }
            }
        }
    }
    

    struct ARViewContainer : UIViewRepresentable {
        
        @ObservedObject var catcher = Catcher()
        @Binding var boolean: Bool
        var arView = ARView(frame: .zero)
        
        func makeUIView(context: Context) -> ARView {
            catcher.sceneLoader()
            return arView
        }
        func updateUIView(_ vue: ARView, context: Context) {
    
            vue.scene.anchors.append(catcher.scene)
            
            if boolean {
                catcher.notificationLoader()
            }
            DispatchQueue.main.async {
                if boolean {
                    boolean = false
                }
            }
        }
    }
    

    enter image description here