Reputation: 15247
I have a visionOS app using immersive space with RealityView
. The app adds RealityKit entities to the app's Scene
instance, and uses the scene.raycast
function to find CollisionCastHit
s, see here.
I want now to write a unit test to check if the app finds the right hits.
To do so, I have to access the Scene
instance to add entities, and to check if they are hit by a raycast.
But how can I access the scene instance?
I can access it e.g. after creating the RealityView
via its content
parameter or via @Environment(\.realityKitScene)
.
But this is not possible in a unit test.
EDIT:
I tried to write a unit test, following the suggestion of Andy Jazz below, as far as I understood it.
I tried to initialize a RealityView
instance and added an entity to its content in order to get a reference to its scene
via this entity.
Since the make
closure of RealityView
is async, one has to wait for its completion, otherwise the test would fail immediately. Waiting is done with withCheckedContinuation
.
I expected that scene
is stored then in its property.
Here is my test:
@MainActor @Test func test() async throws {
var scene: RealityKit.Scene?
await withCheckedContinuation { continuation in
_ = RealityView(make: { content in
print("make")
let entity = Entity()
content.add(entity)
scene = entity.scene
continuation.resume()
})
}
#expect(scene != nil)
}
However, when I run this test, I get the log
◇ Test test() started.
SWIFT TASK CONTINUATION MISUSE: test() leaked its continuation!
I assume the reason is that RealityView
has to be set up in a SwiftUI View
body
, and since this is not the case, continuation.resume()
will never be called, which is a continuation
misuse.
Upvotes: 1
Views: 148
Reputation: 58533
Try using optional rvc.entities.first?.scene
object (or bp.scene
):
RealityView { rvc in
let bp = try! await Entity(named: "biplane")
bp.scale *= 10
bp.position.z = -3.0
bp.generateCollisionShapes(recursive: true)
rvc.add(bp)
} update: { rvc in
print(rvc.entities.first?.scene?.raycast(
from: .zero,
to: [0, 0.5,-3]).first?.entity.name ?? "no model"
)
// Result:
// "toy_biplane_bind"
}
Put withCheckedContinuation(...)
function inside your RealityView's make closure and don't forget to insert a Task
block for scene
initialization:
import Testing
import RealityKit
import SwiftUI
@testable import MyCodeFor
struct MyCodeForTests {
@MainActor @Test func test() async throws {
var scene: RealityKit.Scene?
_ = RealityView { content in
await withCheckedContinuation { continuation in
let entity = Entity()
content.add(entity)
Task {
scene = entity.scene
continuation.resume()
}
}
#expect(scene != nil)
}
}
}
Upvotes: 0