Skip to content

Commit b1c314e

Browse files
CAMOBAPclaude
andcommitted
fix: correct Expo factory class name, imports, and wire Expo packages into Android sandbox
- SandboxExpoFactory.mm: use correct ObjC class name EXReactNativeFactory and import <Expo/ExpoReactNativeFactory.h> (ObjC header, not Swift-generated) - SandboxAppContextBridge.swift: fix return type to AppContext (Swift name), not EXAppContext (ObjC alias) - ExpoIntegration.kt: replace legacy ModuleRegistryAdapter with ExpoModulesPackage - SandboxReactNativeDelegate.kt: wire Expo packages into sandbox host via reflection-guarded getExpoPackages() so the main source set never directly references the conditionally-compiled ExpoIntegration class - React-Sandbox.podspec: refactor source_files to declarative ternary Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
1 parent 457bc08 commit b1c314e

5 files changed

Lines changed: 25 additions & 39 deletions

File tree

packages/react-native-sandbox/React-Sandbox.podspec

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -20,13 +20,14 @@ Pod::Spec.new do |s|
2020
s.authors = { "Alex Babrykovich" => "aliaksandr.babrykovich@callstack.com" }
2121
s.platforms = { :ios => "12.4" }
2222
s.source = { :git => "https://github.com/callstackincubator/react-native-sandbox.git", :tag => "#{s.version}" }
23-
s.source_files = ["ios/*.{h,m,mm,cpp}", "cxx/**/*.{h,cpp}"]
23+
core_sources = ["ios/*.{h,m,mm,cpp}", "cxx/**/*.{h,cpp}"]
24+
expo_sources = has_expo ? ["ios/Expo/**/*.{h,m,mm,swift}"] : []
25+
s.source_files = core_sources + expo_sources
2426
install_modules_dependencies(s)
2527
s.dependency "fmt"
2628

2729
expo_xcconfig = {}
2830
if has_expo
29-
s.source_files += ["ios/Expo/**/*.{h,m,mm,swift}"]
3031
s.dependency "expo-modules-core"
3132
s.dependency "expo"
3233
expo_xcconfig = { "OTHER_CPLUSPLUSFLAGS" => "$(inherited) -DRNS_HAS_EXPO_MODULES=1" }
Lines changed: 2 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -1,29 +1,8 @@
11
package io.callstack.rnsandbox
22

33
import com.facebook.react.ReactPackage
4-
import expo.modules.adapters.react.ModuleRegistryAdapter
5-
import expo.modules.adapters.react.ReactAdapterPackage
6-
import expo.modules.adapters.react.ReactModuleRegistryProvider
7-
import expo.modules.kotlin.ExpoModulesHelper
4+
import expo.modules.kotlin.ExpoModulesPackage
85

9-
/**
10-
* Wires Expo modules into a sandbox React Native host.
11-
*
12-
* Each sandbox already wraps its ReactApplicationContext with SandboxContextWrapper,
13-
* which overrides getFilesDir(), getCacheDir(), and getSharedPreferences() to return
14-
* origin-namespaced paths. Since Expo's AppContext receives this wrapped context, all
15-
* Expo modules (FileSystem, SecureStore, etc.) automatically operate in scoped
16-
* directories without additional AppContext configuration.
17-
*/
186
internal object ExpoIntegration {
19-
/**
20-
* Returns the ReactPackage instances required to expose Expo modules inside a
21-
* sandbox. Include the result in the sandbox's package list when
22-
* BuildConfig.HAS_EXPO_MODULES is true.
23-
*/
24-
fun createPackages(): List<ReactPackage> {
25-
val registryProvider = ReactModuleRegistryProvider(listOf(ReactAdapterPackage()), null)
26-
val modulesProvider = ExpoModulesHelper.modulesProvider
27-
return listOf(ModuleRegistryAdapter(registryProvider, modulesProvider))
28-
}
7+
fun createPackages(): List<ReactPackage> = listOf(ExpoModulesPackage())
298
}

packages/react-native-sandbox/android/src/main/java/io/callstack/rnsandbox/SandboxReactNativeDelegate.kt

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -186,7 +186,7 @@ class SandboxReactNativeDelegate(
186186
capturedSubstitutionPackages,
187187
capturedOrigin,
188188
),
189-
)
189+
) + getExpoPackages()
190190

191191
val bundleLoader = createBundleLoader(capturedBundleSource) ?: return null
192192

@@ -304,6 +304,19 @@ class SandboxReactNativeDelegate(
304304
}
305305
}
306306

307+
private fun getExpoPackages(): List<ReactPackage> {
308+
if (!BuildConfig.HAS_EXPO_MODULES) return emptyList()
309+
return try {
310+
val clazz = Class.forName("io.callstack.rnsandbox.ExpoIntegration")
311+
val instance = clazz.getDeclaredField("INSTANCE").get(null)
312+
@Suppress("UNCHECKED_CAST")
313+
clazz.getDeclaredMethod("createPackages").invoke(instance) as List<ReactPackage>
314+
} catch (e: Exception) {
315+
Log.w(TAG, "Failed to load Expo packages", e)
316+
emptyList()
317+
}
318+
}
319+
307320
private fun createBundleLoader(bundleSource: String): JSBundleLoader? {
308321
if (bundleSource.isEmpty()) return null
309322
return when {

packages/react-native-sandbox/ios/Expo/SandboxAppContextBridge.swift

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,12 +4,12 @@ import ExpoModulesCore
44
import Foundation
55

66
/// Bridges Swift-only Expo APIs to ObjC so the sandbox can create a scoped
7-
/// EXAppContext per origin without exposing Swift value types across the boundary.
7+
/// AppContext per origin without exposing Swift value types across the boundary.
88
@objc(SandboxAppContextBridge)
99
public class SandboxAppContextBridge: NSObject {
10-
/// Returns an EXAppContext whose document and cache directories are namespaced
10+
/// Returns an AppContext whose document and cache directories are namespaced
1111
/// to the given sandbox origin. Callers own the returned object.
12-
@objc public static func createScopedAppContext(forOrigin origin: String) -> EXAppContext {
12+
@objc public static func createScopedAppContext(forOrigin origin: String) -> AppContext {
1313
let sanitized =
1414
origin
1515
.replacingOccurrences(of: "/", with: "-")

packages/react-native-sandbox/ios/Expo/SandboxExpoFactory.mm

Lines changed: 3 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -2,17 +2,10 @@
22

33
#import "SandboxExpoFactory.h"
44

5-
// ExpoReactNativeFactory is a Swift class in the Expo pod. Its ObjC interface is
6-
// generated during build and available via the Swift compatibility header.
7-
#if __has_include(<Expo/Expo-Swift.h>)
8-
#import <Expo/Expo-Swift.h>
9-
#elif __has_include(<Expo/Swift.h>)
10-
#import <Expo/Swift.h>
11-
#else
12-
#import "Expo-Swift.h"
13-
#endif
5+
// EXReactNativeFactory is the ObjC class name — import its ObjC header directly.
6+
#import <Expo/ExpoReactNativeFactory.h>
147

158
RCTReactNativeFactory *SandboxCreateExpoFactory(id<RCTReactNativeFactoryDelegate> delegate)
169
{
17-
return [[ExpoReactNativeFactory alloc] initWithDelegate:delegate];
10+
return [[EXReactNativeFactory alloc] initWithDelegate:delegate];
1811
}

0 commit comments

Comments
 (0)