expo-modules-core 57.0.0 → 57.0.2

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/CHANGELOG.md CHANGED
@@ -10,6 +10,18 @@
10
10
 
11
11
  ### 💡 Others
12
12
 
13
+ ## 57.0.2 — 2026-07-03
14
+
15
+ ### 🐛 Bug fixes
16
+
17
+ - [iOS] Fix `ExpoModulesProvider` lookup failing for bundle names with non-identifier characters (e.g. dots). ([#46424](https://github.com/expo/expo/pull/46424) by [@shawnthye-guru](https://github.com/shawnthye-guru))
18
+
19
+ ## 57.0.1 — 2026-06-30
20
+
21
+ ### 🐛 Bug fixes
22
+
23
+ - [iOS] Fix a reload deadlock on the new architecture where reloading (from pressing `r` in the iOS simulator) would freeze the app until the watchdog killed it. `reloadAppAsync` now triggers the reload synchronously when already on the main thread instead of always deferring via `DispatchQueue.main.async`. ([#47392](https://github.com/expo/expo/pull/47392) by [@brentvatne](https://github.com/brentvatne))
24
+
13
25
  ## 57.0.0 — 2026-06-25
14
26
 
15
27
  ### 🎉 New features
@@ -21,6 +33,7 @@
21
33
 
22
34
  ### 🐛 Bug fixes
23
35
 
36
+ - [iOS] Pair a native shared object with one JavaScript object per runtime, so a shared object exposed to several runtimes (e.g. the main and UI runtimes, or worklet contexts) keeps a live pairing in each instead of only the one paired last. ([#47238](https://github.com/expo/expo/pull/47238) by [@tsapeta](https://github.com/tsapeta))
24
37
  - [Android][compose] Guard `onLayout` against detached window to prevent `LayoutNode should be attached to an owner` crash. ([#47085](https://github.com/expo/expo/pull/47085) by [@roitium](https://github.com/roitium))
25
38
  - [Android] Fix Jetpack Compose `Host` content disappearing before a react-native-screens pop animation finishes, by deferring composition disposal to window detach while the view is still on-screen for a transition. ([#45914](https://github.com/expo/expo/issues/45914), [#47086](https://github.com/expo/expo/issues/47086) by [@aubrey-wodonga](https://github.com/aubrey-wodonga)) ([#47099](https://github.com/expo/expo/pull/47099) by [@nishan](https://github.com/intergalacticspacehighway))
26
39
  - [iOS] Fix a crash on `Updates.reloadAsync()` where the previous `AppContext` could deallocate before the old runtime finished tearing down, releasing cached JSI objects against a dying runtime on the wrong thread. Its lifetime is now tied to the runtime via native state attached to the `global.expo` object. ([#47051](https://github.com/expo/expo/issues/47051) by [@HaiyiMei](https://github.com/HaiyiMei)) ([#47080](https://github.com/expo/expo/pull/47080), [#47098](https://github.com/expo/expo/pull/47098) by [@tsapeta](https://github.com/tsapeta))
@@ -27,7 +27,7 @@ if (shouldIncludeCompose) {
27
27
  }
28
28
 
29
29
  group = 'host.exp.exponent'
30
- version = '57.0.0'
30
+ version = '57.0.2'
31
31
 
32
32
  def isExpoModulesCoreTests = {
33
33
  Gradle gradle = getGradle()
@@ -94,7 +94,7 @@ android {
94
94
  defaultConfig {
95
95
  consumerProguardFiles 'proguard-rules.pro'
96
96
  versionCode 1
97
- versionName "57.0.0"
97
+ versionName "57.0.2"
98
98
  buildConfigField "String", "EXPO_MODULES_CORE_VERSION", "\"${versionName}\""
99
99
  buildConfigField "boolean", "IS_NEW_ARCHITECTURE_ENABLED", "true"
100
100
 
@@ -722,9 +722,18 @@ public final class AppContext: NSObject, EXAppContextProtocol, @unchecked Sendab
722
722
  public static func modulesProvider(withName providerName: String = "ExpoModulesProvider") -> ModulesProvider {
723
723
  // [0] When ExpoModulesCore is built as separated framework/module,
724
724
  // we should explicitly load main bundle's `ExpoModulesProvider` class.
725
- if let bundleName = Bundle.main.infoDictionary?["CFBundleName"],
726
- let providerClass = NSClassFromString("\(bundleName).\(providerName)") as? ModulesProvider.Type {
727
- return providerClass.init()
725
+ // CFBundleExecutable is tried first: it equals $(PRODUCT_NAME:c99extidentifier) and
726
+ // directly matches the Swift module name. CFBundleName is kept as a fallback for the
727
+ // uncommon case where both values are identical valid identifiers.
728
+ let mainBundleNames = [
729
+ Bundle.main.infoDictionary?["CFBundleExecutable"],
730
+ Bundle.main.infoDictionary?["CFBundleName"]
731
+ ].compactMap { $0 as? String }
732
+
733
+ for providerClassName in moduleProviderClassNames(withName: providerName, bundleNames: mainBundleNames) {
734
+ if let providerClass = NSClassFromString(providerClassName) as? ModulesProvider.Type {
735
+ return providerClass.init()
736
+ }
728
737
  }
729
738
 
730
739
  // [1] Fallback to `ExpoModulesProvider` class from the current module.
@@ -734,9 +743,15 @@ public final class AppContext: NSObject, EXAppContextProtocol, @unchecked Sendab
734
743
 
735
744
  // [2] Fallback to search for `ExpoModulesProvider` in frameworks (brownfield use case)
736
745
  for bundle in Bundle.allFrameworks {
737
- guard let bundleName = bundle.infoDictionary?["CFBundleName"] as? String else { continue }
738
- if let providerClass = NSClassFromString("\(bundleName).\(providerName)") as? ModulesProvider.Type {
739
- return providerClass.init()
746
+ let frameworkBundleNames = [
747
+ bundle.infoDictionary?["CFBundleExecutable"],
748
+ bundle.infoDictionary?["CFBundleName"]
749
+ ].compactMap { $0 as? String }
750
+
751
+ for providerClassName in moduleProviderClassNames(withName: providerName, bundleNames: frameworkBundleNames) {
752
+ if let providerClass = NSClassFromString(providerClassName) as? ModulesProvider.Type {
753
+ return providerClass.init()
754
+ }
740
755
  }
741
756
  }
742
757
 
@@ -744,12 +759,25 @@ public final class AppContext: NSObject, EXAppContextProtocol, @unchecked Sendab
744
759
  return ModulesProvider()
745
760
  }
746
761
 
762
+ internal static func moduleProviderClassNames(withName providerName: String, bundleNames: [String]) -> [String] {
763
+ var seen = Set<String>()
764
+ return bundleNames.compactMap { bundleName in
765
+ let candidate = "\(bundleName).\(providerName)"
766
+ return seen.insert(candidate).inserted ? candidate : nil
767
+ }
768
+ }
769
+
747
770
  public func reloadAppAsync(_ reason: String = "Reload from appContext") {
748
771
  if moduleRegistry.has(moduleWithName: "ExpoGo") {
749
772
  NotificationCenter.default.post(name: NSNotification.Name(rawValue: "EXReloadActiveAppRequest"), object: nil)
750
773
  } else {
751
- DispatchQueue.main.async {
752
- RCTTriggerReloadCommandListeners(reason)
774
+ // Must run on the main thread (see #31789), but synchronously when already there — deferring
775
+ // via `DispatchQueue.main.async` deadlocks the bridgeless reload against TurboModule teardown.
776
+ let trigger = { RCTTriggerReloadCommandListeners(reason) }
777
+ if Thread.isMainThread {
778
+ trigger()
779
+ } else {
780
+ DispatchQueue.main.async(execute: trigger)
753
781
  }
754
782
  }
755
783
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "expo-modules-core",
3
- "version": "57.0.0",
3
+ "version": "57.0.2",
4
4
  "description": "The core of Expo Modules architecture",
5
5
  "main": "src/index.ts",
6
6
  "types": "build/index.d.ts",
@@ -66,7 +66,7 @@
66
66
  "@types/invariant": "^2.2.33",
67
67
  "expo-module-scripts": "56.0.3"
68
68
  },
69
- "gitHead": "e3eb896c5fdcd89e0cded98ff4e35c9db12cc9c0",
69
+ "gitHead": "5104cb89c3938bc9653e0cbad43da3a43a2e77a7",
70
70
  "scripts": {
71
71
  "build": "expo-module build",
72
72
  "clean": "expo-module clean",