@sceneview-sdk/react-native 4.14.0 → 4.15.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -6,7 +6,9 @@
6
6
 
7
7
  React Native bindings for [SceneView](https://sceneview.github.io) — 3D and AR scenes powered by Filament (Android) and RealityKit (iOS).
8
8
 
9
- > **Status:** Alpha — 3D model loading works on both platforms. AR scene is functional on Android.
9
+ > **Status:** Alpha — 3D model loading works on both platforms. AR scene is
10
+ > functional on Android. The iOS native bridge compiles against the real
11
+ > `SceneViewSwift` API and is CI-verified (`.github/workflows/rn-ios-compile.yml`).
10
12
 
11
13
  ## Features
12
14
 
@@ -45,9 +47,18 @@ npm install github:sceneview/sceneview#v4.0.9 --save # path includes react-nati
45
47
  cd ios && pod install
46
48
  ```
47
49
 
48
- Requires iOS 17+ and Xcode 15+. The host app must also add `SceneViewSwift` via Swift Package Manager:
50
+ Requires iOS 17+ and Xcode 15+.
51
+
52
+ **The host app must add `SceneViewSwift` via Swift Package Manager.**
53
+ `SceneViewSwift` ships as a SwiftPM package only (no CocoaPods spec), so this
54
+ module's podspec deliberately does **not** declare it as a `s.dependency` —
55
+ add it once in Xcode (*File ▸ Add Package Dependencies…*):
56
+
49
57
  - URL: `https://github.com/sceneview/SceneViewSwift`
50
- - Version: `4.0.0`
58
+ - Version: `4.14.0` (or *Up to Next Major*)
59
+
60
+ The module's `ios/*.swift` `import SceneViewSwift` resolves against that
61
+ app-level package at build time.
51
62
 
52
63
  ### Android
53
64
 
@@ -163,10 +174,10 @@ React Native JS
163
174
  requireNativeComponent('RNSceneView' / 'RNARSceneView')
164
175
  |
165
176
  +---> Android: ViewManager -> ComposeView -> SceneView { } / ARSceneView { }
166
- | (Filament, SceneView SDK 4.0.0)
177
+ | (Filament, SceneView SDK)
167
178
  |
168
179
  +---> iOS: RCTViewManager -> UIHostingController -> SceneView / ARSceneView
169
- (RealityKit, SceneViewSwift 4.0.0)
180
+ (RealityKit, SceneViewSwift)
170
181
  ```
171
182
 
172
183
  Props are mapped from the React Native bridge to native view parameters on each platform.
@@ -3,6 +3,7 @@ package io.github.sceneview.reactnative
3
3
  import android.widget.FrameLayout
4
4
  import androidx.compose.foundation.layout.fillMaxSize
5
5
  import androidx.compose.runtime.DisposableEffect
6
+ import androidx.compose.runtime.LaunchedEffect
6
7
  import androidx.compose.runtime.mutableStateListOf
7
8
  import androidx.compose.runtime.mutableStateOf
8
9
  import androidx.compose.runtime.remember
@@ -10,16 +11,24 @@ import androidx.compose.ui.Modifier
10
11
  import androidx.compose.ui.platform.ComposeView
11
12
  import com.facebook.react.bridge.ReadableArray
12
13
  import com.facebook.react.bridge.ReadableType
14
+ import com.facebook.react.common.MapBuilder
13
15
  import com.facebook.react.uimanager.SimpleViewManager
14
16
  import com.facebook.react.uimanager.ThemedReactContext
15
17
  import com.facebook.react.uimanager.annotations.ReactProp
16
18
  import com.google.android.filament.LightManager
19
+ import com.google.ar.core.Config
20
+ import com.google.ar.core.Plane
21
+ import com.google.ar.core.Session
17
22
  import io.github.sceneview.SurfaceType
23
+ import io.github.sceneview.ar.arcore.configure
24
+ import io.github.sceneview.ar.arcore.getUpdatedPlanes
25
+ import io.github.sceneview.gesture.GestureDetector
18
26
  import io.github.sceneview.math.Size
19
27
  import io.github.sceneview.rememberEngine
20
28
  import io.github.sceneview.rememberMaterialLoader
21
29
  import io.github.sceneview.rememberModelInstance
22
30
  import io.github.sceneview.rememberModelLoader
31
+ import java.util.concurrent.atomic.AtomicReference
23
32
 
24
33
  /**
25
34
  * Per-instance AR scene state stored as a tag on the FrameLayout container.
@@ -31,6 +40,14 @@ class ARSceneViewState {
31
40
  val planeDetection = mutableStateOf(true)
32
41
  val depthOcclusion = mutableStateOf(false)
33
42
  val instantPlacement = mutableStateOf(false)
43
+
44
+ /**
45
+ * ARCore [Plane] instances already reported to JS via `onPlaneDetected`.
46
+ * ARCore returns the same `Plane` reference for a given trackable across
47
+ * frames, so reference identity is the correct de-duplication key — the
48
+ * event fires exactly once per newly-detected plane (issue #2053).
49
+ */
50
+ val reportedPlanes = HashSet<Plane>()
34
51
  }
35
52
 
36
53
  /**
@@ -48,17 +65,86 @@ class ARSceneViewManager : SimpleViewManager<FrameLayout>() {
48
65
  return view.tag as? ARSceneViewState ?: ARSceneViewState().also { view.tag = it }
49
66
  }
50
67
 
68
+ /**
69
+ * Applies the `depthOcclusion` / `instantPlacement` JS props to an ARCore
70
+ * [Config]. Shared by the session-creation `sessionConfiguration` callback
71
+ * and the live-session re-configure path so the two never diverge.
72
+ *
73
+ * `depthMode` is support-gated: enabling `Config.DepthMode.AUTOMATIC` on a
74
+ * device that does not support depth would make `Session.configure` throw,
75
+ * so it falls back to `DISABLED` when unsupported.
76
+ */
77
+ private fun applyArConfig(
78
+ session: Session,
79
+ config: Config,
80
+ depthOcclusion: Boolean,
81
+ instantPlacement: Boolean,
82
+ ) {
83
+ config.depthMode =
84
+ if (depthOcclusion &&
85
+ session.isDepthModeSupported(Config.DepthMode.AUTOMATIC)
86
+ ) {
87
+ Config.DepthMode.AUTOMATIC
88
+ } else {
89
+ Config.DepthMode.DISABLED
90
+ }
91
+ config.instantPlacementMode =
92
+ if (instantPlacement) {
93
+ Config.InstantPlacementMode.LOCAL_Y_UP
94
+ } else {
95
+ Config.InstantPlacementMode.DISABLED
96
+ }
97
+ }
98
+
99
+ /**
100
+ * Registers `onTap` / `onPlaneDetected` so React Native delivers the native
101
+ * event dispatches to the matching JS props. Without these entries the
102
+ * events are silently dropped even when the native side dispatches them
103
+ * (issue #2053).
104
+ */
105
+ override fun getExportedCustomDirectEventTypeConstants(): Map<String, Any> =
106
+ MapBuilder.builder<String, Any>()
107
+ .put(TapEvent.NAME, MapBuilder.of("registrationName", "onTap"))
108
+ .put(
109
+ PlaneDetectedEvent.NAME,
110
+ MapBuilder.of("registrationName", "onPlaneDetected"),
111
+ )
112
+ .build()
113
+
51
114
  override fun createViewInstance(reactContext: ThemedReactContext): FrameLayout {
52
115
  val container = FrameLayout(reactContext)
53
116
  val state = ARSceneViewState()
54
117
  container.tag = state
55
118
 
119
+ // Tap gesture → JS `onTap` (issue #2053).
120
+ val gestureListener = object : GestureDetector.SimpleOnGestureListener() {
121
+ override fun onSingleTapConfirmed(
122
+ e: android.view.MotionEvent,
123
+ node: io.github.sceneview.node.Node?,
124
+ ) {
125
+ dispatchTapEvent(reactContext, container, node)
126
+ }
127
+ }
128
+
56
129
  val composeView = ComposeView(reactContext).apply {
57
130
  setContent {
58
131
  val engine = rememberEngine()
59
132
  val modelLoader = rememberModelLoader(engine)
60
133
  val materialLoader = rememberMaterialLoader(engine)
61
134
 
135
+ // Read the AR config flags so they recompose this content when
136
+ // JS toggles a prop (issue #2055).
137
+ val depthOcclusion = state.depthOcclusion.value
138
+ val instantPlacement = state.instantPlacement.value
139
+
140
+ // Live ARCore session, captured once it is created. The
141
+ // consumed `arsceneview:4.7.0` invokes `sessionConfiguration`
142
+ // ONLY at session creation — it never re-runs the lambda on a
143
+ // later recomposition — so a JS prop toggle would otherwise be
144
+ // a silent no-op. Holding the Session lets the LaunchedEffect
145
+ // below re-apply the Config to the running session (#2070).
146
+ val sessionRef = remember { AtomicReference<Session?>(null) }
147
+
62
148
  io.github.sceneview.ar.ARSceneView(
63
149
  modifier = Modifier.fillMaxSize(),
64
150
  surfaceType = SurfaceType.TextureSurface,
@@ -66,6 +152,35 @@ class ARSceneViewManager : SimpleViewManager<FrameLayout>() {
66
152
  modelLoader = modelLoader,
67
153
  materialLoader = materialLoader,
68
154
  planeRenderer = state.planeDetection.value,
155
+ onGestureListener = gestureListener,
156
+ // depthOcclusion / instantPlacement → ARCore Config at
157
+ // session creation. The typed `depthMode` /
158
+ // `instantPlacementMode` composable params (#1766) post-date
159
+ // the consumed arsceneview artifact, so the INITIAL values
160
+ // are applied via the stable `sessionConfiguration`
161
+ // callback. `sessionConfiguration` runs only once (at
162
+ // creation); runtime toggles are re-applied by the
163
+ // LaunchedEffect below (issue #2070).
164
+ sessionConfiguration = { session, config ->
165
+ applyArConfig(session, config, depthOcclusion, instantPlacement)
166
+ },
167
+ // Capture the live Session so prop toggles can re-configure
168
+ // it after creation (issue #2070).
169
+ onSessionCreated = { session ->
170
+ sessionRef.set(session)
171
+ },
172
+ // Per-frame plane diff → JS `onPlaneDetected`. Fires once
173
+ // per newly-tracked ARCore plane (issue #2053).
174
+ onSessionUpdated = { _, frame ->
175
+ for (plane in frame.getUpdatedPlanes()) {
176
+ if (plane.trackingState != com.google.ar.core.TrackingState.TRACKING) {
177
+ continue
178
+ }
179
+ if (state.reportedPlanes.add(plane)) {
180
+ dispatchPlaneEvent(reactContext, container, plane)
181
+ }
182
+ }
183
+ },
69
184
  ) {
70
185
  state.modelPaths.forEach { model ->
71
186
  val instance = rememberModelInstance(modelLoader, model.src)
@@ -158,6 +273,30 @@ class ARSceneViewManager : SimpleViewManager<FrameLayout>() {
158
273
  )
159
274
  }
160
275
  }
276
+
277
+ // Re-apply the AR config to the LIVE session whenever JS
278
+ // toggles `depthOcclusion` / `instantPlacement` after the
279
+ // session was created. `sessionConfiguration` only runs once
280
+ // (at creation), so without this the toggle is a silent no-op
281
+ // on the running session (issue #2070). Keyed on the flag
282
+ // values so it re-runs on every change; `Session.configure`
283
+ // is a no-op for the initial values already applied at
284
+ // creation. ARCore's `configure` is safe to call on a
285
+ // resumed session.
286
+ LaunchedEffect(depthOcclusion, instantPlacement) {
287
+ sessionRef.get()?.let { session ->
288
+ runCatching {
289
+ session.configure { config ->
290
+ applyArConfig(
291
+ session,
292
+ config,
293
+ depthOcclusion,
294
+ instantPlacement,
295
+ )
296
+ }
297
+ }
298
+ }
299
+ }
161
300
  }
162
301
  }
163
302
  container.addView(composeView)
@@ -0,0 +1,130 @@
1
+ package io.github.sceneview.reactnative
2
+
3
+ import android.view.View
4
+ import com.facebook.react.bridge.Arguments
5
+ import com.facebook.react.bridge.ReactContext
6
+ import com.facebook.react.bridge.WritableMap
7
+ import com.facebook.react.uimanager.UIManagerHelper
8
+ import com.facebook.react.uimanager.events.Event
9
+ import com.google.ar.core.Plane
10
+ import io.github.sceneview.node.Node
11
+
12
+ /**
13
+ * RN Fabric event delivered to the JS `onTap` prop of `<SceneView>` / `<ARSceneView>`.
14
+ *
15
+ * Payload mirrors the TypeScript `TapEvent` interface in `src/index.tsx`:
16
+ * `{ x, y, z, nodeName? }`. Wired in [SceneViewManager] / [ARSceneViewManager]
17
+ * via `getExportedCustomDirectEventTypeConstants` (issue #2053).
18
+ */
19
+ class TapEvent(
20
+ surfaceId: Int,
21
+ viewId: Int,
22
+ private val x: Float,
23
+ private val y: Float,
24
+ private val z: Float,
25
+ private val nodeName: String?,
26
+ ) : Event<TapEvent>(surfaceId, viewId) {
27
+
28
+ override fun getEventName(): String = NAME
29
+
30
+ override fun getEventData(): WritableMap = Arguments.createMap().apply {
31
+ putDouble("x", x.toDouble())
32
+ putDouble("y", y.toDouble())
33
+ putDouble("z", z.toDouble())
34
+ if (nodeName != null) putString("nodeName", nodeName) else putNull("nodeName")
35
+ }
36
+
37
+ companion object {
38
+ const val NAME = "topSceneViewTap"
39
+ }
40
+ }
41
+
42
+ /**
43
+ * RN Fabric event delivered to the JS `onPlaneDetected` prop of `<ARSceneView>`.
44
+ *
45
+ * Payload mirrors the TypeScript `PlaneDetectedEvent` interface in `src/index.tsx`:
46
+ * `{ id, type, center, extent }`. Fires once per newly-detected ARCore plane
47
+ * (issue #2053).
48
+ */
49
+ class PlaneDetectedEvent(
50
+ surfaceId: Int,
51
+ viewId: Int,
52
+ private val id: String,
53
+ private val type: String,
54
+ private val center: FloatArray,
55
+ private val extent: FloatArray,
56
+ ) : Event<PlaneDetectedEvent>(surfaceId, viewId) {
57
+
58
+ override fun getEventName(): String = NAME
59
+
60
+ override fun getEventData(): WritableMap = Arguments.createMap().apply {
61
+ putString("id", id)
62
+ putString("type", type)
63
+ putArray("center", Arguments.fromArray(center))
64
+ putArray("extent", Arguments.fromArray(extent))
65
+ }
66
+
67
+ companion object {
68
+ const val NAME = "topSceneViewPlaneDetected"
69
+ }
70
+ }
71
+
72
+ /**
73
+ * Dispatches a [TapEvent] for [view] through React Native's [EventDispatcher],
74
+ * carrying the tapped [node]'s name and world-space position (issue #2053).
75
+ */
76
+ internal fun dispatchTapEvent(
77
+ reactContext: ReactContext,
78
+ view: View,
79
+ node: Node?,
80
+ ) {
81
+ val surfaceId = UIManagerHelper.getSurfaceId(view)
82
+ val dispatcher =
83
+ UIManagerHelper.getEventDispatcherForReactTag(reactContext, view.id) ?: return
84
+ val worldPosition = node?.worldPosition
85
+ dispatcher.dispatchEvent(
86
+ TapEvent(
87
+ surfaceId = surfaceId,
88
+ viewId = view.id,
89
+ x = worldPosition?.x ?: 0f,
90
+ y = worldPosition?.y ?: 0f,
91
+ z = worldPosition?.z ?: 0f,
92
+ nodeName = node?.name,
93
+ )
94
+ )
95
+ }
96
+
97
+ /**
98
+ * Maps an ARCore [Plane] to a [PlaneDetectedEvent] and dispatches it for [view]
99
+ * through React Native's [EventDispatcher] (issue #2053).
100
+ *
101
+ * The JS `PlaneDetectedEvent` only distinguishes `'horizontal'` / `'vertical'`,
102
+ * so ARCore's two horizontal sub-types both collapse to `'horizontal'`.
103
+ */
104
+ internal fun dispatchPlaneEvent(
105
+ reactContext: ReactContext,
106
+ view: View,
107
+ plane: Plane,
108
+ ) {
109
+ val surfaceId = UIManagerHelper.getSurfaceId(view)
110
+ val dispatcher =
111
+ UIManagerHelper.getEventDispatcherForReactTag(reactContext, view.id) ?: return
112
+ val type = when (plane.type) {
113
+ Plane.Type.VERTICAL -> "vertical"
114
+ else -> "horizontal"
115
+ }
116
+ val centerPose = plane.centerPose
117
+ dispatcher.dispatchEvent(
118
+ PlaneDetectedEvent(
119
+ surfaceId = surfaceId,
120
+ viewId = view.id,
121
+ // ARCore exposes no stable string id; the Plane's identity hash is
122
+ // stable for the lifetime of the trackable, which is all the JS
123
+ // `id` field needs.
124
+ id = System.identityHashCode(plane).toString(),
125
+ type = type,
126
+ center = floatArrayOf(centerPose.tx(), centerPose.ty(), centerPose.tz()),
127
+ extent = floatArrayOf(plane.extentX, plane.extentZ),
128
+ )
129
+ )
130
+ }
@@ -10,12 +10,14 @@ import androidx.compose.ui.Modifier
10
10
  import androidx.compose.ui.platform.ComposeView
11
11
  import com.facebook.react.bridge.ReadableArray
12
12
  import com.facebook.react.bridge.ReadableType
13
+ import com.facebook.react.common.MapBuilder
13
14
  import com.facebook.react.uimanager.SimpleViewManager
14
15
  import com.facebook.react.uimanager.ThemedReactContext
15
16
  import com.facebook.react.uimanager.annotations.ReactProp
16
17
  import com.google.android.filament.LightManager
17
18
  import io.github.sceneview.SceneView
18
19
  import io.github.sceneview.SurfaceType
20
+ import io.github.sceneview.gesture.GestureDetector
19
21
  import io.github.sceneview.math.Direction
20
22
  import io.github.sceneview.math.Position
21
23
  import io.github.sceneview.math.Rotation
@@ -69,11 +71,33 @@ class SceneViewManager : SimpleViewManager<FrameLayout>() {
69
71
  return view.tag as? SceneViewState ?: SceneViewState().also { view.tag = it }
70
72
  }
71
73
 
74
+ /**
75
+ * Registers `onTap` so React Native delivers the native [TapEvent] dispatch
76
+ * to the JS `onTap` prop. Without this entry the event is silently dropped
77
+ * even when the native side dispatches it (issue #2053).
78
+ */
79
+ override fun getExportedCustomDirectEventTypeConstants(): Map<String, Any> =
80
+ MapBuilder.builder<String, Any>()
81
+ .put(TapEvent.NAME, MapBuilder.of("registrationName", "onTap"))
82
+ .build()
83
+
72
84
  override fun createViewInstance(reactContext: ThemedReactContext): FrameLayout {
73
85
  val container = FrameLayout(reactContext)
74
86
  val state = SceneViewState()
75
87
  container.tag = state
76
88
 
89
+ // Tap gesture → JS `onTap`. SceneView's GestureDetector resolves the
90
+ // tapped node (if any) via collision hit-testing, so the event carries
91
+ // the node name and its world-space position (issue #2053).
92
+ val gestureListener = object : GestureDetector.SimpleOnGestureListener() {
93
+ override fun onSingleTapConfirmed(
94
+ e: android.view.MotionEvent,
95
+ node: io.github.sceneview.node.Node?,
96
+ ) {
97
+ dispatchTapEvent(reactContext, container, node)
98
+ }
99
+ }
100
+
77
101
  val composeView = ComposeView(reactContext).apply {
78
102
  setContent {
79
103
  val engine = rememberEngine()
@@ -100,6 +124,7 @@ class SceneViewManager : SimpleViewManager<FrameLayout>() {
100
124
  materialLoader = materialLoader,
101
125
  cameraNode = cameraNode,
102
126
  environment = environment ?: rememberEnvironment(environmentLoader),
127
+ onGestureListener = gestureListener,
103
128
  ) {
104
129
  state.modelPaths.forEach { model ->
105
130
  val instance = rememberModelInstance(modelLoader, model.src)
@@ -1,5 +1,6 @@
1
1
  import Foundation
2
2
  import React
3
+ import RealityKit
3
4
  import SceneViewSwift
4
5
  import SwiftUI
5
6
 
@@ -51,6 +52,11 @@ class RNSceneState: ObservableObject {
51
52
  @Published var cameraOrbit: Bool = true
52
53
  @Published var cameraControlMode: CameraControlMode = .orbit
53
54
  @Published var autoCenterContent: Bool = true
55
+
56
+ /// Invoked from the SwiftUI content's `onEntityTapped` modifier so the
57
+ /// wrapper can forward the tap to React Native's `onTap` prop (issue #2053).
58
+ /// Not `@Published` — it is plumbing, not rendered state.
59
+ var onTap: ((Entity) -> Void)?
54
60
  }
55
61
 
56
62
  /// UIView wrapper that hosts a SwiftUI `SceneView` via UIHostingController.
@@ -60,7 +66,24 @@ class RNSceneViewWrapper: UIView {
60
66
  private let sceneState = RNSceneState()
61
67
 
62
68
  /// Event callback for tap events.
63
- @objc var onTap: RCTDirectEventBlock?
69
+ @objc var onTap: RCTDirectEventBlock? {
70
+ didSet {
71
+ let block = onTap
72
+ Task { @MainActor in
73
+ sceneState.onTap = { entity in
74
+ // Mirrors the Android `TapEvent` payload: world-space
75
+ // coordinates of the tapped entity + its node name.
76
+ let p = entity.position(relativeTo: nil)
77
+ block?([
78
+ "x": p.x,
79
+ "y": p.y,
80
+ "z": p.z,
81
+ "nodeName": entity.name,
82
+ ])
83
+ }
84
+ }
85
+ }
86
+ }
64
87
 
65
88
  override init(frame: CGRect) {
66
89
  super.init(frame: frame)
@@ -145,20 +168,75 @@ class RNSceneViewWrapper: UIView {
145
168
  }
146
169
  }
147
170
 
148
- /// SwiftUI content view rendering SceneViewSwift.SceneView.
171
+ /// SwiftUI content view rendering `SceneViewSwift.SceneView` (issue #2067).
172
+ ///
173
+ /// `SceneViewSwift` loads models **asynchronously** (`ModelNode.load(_:)` is
174
+ /// `async throws`) — there is no synchronous `ModelNode(String)` initialiser,
175
+ /// and the `@NodeBuilder` content builder composes static `EntityProvider`
176
+ /// values, not a SwiftUI `ForEach`. So the bridge loads each `RNModelData`
177
+ /// off the main actor's `Task`, wraps the resulting `ModelEntity`s in a
178
+ /// stable holder entity, and feeds that holder to `SceneView`'s **imperative**
179
+ /// `init(_ content: (Entity) -> Void)`.
180
+ ///
181
+ /// `SceneView`'s content closure runs once when the underlying `RealityView`
182
+ /// is created, so the holder entity is built up front and re-populated in a
183
+ /// `.task(id:)` whenever the JS `modelNodes` prop changes — RealityKit picks
184
+ /// up the new children on its next render frame without recreating the view.
149
185
  struct RNSceneViewContent: View {
150
186
  @ObservedObject var state: RNSceneState
151
187
 
188
+ /// Stable parent entity handed to `SceneView`. Its children are the
189
+ /// loaded model entities; rebuilt by `loadModels()` on every prop change.
190
+ @State private var modelRoot = Entity()
191
+
152
192
  var body: some View {
153
- SceneView {
154
- ForEach(state.models) { model in
155
- ModelNode(model.path)
156
- .position(model.position)
157
- .scale(model.scale)
158
- }
193
+ SceneView { root in
194
+ root.addChild(modelRoot)
159
195
  }
160
196
  .cameraControls(state.cameraControlMode)
161
197
  .autoCenterContent(state.autoCenterContent)
198
+ // Forward entity taps to React Native's `onTap` prop (issue #2053).
199
+ .onEntityTapped { entity in
200
+ state.onTap?(entity)
201
+ }
202
+ // Reload whenever the JS `modelNodes` prop changes. Keyed on the
203
+ // model identities so an unrelated re-render does not re-download.
204
+ .task(id: state.models.map(\.id)) {
205
+ await loadModels()
206
+ }
207
+ }
208
+
209
+ /// Loads every model in `state.models` and replaces `modelRoot`'s
210
+ /// children with the freshly loaded entities. `ModelNode.load(_:)` is
211
+ /// `@MainActor`-isolated, so this runs on the main actor as required by
212
+ /// RealityKit. A failed load is skipped (the others still render).
213
+ @MainActor
214
+ private func loadModels() async {
215
+ // Clear previous content before reloading.
216
+ for child in modelRoot.children {
217
+ child.removeFromParent()
218
+ }
219
+ for model in state.models {
220
+ do {
221
+ let node = try await ModelNode.load(model.path)
222
+ // `.task(id:)` cancels this task when the `modelNodes` prop
223
+ // changes mid-load. A cancelled task still resumes past the
224
+ // `await`, so bail out before mutating the scene — otherwise a
225
+ // superseded load leaks a stale model into `modelRoot`.
226
+ guard !Task.isCancelled else { return }
227
+ node.position(model.position)
228
+ node.scale(model.scale)
229
+ if let animation = model.animation {
230
+ node.playAnimation(named: animation)
231
+ } else if node.animationCount > 0 {
232
+ node.playAllAnimations()
233
+ }
234
+ modelRoot.addChild(node.entity)
235
+ } catch {
236
+ // A single bad path must not break the whole scene.
237
+ print("[RNSceneView] Failed to load model '\(model.path)': \(error)")
238
+ }
239
+ }
162
240
  }
163
241
  }
164
242
 
@@ -185,6 +263,11 @@ class RNARSceneState: ObservableObject {
185
263
  @Published var planeDetection: Bool = true
186
264
  @Published var depthOcclusion: Bool = false
187
265
  @Published var instantPlacement: Bool = false
266
+
267
+ /// Invoked from `ARSceneView`'s `onTapOnPlane` so the wrapper can forward
268
+ /// the tap to React Native's `onTap` prop (issue #2053). Not `@Published` —
269
+ /// it is plumbing, not rendered state.
270
+ var onTap: ((SIMD3<Float>) -> Void)?
188
271
  }
189
272
 
190
273
  /// UIView wrapper that hosts a SwiftUI `ARSceneView` via UIHostingController.
@@ -194,9 +277,30 @@ class RNARSceneViewWrapper: UIView {
194
277
  private let sceneState = RNARSceneState()
195
278
 
196
279
  /// Event callback for tap events.
197
- @objc var onTap: RCTDirectEventBlock?
280
+ @objc var onTap: RCTDirectEventBlock? {
281
+ didSet {
282
+ let block = onTap
283
+ Task { @MainActor in
284
+ sceneState.onTap = { worldPosition in
285
+ // Mirrors the Android `TapEvent` payload. `ARSceneView`'s
286
+ // `onTapOnPlane` reports the tapped surface point, not a
287
+ // node, so `nodeName` is left absent.
288
+ block?([
289
+ "x": worldPosition.x,
290
+ "y": worldPosition.y,
291
+ "z": worldPosition.z,
292
+ ])
293
+ }
294
+ }
295
+ }
296
+ }
198
297
 
199
298
  /// Event callback for plane detection events.
299
+ ///
300
+ /// **iOS limitation (issue #2053):** SceneViewSwift's `ARSceneView` does
301
+ /// not expose a public per-plane-detected callback — only `onTapOnPlane`.
302
+ /// The block is accepted for API compatibility but is not yet invoked on
303
+ /// iOS; the TypeScript doc comment for `onPlaneDetected` discloses this.
200
304
  @objc var onPlaneDetected: RCTDirectEventBlock?
201
305
 
202
306
  override init(frame: CGRect) {
@@ -277,15 +381,99 @@ class RNARSceneViewWrapper: UIView {
277
381
  }
278
382
  }
279
383
 
280
- /// SwiftUI content view rendering SceneViewSwift.ARSceneView.
384
+ /// SwiftUI content view rendering `SceneViewSwift.ARSceneView` (issue #2067).
385
+ ///
386
+ /// `SceneViewSwift.ARSceneView` is a `UIViewRepresentable` with **no content
387
+ /// builder closure** — content is added imperatively to the underlying
388
+ /// `ARView` once the session starts (`onSessionStarted`) or on tap
389
+ /// (`onTapOnPlane`). The previous `ARSceneView(...) { anchor in ForEach … }`
390
+ /// trailing closure referenced API that does not exist and never compiled.
391
+ ///
392
+ /// This bridge captures the `ARView` in `onSessionStarted`, then loads each
393
+ /// `RNModelData` (async, via `ModelNode.load(_:)`) into a single
394
+ /// `AnchorNode` anchored at the world origin. The models are (re)placed in a
395
+ /// `.task(id:)` keyed on the JS `modelNodes` prop so prop changes are honoured
396
+ /// after the session has already started.
281
397
  struct RNARSceneViewContent: View {
282
398
  @ObservedObject var state: RNARSceneState
283
399
 
400
+ /// Captured once the AR session starts so prop-driven model reloads can
401
+ /// add / remove content after `makeUIView`. Held in a reference box so a
402
+ /// SwiftUI body re-evaluation does not lose it.
403
+ @State private var sessionBox = ARSessionBox()
404
+
405
+ /// Reference holder for the captured `ARView` + content anchor. A class
406
+ /// keeps the references stable across `RNARSceneViewContent` value copies.
407
+ @MainActor
408
+ final class ARSessionBox {
409
+ weak var arView: ARView?
410
+ /// Anchor that owns every placed model. Added to the scene the first
411
+ /// time the session starts; its children are rebuilt on prop changes.
412
+ var contentAnchor: AnchorNode?
413
+ }
414
+
284
415
  var body: some View {
285
- ARSceneView { anchor in
286
- ForEach(state.models) { model in
287
- ModelNode(model.path)
288
- .scale(model.scale)
416
+ // Forward surface taps to React Native's `onTap` prop (issue #2053).
417
+ // `depthOcclusion` / `instantPlacement` are accepted as props but have
418
+ // no `ARSceneView` configuration knob in SceneViewSwift yet — the
419
+ // TypeScript doc comments disclose that iOS gap (issue #2055).
420
+ ARSceneView(
421
+ planeDetection: state.planeDetection ? .both : .none,
422
+ onTapOnPlane: { worldPosition, _ in
423
+ state.onTap?(worldPosition)
424
+ }
425
+ )
426
+ .onSessionStarted { arView in
427
+ sessionBox.arView = arView
428
+ let anchor = AnchorNode.world(position: .zero)
429
+ arView.scene.addAnchor(anchor.entity)
430
+ sessionBox.contentAnchor = anchor
431
+ }
432
+ // (Re)load models whenever the JS `modelNodes` prop changes — runs
433
+ // after `onSessionStarted` too, so the initial set is placed once the
434
+ // session is up.
435
+ .task(id: state.models.map(\.id)) {
436
+ await placeModels()
437
+ }
438
+ }
439
+
440
+ /// Loads every model in `state.models` and replaces the content anchor's
441
+ /// children with the freshly loaded entities. Waits until the AR session
442
+ /// has provided a content anchor before placing anything.
443
+ @MainActor
444
+ private func placeModels() async {
445
+ // The session may not have started yet on the first invocation —
446
+ // poll briefly so the initial model set still lands.
447
+ var anchor = sessionBox.contentAnchor
448
+ var waited = 0
449
+ while anchor == nil && waited < 50 { // up to ~5 s
450
+ try? await Task.sleep(nanoseconds: 100_000_000)
451
+ waited += 1
452
+ anchor = sessionBox.contentAnchor
453
+ }
454
+ guard let anchor else { return }
455
+ // The poll above `await`s; if the `modelNodes` prop changed while we
456
+ // waited, this task was superseded — do not clear/repopulate the anchor.
457
+ guard !Task.isCancelled else { return }
458
+ anchor.removeAll()
459
+ for model in state.models {
460
+ do {
461
+ let node = try await ModelNode.load(model.path)
462
+ // `.task(id:)` cancels this task when the `modelNodes` prop
463
+ // changes mid-load. A cancelled task still resumes past the
464
+ // `await`, so bail out before mutating the scene — otherwise a
465
+ // superseded load leaks a stale model into the content anchor.
466
+ guard !Task.isCancelled else { return }
467
+ node.position(model.position)
468
+ node.scale(model.scale)
469
+ if let animation = model.animation {
470
+ node.playAnimation(named: animation)
471
+ } else if node.animationCount > 0 {
472
+ node.playAllAnimations()
473
+ }
474
+ anchor.add(node.entity)
475
+ } catch {
476
+ print("[RNARSceneView] Failed to load model '\(model.path)': \(error)")
289
477
  }
290
478
  }
291
479
  }
@@ -306,7 +494,11 @@ class RNARRecorder: NSObject {
306
494
  /// so multiple JS instances still drive one underlying recorder.
307
495
  @MainActor private lazy var recorder = ARRecorder()
308
496
 
309
- override static func requiresMainQueueSetup() -> Bool {
497
+ /// `RNARRecorder` extends `NSObject` directly (it is registered as an
498
+ /// `RCT_EXTERN_MODULE`, not a view manager), so this is **not** an
499
+ /// `override` — `NSObject` has no `requiresMainQueueSetup`. React Native
500
+ /// reads the static method via the bridge-module protocol. (#2067)
501
+ @objc static func requiresMainQueueSetup() -> Bool {
310
502
  return true
311
503
  }
312
504
 
@@ -13,9 +13,25 @@ function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e
13
13
 
14
14
  /** A 3D model loaded from a .glb / .gltf file. */
15
15
 
16
- /** A procedural geometry node (box, sphere, cylinder, plane). */
16
+ /**
17
+ * A procedural geometry node (box, sphere, cylinder, plane).
18
+ *
19
+ * Platform support:
20
+ * - **Android**: fully rendered.
21
+ * - **iOS**: acknowledged but not yet rendered — the RealityKit bridge does
22
+ * not currently map procedural geometry nodes. Tracked under the
23
+ * cross-platform bridge-parity umbrella (#909). Use `modelNodes` on iOS.
24
+ */
17
25
 
18
- /** A light source in the scene. */
26
+ /**
27
+ * A light source in the scene.
28
+ *
29
+ * Platform support:
30
+ * - **Android**: fully rendered.
31
+ * - **iOS**: acknowledged but not yet rendered — the RealityKit bridge does
32
+ * not currently map declarative light nodes. Tracked under the
33
+ * cross-platform bridge-parity umbrella (#909).
34
+ */
19
35
 
20
36
  // ---------------------------------------------------------------------------
21
37
  // Event payloads
@@ -1 +1 @@
1
- {"version":3,"names":["_react","_interopRequireDefault","require","_reactNative","e","__esModule","default","isNativeAvailable","Platform","OS","NativeSceneView","requireNativeComponent","NativeARSceneView","UnsupportedView","name","createElement","View","style","fallbackStyles","container","Text","text","StyleSheet","create","flex","justifyContent","alignItems","backgroundColor","color","fontSize","SceneView","props","exports","ARSceneView","NativeARRecorder","NativeModules","RNARRecorder","ARRecorder","isSupported","rejectUnsupported","Promise","reject","Error","start","stop","outputPath","saveToPhotoLibrary","movPath"],"sourceRoot":"../../src","sources":["index.tsx"],"mappings":";;;;;;AAAA,IAAAA,MAAA,GAAAC,sBAAA,CAAAC,OAAA;AACA,IAAAC,YAAA,GAAAD,OAAA;AASsB,SAAAD,uBAAAG,CAAA,WAAAA,CAAA,IAAAA,CAAA,CAAAC,UAAA,GAAAD,CAAA,KAAAE,OAAA,EAAAF,CAAA;AAEtB;AACA;AACA;;AAEA;;AAiBA;;AAmBA;;AASA;AACA;AACA;;AAkBA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAoDA;AACA;AACA;;AAEA,MAAMG,iBAAiB,GAAGC,qBAAQ,CAACC,EAAE,KAAK,SAAS,IAAID,qBAAQ,CAACC,EAAE,KAAK,KAAK;AAE5E,MAAMC,eAAe,GAAGH,iBAAiB,GACrC,IAAAI,mCAAsB,EAAiB,aAAa,CAAC,GACrD,IAAI;AAER,MAAMC,iBAAiB,GAAGL,iBAAiB,GACvC,IAAAI,mCAAsB,EAAmB,eAAe,CAAC,GACzD,IAAI;;AAER;AACA;AACA;;AAEA,MAAME,eAA2C,GAAGA,CAAC;EAAEC;AAAK,CAAC,kBAC3Dd,MAAA,CAAAM,OAAA,CAAAS,aAAA,CAACZ,YAAA,CAAAa,IAAI;EAACC,KAAK,EAAEC,cAAc,CAACC;AAAU,gBACpCnB,MAAA,CAAAM,OAAA,CAAAS,aAAA,CAACZ,YAAA,CAAAiB,IAAI;EAACH,KAAK,EAAEC,cAAc,CAACG;AAAK,GAAEP,IAAI,EAAC,oCAAwC,CAC5E,CACP;AAED,MAAMI,cAAc,GAAGI,uBAAU,CAACC,MAAM,CAAC;EACvCJ,SAAS,EAAE;IACTK,IAAI,EAAE,CAAC;IACPC,cAAc,EAAE,QAAQ;IACxBC,UAAU,EAAE,QAAQ;IACpBC,eAAe,EAAE;EACnB,CAAC;EACDN,IAAI,EAAE;IACJO,KAAK,EAAE,MAAM;IACbC,QAAQ,EAAE;EACZ;AACF,CAAC,CAAC;;AAEF;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACO,MAAMC,SAAmC,GAAIC,KAAK,IAAK;EAC5D,IAAI,CAACrB,eAAe,EAAE;IACpB,oBAAOV,MAAA,CAAAM,OAAA,CAAAS,aAAA,CAACF,eAAe;MAACC,IAAI,EAAC;IAAW,CAAE,CAAC;EAC7C;EACA,oBAAOd,MAAA,CAAAM,OAAA,CAAAS,aAAA,CAACL,eAAe,EAAKqB,KAAQ,CAAC;AACvC,CAAC;;AAED;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AATAC,OAAA,CAAAF,SAAA,GAAAA,SAAA;AAUO,MAAMG,WAAuC,GAAIF,KAAK,IAAK;EAChE,IAAI,CAACnB,iBAAiB,EAAE;IACtB,oBAAOZ,MAAA,CAAAM,OAAA,CAAAS,aAAA,CAACF,eAAe;MAACC,IAAI,EAAC;IAAa,CAAE,CAAC;EAC/C;EACA,oBAAOd,MAAA,CAAAM,OAAA,CAAAS,aAAA,CAACH,iBAAiB,EAAKmB,KAAQ,CAAC;AACzC,CAAC;;AAED;AACA;AACA;;AAEA;AAAAC,OAAA,CAAAC,WAAA,GAAAA,WAAA;AAOA,MAAMC,gBAAgD,GACpDC,0BAAa,CAACC,YAAY;;AAE5B;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACO,MAAMC,UAAU,CAAC;EACtB;EACA,WAAWC,WAAWA,CAAA,EAAY;IAChC,OAAO9B,qBAAQ,CAACC,EAAE,KAAK,KAAK,IAAIyB,gBAAgB,IAAI,IAAI;EAC1D;EAEQK,iBAAiBA,CAAA,EAAmB;IAC1C,OAAOC,OAAO,CAACC,MAAM,CACnB,IAAIC,KAAK,CACP,oEAAoE,GAClE,sCACJ,CACF,CAAC;EACH;;EAEA;EACAC,KAAKA,CAAA,EAAkB;IACrB,IAAI,CAACN,UAAU,CAACC,WAAW,IAAI,CAACJ,gBAAgB,EAAE;MAChD,OAAO,IAAI,CAACK,iBAAiB,CAAC,CAAC;IACjC;IACA,OAAOL,gBAAgB,CAACS,KAAK,CAAC,CAAC;EACjC;;EAEA;AACF;AACA;AACA;AACA;AACA;AACA;EACEC,IAAIA,CAACC,UAAmB,EAAmB;IACzC,IAAI,CAACR,UAAU,CAACC,WAAW,IAAI,CAACJ,gBAAgB,EAAE;MAChD,OAAO,IAAI,CAACK,iBAAiB,CAAC,CAAC;IACjC;IACA,OAAOL,gBAAgB,CAACU,IAAI,CAACC,UAAU,IAAI,IAAI,CAAC;EAClD;;EAEA;EACAC,kBAAkBA,CAACC,OAAe,EAAiB;IACjD,IAAI,CAACV,UAAU,CAACC,WAAW,IAAI,CAACJ,gBAAgB,EAAE;MAChD,OAAO,IAAI,CAACK,iBAAiB,CAAC,CAAC;IACjC;IACA,OAAOL,gBAAgB,CAACY,kBAAkB,CAACC,OAAO,CAAC;EACrD;AACF;AAACf,OAAA,CAAAK,UAAA,GAAAA,UAAA","ignoreList":[]}
1
+ {"version":3,"names":["_react","_interopRequireDefault","require","_reactNative","e","__esModule","default","isNativeAvailable","Platform","OS","NativeSceneView","requireNativeComponent","NativeARSceneView","UnsupportedView","name","createElement","View","style","fallbackStyles","container","Text","text","StyleSheet","create","flex","justifyContent","alignItems","backgroundColor","color","fontSize","SceneView","props","exports","ARSceneView","NativeARRecorder","NativeModules","RNARRecorder","ARRecorder","isSupported","rejectUnsupported","Promise","reject","Error","start","stop","outputPath","saveToPhotoLibrary","movPath"],"sourceRoot":"../../src","sources":["index.tsx"],"mappings":"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","ignoreList":[]}
@@ -7,9 +7,25 @@ import { requireNativeComponent, NativeModules, Platform, View, Text, StyleSheet
7
7
 
8
8
  /** A 3D model loaded from a .glb / .gltf file. */
9
9
 
10
- /** A procedural geometry node (box, sphere, cylinder, plane). */
10
+ /**
11
+ * A procedural geometry node (box, sphere, cylinder, plane).
12
+ *
13
+ * Platform support:
14
+ * - **Android**: fully rendered.
15
+ * - **iOS**: acknowledged but not yet rendered — the RealityKit bridge does
16
+ * not currently map procedural geometry nodes. Tracked under the
17
+ * cross-platform bridge-parity umbrella (#909). Use `modelNodes` on iOS.
18
+ */
11
19
 
12
- /** A light source in the scene. */
20
+ /**
21
+ * A light source in the scene.
22
+ *
23
+ * Platform support:
24
+ * - **Android**: fully rendered.
25
+ * - **iOS**: acknowledged but not yet rendered — the RealityKit bridge does
26
+ * not currently map declarative light nodes. Tracked under the
27
+ * cross-platform bridge-parity umbrella (#909).
28
+ */
13
29
 
14
30
  // ---------------------------------------------------------------------------
15
31
  // Event payloads
@@ -1 +1 @@
1
- {"version":3,"names":["React","requireNativeComponent","NativeModules","Platform","View","Text","StyleSheet","isNativeAvailable","OS","NativeSceneView","NativeARSceneView","UnsupportedView","name","createElement","style","fallbackStyles","container","text","create","flex","justifyContent","alignItems","backgroundColor","color","fontSize","SceneView","props","ARSceneView","NativeARRecorder","RNARRecorder","ARRecorder","isSupported","rejectUnsupported","Promise","reject","Error","start","stop","outputPath","saveToPhotoLibrary","movPath"],"sourceRoot":"../../src","sources":["index.tsx"],"mappings":"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","ignoreList":[]}
1
+ {"version":3,"names":["React","requireNativeComponent","NativeModules","Platform","View","Text","StyleSheet","isNativeAvailable","OS","NativeSceneView","NativeARSceneView","UnsupportedView","name","createElement","style","fallbackStyles","container","text","create","flex","justifyContent","alignItems","backgroundColor","color","fontSize","SceneView","props","ARSceneView","NativeARRecorder","RNARRecorder","ARRecorder","isSupported","rejectUnsupported","Promise","reject","Error","start","stop","outputPath","saveToPhotoLibrary","movPath"],"sourceRoot":"../../src","sources":["index.tsx"],"mappings":"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","ignoreList":[]}
@@ -16,7 +16,15 @@ export interface ModelNode {
16
16
  */
17
17
  animation?: string;
18
18
  }
19
- /** A procedural geometry node (box, sphere, cylinder, plane). */
19
+ /**
20
+ * A procedural geometry node (box, sphere, cylinder, plane).
21
+ *
22
+ * Platform support:
23
+ * - **Android**: fully rendered.
24
+ * - **iOS**: acknowledged but not yet rendered — the RealityKit bridge does
25
+ * not currently map procedural geometry nodes. Tracked under the
26
+ * cross-platform bridge-parity umbrella (#909). Use `modelNodes` on iOS.
27
+ */
20
28
  export interface GeometryNode {
21
29
  type: 'box' | 'cube' | 'sphere' | 'cylinder' | 'plane';
22
30
  size?: [number, number, number];
@@ -34,7 +42,15 @@ export interface GeometryNode {
34
42
  */
35
43
  unlit?: boolean;
36
44
  }
37
- /** A light source in the scene. */
45
+ /**
46
+ * A light source in the scene.
47
+ *
48
+ * Platform support:
49
+ * - **Android**: fully rendered.
50
+ * - **iOS**: acknowledged but not yet rendered — the RealityKit bridge does
51
+ * not currently map declarative light nodes. Tracked under the
52
+ * cross-platform bridge-parity umbrella (#909).
53
+ */
38
54
  export interface LightNode {
39
55
  type: 'directional' | 'point' | 'spot';
40
56
  intensity?: number;
@@ -72,9 +88,17 @@ export interface SceneViewProps {
72
88
  environment?: string;
73
89
  /** Model nodes to render in the scene. */
74
90
  modelNodes?: ModelNode[];
75
- /** Geometry nodes to render in the scene. */
91
+ /**
92
+ * Geometry nodes to render in the scene.
93
+ *
94
+ * **iOS:** acknowledged but not yet rendered — see {@link GeometryNode}.
95
+ */
76
96
  geometryNodes?: GeometryNode[];
77
- /** Light nodes in the scene. */
97
+ /**
98
+ * Light nodes in the scene.
99
+ *
100
+ * **iOS:** acknowledged but not yet rendered — see {@link LightNode}.
101
+ */
78
102
  lightNodes?: LightNode[];
79
103
  /** Enable default orbit camera controls. Default: true. */
80
104
  cameraOrbit?: boolean;
@@ -91,17 +115,46 @@ export interface SceneViewProps {
91
115
  * issue #1051.
92
116
  */
93
117
  autoCenterContent?: boolean;
94
- /** Called when the user taps inside the scene. */
118
+ /**
119
+ * Called when the user taps inside the scene.
120
+ *
121
+ * The event payload carries the world-space tap coordinates. On Android the
122
+ * tapped node's `nodeName` is included when the tap hits a node; on iOS AR
123
+ * the tap reports the surface point only, so `nodeName` is absent there.
124
+ */
95
125
  onTap?: (event: NativeSyntheticEvent<TapEvent>) => void;
96
126
  }
97
127
  export interface ARSceneViewProps extends SceneViewProps {
98
128
  /** Enable plane detection. Default: true. */
99
129
  planeDetection?: boolean;
100
- /** Enable depth occlusion (ARCore Depth API / LiDAR). Default: false. */
130
+ /**
131
+ * Enable depth occlusion (ARCore Depth API / LiDAR). Default: false.
132
+ *
133
+ * Platform support:
134
+ * - **Android**: wired to ARCore's `Config.DepthMode.AUTOMATIC` (the flag is
135
+ * ignored on devices that do not support the Depth API).
136
+ * - **iOS**: accepted but not yet wired — SceneViewSwift's `ARSceneView`
137
+ * exposes no scene-understanding occlusion knob. Tracked under #909.
138
+ */
101
139
  depthOcclusion?: boolean;
102
- /** Enable instant placement (approximate hit-test before tracking). Default: false. */
140
+ /**
141
+ * Enable instant placement (approximate hit-test before tracking).
142
+ * Default: false.
143
+ *
144
+ * Platform support:
145
+ * - **Android**: wired to ARCore's `Config.InstantPlacementMode.LOCAL_Y_UP`.
146
+ * - **iOS**: accepted but not yet wired — SceneViewSwift's `ARSceneView`
147
+ * exposes no instant-placement knob. Tracked under #909.
148
+ */
103
149
  instantPlacement?: boolean;
104
- /** Called when a new plane is detected. */
150
+ /**
151
+ * Called when a new plane is detected.
152
+ *
153
+ * Platform support:
154
+ * - **Android**: fires once per newly-tracked ARCore plane.
155
+ * - **iOS**: not yet dispatched — SceneViewSwift's `ARSceneView` exposes no
156
+ * public per-plane-detected callback. Tracked under #909.
157
+ */
105
158
  onPlaneDetected?: (event: NativeSyntheticEvent<PlaneDetectedEvent>) => void;
106
159
  }
107
160
  /**
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/index.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,MAAM,OAAO,CAAC;AAC1B,OAAO,EAIL,KAAK,SAAS,EACd,KAAK,oBAAoB,EAI1B,MAAM,cAAc,CAAC;AAMtB,kDAAkD;AAClD,MAAM,WAAW,SAAS;IACxB,+CAA+C;IAC/C,GAAG,EAAE,MAAM,CAAC;IACZ,0DAA0D;IAC1D,QAAQ,CAAC,EAAE,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,CAAC,CAAC;IACpC,+DAA+D;IAC/D,QAAQ,CAAC,EAAE,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,CAAC,CAAC;IACpC,mEAAmE;IACnE,KAAK,CAAC,EAAE,MAAM,GAAG,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,CAAC,CAAC;IAC1C;;;OAGG;IACH,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAED,iEAAiE;AACjE,MAAM,WAAW,YAAY;IAC3B,IAAI,EAAE,KAAK,GAAG,MAAM,GAAG,QAAQ,GAAG,UAAU,GAAG,OAAO,CAAC;IACvD,IAAI,CAAC,EAAE,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,CAAC,CAAC;IAChC,QAAQ,CAAC,EAAE,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,CAAC,CAAC;IACpC,QAAQ,CAAC,EAAE,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,CAAC,CAAC;IACpC,KAAK,CAAC,EAAE,MAAM,GAAG,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,CAAC,CAAC;IAC1C,wCAAwC;IACxC,KAAK,CAAC,EAAE,MAAM,CAAC;IACf;;;;;;OAMG;IACH,KAAK,CAAC,EAAE,OAAO,CAAC;CACjB;AAED,mCAAmC;AACnC,MAAM,WAAW,SAAS;IACxB,IAAI,EAAE,aAAa,GAAG,OAAO,GAAG,MAAM,CAAC;IACvC,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,QAAQ,CAAC,EAAE,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,CAAC,CAAC;IACpC,SAAS,CAAC,EAAE,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,CAAC,CAAC;CACtC;AAMD,MAAM,WAAW,QAAQ;IACvB,0CAA0C;IAC1C,CAAC,EAAE,MAAM,CAAC;IACV,CAAC,EAAE,MAAM,CAAC;IACV,CAAC,EAAE,MAAM,CAAC;IACV,uCAAuC;IACvC,QAAQ,CAAC,EAAE,MAAM,CAAC;CACnB;AAED,MAAM,WAAW,kBAAkB;IACjC,EAAE,EAAE,MAAM,CAAC;IACX,IAAI,EAAE,YAAY,GAAG,UAAU,CAAC;IAChC,MAAM,EAAE,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,CAAC,CAAC;IACjC,MAAM,EAAE,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;CAC1B;AAMD;;;;;;;;GAQG;AACH,MAAM,MAAM,iBAAiB,GAAG,OAAO,GAAG,KAAK,GAAG,aAAa,CAAC;AAEhE,MAAM,WAAW,cAAc;IAC7B,KAAK,CAAC,EAAE,SAAS,CAAC;IAElB,mEAAmE;IACnE,WAAW,CAAC,EAAE,MAAM,CAAC;IAErB,0CAA0C;IAC1C,UAAU,CAAC,EAAE,SAAS,EAAE,CAAC;IACzB,6CAA6C;IAC7C,aAAa,CAAC,EAAE,YAAY,EAAE,CAAC;IAC/B,gCAAgC;IAChC,UAAU,CAAC,EAAE,SAAS,EAAE,CAAC;IAEzB,2DAA2D;IAC3D,WAAW,CAAC,EAAE,OAAO,CAAC;IAEtB;;;;;OAKG;IACH,iBAAiB,CAAC,EAAE,iBAAiB,CAAC;IAEtC;;;;OAIG;IACH,iBAAiB,CAAC,EAAE,OAAO,CAAC;IAE5B,kDAAkD;IAClD,KAAK,CAAC,EAAE,CAAC,KAAK,EAAE,oBAAoB,CAAC,QAAQ,CAAC,KAAK,IAAI,CAAC;CACzD;AAED,MAAM,WAAW,gBAAiB,SAAQ,cAAc;IACtD,6CAA6C;IAC7C,cAAc,CAAC,EAAE,OAAO,CAAC;IAEzB,yEAAyE;IACzE,cAAc,CAAC,EAAE,OAAO,CAAC;IAEzB,uFAAuF;IACvF,gBAAgB,CAAC,EAAE,OAAO,CAAC;IAE3B,2CAA2C;IAC3C,eAAe,CAAC,EAAE,CAAC,KAAK,EAAE,oBAAoB,CAAC,kBAAkB,CAAC,KAAK,IAAI,CAAC;CAC7E;AA2CD;;;;;;;;;GASG;AACH,eAAO,MAAM,SAAS,EAAE,KAAK,CAAC,EAAE,CAAC,cAAc,CAK9C,CAAC;AAEF;;;;;;;;;GASG;AACH,eAAO,MAAM,WAAW,EAAE,KAAK,CAAC,EAAE,CAAC,gBAAgB,CAKlD,CAAC;AAgBF;;;;;;;;;;;;;;;;;;;;GAoBG;AACH,qBAAa,UAAU;IACrB,2EAA2E;IAC3E,MAAM,KAAK,WAAW,IAAI,OAAO,CAEhC;IAED,OAAO,CAAC,iBAAiB;IASzB,sCAAsC;IACtC,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC;IAOtB;;;;;;OAMG;IACH,IAAI,CAAC,UAAU,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC;IAO1C,wEAAwE;IACxE,kBAAkB,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;CAMnD"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/index.tsx"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@sceneview-sdk/react-native",
3
- "version": "4.14.0",
3
+ "version": "4.15.1",
4
4
  "description": "React Native bindings for SceneView — 3D and AR scenes powered by Filament (Android) and RealityKit (iOS)",
5
5
  "main": "lib/commonjs/index",
6
6
  "module": "lib/module/index",
@@ -11,11 +11,19 @@ Pod::Spec.new do |s|
11
11
  s.authors = "SceneView contributors"
12
12
 
13
13
  s.platforms = { :ios => "17.0" }
14
- s.source = { :git => "https://github.com/sceneview/sceneview.git", :tag => s.version }
14
+ s.source = { :git => "https://github.com/sceneview/sceneview.git", :tag => "v#{s.version}" }
15
15
  s.source_files = "ios/**/*.{swift,m}"
16
16
 
17
17
  s.dependency "React-Core"
18
- s.dependency "SceneViewSwift", "~> 4.9"
18
+
19
+ # `SceneViewSwift` is distributed via Swift Package Manager only — there is
20
+ # no published CocoaPods spec for it — so it CANNOT be declared as a
21
+ # `s.dependency` here (CocoaPods would fail `pod install` resolving it).
22
+ # The host app must add it once via Xcode's SwiftPM integration:
23
+ # File ▸ Add Package Dependencies… ▸ https://github.com/sceneview/SceneViewSwift
24
+ # The module's `ios/*.swift` `import SceneViewSwift` then resolves at the
25
+ # app build, exactly like any RN native module with a SwiftPM dependency.
26
+ # See this module's README "iOS" section.
19
27
 
20
28
  s.swift_version = "5.9"
21
29
  end
package/src/index.tsx CHANGED
@@ -31,7 +31,15 @@ export interface ModelNode {
31
31
  animation?: string;
32
32
  }
33
33
 
34
- /** A procedural geometry node (box, sphere, cylinder, plane). */
34
+ /**
35
+ * A procedural geometry node (box, sphere, cylinder, plane).
36
+ *
37
+ * Platform support:
38
+ * - **Android**: fully rendered.
39
+ * - **iOS**: acknowledged but not yet rendered — the RealityKit bridge does
40
+ * not currently map procedural geometry nodes. Tracked under the
41
+ * cross-platform bridge-parity umbrella (#909). Use `modelNodes` on iOS.
42
+ */
35
43
  export interface GeometryNode {
36
44
  type: 'box' | 'cube' | 'sphere' | 'cylinder' | 'plane';
37
45
  size?: [number, number, number];
@@ -50,7 +58,15 @@ export interface GeometryNode {
50
58
  unlit?: boolean;
51
59
  }
52
60
 
53
- /** A light source in the scene. */
61
+ /**
62
+ * A light source in the scene.
63
+ *
64
+ * Platform support:
65
+ * - **Android**: fully rendered.
66
+ * - **iOS**: acknowledged but not yet rendered — the RealityKit bridge does
67
+ * not currently map declarative light nodes. Tracked under the
68
+ * cross-platform bridge-parity umbrella (#909).
69
+ */
54
70
  export interface LightNode {
55
71
  type: 'directional' | 'point' | 'spot';
56
72
  intensity?: number;
@@ -102,9 +118,17 @@ export interface SceneViewProps {
102
118
 
103
119
  /** Model nodes to render in the scene. */
104
120
  modelNodes?: ModelNode[];
105
- /** Geometry nodes to render in the scene. */
121
+ /**
122
+ * Geometry nodes to render in the scene.
123
+ *
124
+ * **iOS:** acknowledged but not yet rendered — see {@link GeometryNode}.
125
+ */
106
126
  geometryNodes?: GeometryNode[];
107
- /** Light nodes in the scene. */
127
+ /**
128
+ * Light nodes in the scene.
129
+ *
130
+ * **iOS:** acknowledged but not yet rendered — see {@link LightNode}.
131
+ */
108
132
  lightNodes?: LightNode[];
109
133
 
110
134
  /** Enable default orbit camera controls. Default: true. */
@@ -125,7 +149,13 @@ export interface SceneViewProps {
125
149
  */
126
150
  autoCenterContent?: boolean;
127
151
 
128
- /** Called when the user taps inside the scene. */
152
+ /**
153
+ * Called when the user taps inside the scene.
154
+ *
155
+ * The event payload carries the world-space tap coordinates. On Android the
156
+ * tapped node's `nodeName` is included when the tap hits a node; on iOS AR
157
+ * the tap reports the surface point only, so `nodeName` is absent there.
158
+ */
129
159
  onTap?: (event: NativeSyntheticEvent<TapEvent>) => void;
130
160
  }
131
161
 
@@ -133,13 +163,36 @@ export interface ARSceneViewProps extends SceneViewProps {
133
163
  /** Enable plane detection. Default: true. */
134
164
  planeDetection?: boolean;
135
165
 
136
- /** Enable depth occlusion (ARCore Depth API / LiDAR). Default: false. */
166
+ /**
167
+ * Enable depth occlusion (ARCore Depth API / LiDAR). Default: false.
168
+ *
169
+ * Platform support:
170
+ * - **Android**: wired to ARCore's `Config.DepthMode.AUTOMATIC` (the flag is
171
+ * ignored on devices that do not support the Depth API).
172
+ * - **iOS**: accepted but not yet wired — SceneViewSwift's `ARSceneView`
173
+ * exposes no scene-understanding occlusion knob. Tracked under #909.
174
+ */
137
175
  depthOcclusion?: boolean;
138
176
 
139
- /** Enable instant placement (approximate hit-test before tracking). Default: false. */
177
+ /**
178
+ * Enable instant placement (approximate hit-test before tracking).
179
+ * Default: false.
180
+ *
181
+ * Platform support:
182
+ * - **Android**: wired to ARCore's `Config.InstantPlacementMode.LOCAL_Y_UP`.
183
+ * - **iOS**: accepted but not yet wired — SceneViewSwift's `ARSceneView`
184
+ * exposes no instant-placement knob. Tracked under #909.
185
+ */
140
186
  instantPlacement?: boolean;
141
187
 
142
- /** Called when a new plane is detected. */
188
+ /**
189
+ * Called when a new plane is detected.
190
+ *
191
+ * Platform support:
192
+ * - **Android**: fires once per newly-tracked ARCore plane.
193
+ * - **iOS**: not yet dispatched — SceneViewSwift's `ARSceneView` exposes no
194
+ * public per-plane-detected callback. Tracked under #909.
195
+ */
143
196
  onPlaneDetected?: (event: NativeSyntheticEvent<PlaneDetectedEvent>) => void;
144
197
  }
145
198