Sunday, 20 September 2020

How to add Reality file in a Swift Package Manager?

I've heard with the Xcode 12 (now in Beta 6), Swift package manager is now able to include resources. But I am not able to open a reality (.rcproject) file.

Here is what I have tried; (& you can reproduce)

  1. I created a new Augmented Reality App project. (RealityKit + SwiftUI + Swift)
  2. Now if you try to run the project, everything works, you see a default metallic box.
  3. Now I created a new SPM (Swift package manager)
  4. Now I dragged locally created SPM to the project and added it to frameworks in General > Targets tab. (To inform the project about locally added spm)
  5. I dragged Experience.rcproject & ContentView (also copied the autogenerated Experience enum, you can reach it via Cmd+Click) to SPM
  6. Fixed some access initializer issue for ContentView & added platform support platforms: [.iOS(.v13)], in the SPM
  7. Added resources in the SPM for the path Experience.rcproject exist

After those steps finished I'd except to have an AR included swift package manager.
But auto generated Experience enum throws .fileNotFound("Experience.reality") error.
Seems still not able to find reality file in Bundle?

Have you tried something similar. Waiting any helps. Thanks..


Folder Structure

Package.swift

// swift-tools-version:5.3
// The swift-tools-version declares the minimum version of Swift required to build this package.

import PackageDescription

let package = Package(
    name: "ARSPM",
    platforms: [.iOS(.v13)],
    products: [
        .library(
            name: "ARSPM",
            targets: ["ARSPM"]),
    ],
    dependencies: [],
    targets: [
        .target(
            name: "ARSPM",
            dependencies: [], resources: [
                .copy("Resources")
            ]),
        .testTarget(
            name: "ARSPMTests",
            dependencies: ["ARSPM"]),
    ]
)

ARView.swift

import SwiftUI
import RealityKit

public struct EKARView : View {
    public init() { }
    public var body: some View {
        return ARViewContainer().edgesIgnoringSafeArea(.all)
    }
}

public struct ARViewContainer: UIViewRepresentable {
    
    public func makeUIView(context: Context) -> ARView {
        
        let arView = ARView(frame: .zero)
        
        // Load the "Box" scene from the "Experience" Reality File
        let boxAnchor = try! Experience.loadBox()
        
        // Add the box anchor to the scene
        arView.scene.anchors.append(boxAnchor)
        
        return arView
        
    }
    
    public func updateUIView(_ uiView: ARView, context: Context) {}
    
}

GeneratedExperienceFile.swift

//
// Experience.swift
// GENERATED CONTENT. DO NOT EDIT.
//

import Foundation
import RealityKit
import simd
import Combine

internal enum Experience {

    public enum LoadRealityFileError: Error {
        case fileNotFound(String)
    }

    private static var streams = [Combine.AnyCancellable]()

    public static func loadBox() throws -> Experience.Box {
        guard let realityFileURL =
//                Also tried >> Foundation.Bundle.module
                Foundation.Bundle(for: Experience.Box.self)
                    .url(forResource: "Experience", withExtension: "reality") else {
            throw Experience.LoadRealityFileError.fileNotFound("Experience.reality")
        }

        let realityFileSceneURL = realityFileURL.appendingPathComponent("Box", isDirectory: false)
        let anchorEntity = try Experience.Box.loadAnchor(contentsOf: realityFileSceneURL)
        return createBox(from: anchorEntity)
    }

    public static func loadBoxAsync(completion: @escaping (Swift.Result<Experience.Box, Swift.Error>) -> Void) {
        guard let realityFileURL = Foundation.Bundle(for: Experience.Box.self).url(forResource: "Experience", withExtension: "reality") else {
            completion(.failure(Experience.LoadRealityFileError.fileNotFound("Experience.reality")))
            return
        }

        var cancellable: Combine.AnyCancellable?
        let realityFileSceneURL = realityFileURL.appendingPathComponent("Box", isDirectory: false)
        let loadRequest = Experience.Box.loadAnchorAsync(contentsOf: realityFileSceneURL)
        cancellable = loadRequest.sink(receiveCompletion: { loadCompletion in
            if case let .failure(error) = loadCompletion {
                completion(.failure(error))
            }
            streams.removeAll { $0 === cancellable }
        }, receiveValue: { entity in
            completion(.success(Experience.createBox(from: entity)))
        })
        cancellable?.store(in: &streams)
    }

    private static func createBox(from anchorEntity: RealityKit.AnchorEntity) -> Experience.Box {
        let box = Experience.Box()
        box.anchoring = anchorEntity.anchoring
        box.addChild(anchorEntity)
        return box
    }

    public class Box: RealityKit.Entity, RealityKit.HasAnchoring {

        public var steelBox: RealityKit.Entity? {
            return self.findEntity(named: "Steel Box")
        }

    }

}

And in ContentView file, I simple show EKARView.



from How to add Reality file in a Swift Package Manager?

No comments:

Post a Comment