expo 55.0.0-canary-20250912-b5ce2a8 → 55.0.0-canary-20250930-9dc59d3

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.
@@ -32,7 +32,7 @@ buildscript {
32
32
  def reactNativeVersion = project.extensions.getByType(ExpoModuleExtension).reactNativeVersion
33
33
 
34
34
  group = 'host.exp.exponent'
35
- version = '55.0.0-canary-20250912-b5ce2a8'
35
+ version = '55.0.0-canary-20250930-9dc59d3'
36
36
 
37
37
  expoModule {
38
38
  // We can't prebuild the module because it depends on the generated files.
@@ -43,7 +43,7 @@ android {
43
43
  namespace "expo.core"
44
44
  defaultConfig {
45
45
  versionCode 1
46
- versionName "55.0.0-canary-20250912-b5ce2a8"
46
+ versionName "55.0.0-canary-20250930-9dc59d3"
47
47
  consumerProguardFiles("proguard-rules.pro")
48
48
  }
49
49
  testOptions {
@@ -65,11 +65,11 @@ dependencies { dependencyHandler ->
65
65
  implementation 'com.facebook.react:react-android'
66
66
 
67
67
  testImplementation 'junit:junit:4.13.2'
68
- testImplementation 'androidx.test:core:1.5.0'
69
- testImplementation "com.google.truth:truth:1.1.2"
70
68
  testImplementation 'io.mockk:mockk:1.13.5'
69
+ testImplementation 'androidx.test:core:1.7.0'
70
+ testImplementation "com.google.truth:truth:1.4.5"
71
71
  testImplementation 'org.jetbrains.kotlinx:kotlinx-coroutines-test:1.10.2'
72
- testImplementation 'org.robolectric:robolectric:4.15.1'
72
+ testImplementation 'org.robolectric:robolectric:4.16'
73
73
 
74
74
  if (useLegacyAutolinking) {
75
75
  // Link expo modules as dependencies of the adapter. It uses `api` configuration so they all will be visible for the app as well.
@@ -64,7 +64,7 @@ class ReactActivityDelegateWrapper(
64
64
  }
65
65
  private val delayLoadAppHandler: DelayLoadAppHandler? by lazy {
66
66
  reactActivityHandlers.asSequence()
67
- .mapNotNull { it.getDelayLoadAppHandler(activity, reactNativeHost) }
67
+ .mapNotNull { it.getDelayLoadAppHandler(activity, reactHost) }
68
68
  .firstOrNull()
69
69
  }
70
70
 
@@ -13,6 +13,7 @@ import com.facebook.react.defaults.DefaultNewArchitectureEntryPoint.fabricEnable
13
13
  import com.facebook.react.defaults.DefaultReactActivityDelegate
14
14
  import com.facebook.react.interfaces.fabric.ReactSurface
15
15
  import com.facebook.react.internal.featureflags.ReactNativeFeatureFlags
16
+ import com.facebook.react.internal.featureflags.ReactNativeFeatureFlagsForTests
16
17
  import com.facebook.soloader.SoLoader
17
18
  import expo.modules.core.interfaces.Package
18
19
  import expo.modules.core.interfaces.ReactActivityHandler
@@ -59,6 +60,7 @@ internal class ReactActivityDelegateWrapperDelayLoadTest {
59
60
  fun setUp() {
60
61
  SoLoader.setInTestMode()
61
62
  mockkObject(ExpoModulesPackage.Companion)
63
+ ReactNativeFeatureFlagsForTests.setUp()
62
64
  mockkStatic(ReactNativeFeatureFlags::class)
63
65
  every { ReactNativeFeatureFlags.enableBridgelessArchitecture() } returns true
64
66
  every { ReactNativeFeatureFlags.enableFabricRenderer() } returns true
@@ -1 +1 @@
1
- {"version":3,"file":"dom-hooks.d.ts","sourceRoot":"","sources":["../../src/dom/dom-hooks.ts"],"names":[],"mappings":"AAAA,OAAO,EAA+C,KAAK,cAAc,EAAE,KAAK,GAAG,EAAE,MAAM,OAAO,CAAC;AAEnG,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,aAAa,CAAC;AAOxD;;;GAGG;AACH,wBAAgB,sBAAsB,CAAC,CAAC,SAAS,oBAAoB,EACnE,GAAG,EAAE,GAAG,CAAC,CAAC,CAAC,EACX,IAAI,EAAE,MAAM,CAAC,EACb,IAAI,CAAC,EAAE,cAAc,QA6BtB"}
1
+ {"version":3,"file":"dom-hooks.d.ts","sourceRoot":"","sources":["../../src/dom/dom-hooks.ts"],"names":[],"mappings":"AAAA,OAAO,EAA+C,KAAK,cAAc,EAAE,KAAK,GAAG,EAAE,MAAM,OAAO,CAAC;AAEnG,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,aAAa,CAAC;AAOxD;;;GAGG;AACH,wBAAgB,sBAAsB,CAAC,CAAC,SAAS,oBAAoB,EACnE,GAAG,EAAE,GAAG,CAAC,CAAC,CAAC,EACX,IAAI,EAAE,MAAM,CAAC,EACb,IAAI,CAAC,EAAE,cAAc,QAgCtB"}
@@ -1 +1 @@
1
- {"version":3,"file":"dom.web.d.ts","sourceRoot":"","sources":["../../src/dom/dom.web.ts"],"names":[],"mappings":"AAAA,cAAc,aAAa,CAAC;AAG5B,eAAO,MAAM,MAAM,SAEwB,CAAC"}
1
+ {"version":3,"file":"dom.web.d.ts","sourceRoot":"","sources":["../../src/dom/dom.web.ts"],"names":[],"mappings":"AAAA,cAAc,aAAa,CAAC;AAG5B,eAAO,MAAM,MAAM,SAE0B,CAAC"}
@@ -1 +1 @@
1
- {"version":3,"file":"marshal.d.ts","sourceRoot":"","sources":["../../src/dom/marshal.tsx"],"names":[],"mappings":"AAAA,OAAO,EAAE,aAAa,EAAE,SAAS,EAAE,MAAM,aAAa,CAAC;AAevD,eAAO,MAAM,gBAAgB,GAAI,KAAK,SAAS,SAAS,EACtD,aAAa,CAAC,OAAO,EAAE,aAAa,CAAC,KAAK,CAAC,KAAK,IAAI,KACnD,CAAC,MAAM,IAAI,CAab,CAAC;AAsCF,wBAAgB,gBAAgB,IAAI,MAAM,CAAC,MAAM,EAAE,CAAC,GAAG,IAAI,EAAE,GAAG,EAAE,KAAK,IAAI,GAAG,OAAO,CAAC,GAAG,CAAC,CAAC,CAuC1F"}
1
+ {"version":3,"file":"marshal.d.ts","sourceRoot":"","sources":["../../src/dom/marshal.tsx"],"names":[],"mappings":"AAAA,OAAO,EAAE,aAAa,EAAE,SAAS,EAAE,MAAM,aAAa,CAAC;AAiBvD,eAAO,MAAM,gBAAgB,GAAI,KAAK,SAAS,SAAS,EACtD,aAAa,CAAC,OAAO,EAAE,aAAa,CAAC,KAAK,CAAC,KAAK,IAAI,KACnD,CAAC,MAAM,IAAI,CAab,CAAC;AAsCF,wBAAgB,gBAAgB,IAAI,MAAM,CAAC,MAAM,EAAE,CAAC,GAAG,IAAI,EAAE,GAAG,EAAE,KAAK,IAAI,GAAG,OAAO,CAAC,GAAG,CAAC,CAAC,CAuC1F"}
@@ -1,98 +1,99 @@
1
1
  {
2
- "@expo/fingerprint": "0.15.1-canary-20250912-b5ce2a8",
3
- "@expo/metro-runtime": "6.1.2-canary-20250912-b5ce2a8",
2
+ "@expo/fingerprint": "0.15.2-canary-20250930-9dc59d3",
3
+ "@expo/metro-runtime": "6.1.3-canary-20250930-9dc59d3",
4
4
  "@expo/vector-icons": "^15.0.2",
5
- "@expo/ui": "0.2.0-canary-20250912-b5ce2a8",
5
+ "@expo/ui": "0.2.0-canary-20250930-9dc59d3",
6
6
  "@react-native-async-storage/async-storage": "2.2.0",
7
7
  "@react-native-community/datetimepicker": "8.4.4",
8
8
  "@react-native-masked-view/masked-view": "0.3.2",
9
9
  "@react-native-community/netinfo": "11.4.1",
10
10
  "@react-native-community/slider": "5.0.1",
11
11
  "@react-native-community/viewpager": "5.0.11",
12
- "@react-native-picker/picker": "2.11.1",
12
+ "@react-native-picker/picker": "2.11.2",
13
13
  "@react-native-segmented-control/segmented-control": "2.5.7",
14
14
  "@stripe/stripe-react-native": "0.50.3",
15
- "eslint-config-expo": "10.0.1-canary-20250912-b5ce2a8",
15
+ "eslint-config-expo": "10.0.1-canary-20250930-9dc59d3",
16
16
  "expo-analytics-amplitude": "~11.3.0",
17
17
  "expo-app-auth": "~11.1.0",
18
18
  "expo-app-loader-provider": "~8.0.0",
19
- "expo-apple-authentication": "8.0.8-canary-20250912-b5ce2a8",
20
- "expo-application": "7.0.8-canary-20250912-b5ce2a8",
21
- "expo-asset": "12.0.9-canary-20250912-b5ce2a8",
22
- "expo-audio": "1.0.12-canary-20250912-b5ce2a8",
23
- "expo-auth-session": "7.0.9-canary-20250912-b5ce2a8",
24
- "expo-av": "16.0.8-canary-20250912-b5ce2a8",
25
- "expo-background-fetch": "14.0.8-canary-20250912-b5ce2a8",
26
- "expo-background-task": "1.0.8-canary-20250912-b5ce2a8",
27
- "expo-battery": "10.0.8-canary-20250912-b5ce2a8",
28
- "expo-blur": "15.0.8-canary-20250912-b5ce2a8",
29
- "expo-brightness": "14.0.8-canary-20250912-b5ce2a8",
30
- "expo-build-properties": "1.0.9-canary-20250912-b5ce2a8",
31
- "expo-calendar": "15.0.8-canary-20250912-b5ce2a8",
32
- "expo-camera": "17.0.8-canary-20250912-b5ce2a8",
33
- "expo-cellular": "8.0.8-canary-20250912-b5ce2a8",
34
- "expo-checkbox": "5.0.8-canary-20250912-b5ce2a8",
35
- "expo-clipboard": "8.0.8-canary-20250912-b5ce2a8",
36
- "expo-constants": "18.0.9-canary-20250912-b5ce2a8",
37
- "expo-contacts": "15.0.9-canary-20250912-b5ce2a8",
38
- "expo-crypto": "15.0.8-canary-20250912-b5ce2a8",
39
- "expo-dev-client": "6.0.13-canary-20250912-b5ce2a8",
40
- "expo-device": "8.0.8-canary-20250912-b5ce2a8",
41
- "expo-document-picker": "14.0.8-canary-20250912-b5ce2a8",
42
- "expo-file-system": "19.0.13-canary-20250912-b5ce2a8",
43
- "expo-font": "14.0.9-canary-20250912-b5ce2a8",
44
- "expo-gl": "16.0.8-canary-20250912-b5ce2a8",
19
+ "expo-apple-authentication": "8.0.8-canary-20250930-9dc59d3",
20
+ "expo-application": "7.0.8-canary-20250930-9dc59d3",
21
+ "expo-asset": "12.0.10-canary-20250930-9dc59d3",
22
+ "expo-audio": "1.0.14-canary-20250930-9dc59d3",
23
+ "expo-auth-session": "7.0.9-canary-20250930-9dc59d3",
24
+ "expo-av": "16.0.8-canary-20250930-9dc59d3",
25
+ "expo-background-fetch": "14.0.8-canary-20250930-9dc59d3",
26
+ "expo-background-task": "1.1.0-canary-20250930-9dc59d3",
27
+ "expo-battery": "10.0.8-canary-20250930-9dc59d3",
28
+ "expo-blur": "15.0.8-canary-20250930-9dc59d3",
29
+ "expo-brightness": "14.0.8-canary-20250930-9dc59d3",
30
+ "expo-build-properties": "1.0.10-canary-20250930-9dc59d3",
31
+ "expo-calendar": "15.0.8-canary-20250930-9dc59d3",
32
+ "expo-camera": "17.0.9-canary-20250930-9dc59d3",
33
+ "expo-cellular": "8.0.8-canary-20250930-9dc59d3",
34
+ "expo-checkbox": "5.0.8-canary-20250930-9dc59d3",
35
+ "expo-clipboard": "8.0.8-canary-20250930-9dc59d3",
36
+ "expo-constants": "18.0.10-canary-20250930-9dc59d3",
37
+ "expo-contacts": "15.0.9-canary-20250930-9dc59d3",
38
+ "expo-crypto": "15.0.8-canary-20250930-9dc59d3",
39
+ "expo-dev-client": "6.0.13-canary-20250930-9dc59d3",
40
+ "expo-device": "8.1.0-canary-20250930-9dc59d3",
41
+ "expo-document-picker": "14.0.8-canary-20250930-9dc59d3",
42
+ "expo-file-system": "19.1.0-canary-20250930-9dc59d3",
43
+ "expo-font": "14.0.9-canary-20250930-9dc59d3",
44
+ "expo-gl": "16.0.8-canary-20250930-9dc59d3",
45
+ "expo-glass-effect": "0.1.5-canary-20250930-9dc59d3",
45
46
  "expo-google-app-auth": "~8.3.0",
46
- "expo-haptics": "15.0.8-canary-20250912-b5ce2a8",
47
- "expo-image": "3.0.9-canary-20250912-b5ce2a8",
48
- "expo-image-loader": "6.0.1-canary-20250912-b5ce2a8",
49
- "expo-image-manipulator": "14.0.8-canary-20250912-b5ce2a8",
50
- "expo-image-picker": "17.0.9-canary-20250912-b5ce2a8",
51
- "expo-intent-launcher": "13.0.8-canary-20250912-b5ce2a8",
52
- "expo-insights": "0.10.8-canary-20250912-b5ce2a8",
53
- "expo-keep-awake": "15.0.8-canary-20250912-b5ce2a8",
54
- "expo-linear-gradient": "15.0.8-canary-20250912-b5ce2a8",
55
- "expo-linking": "8.0.9-canary-20250912-b5ce2a8",
56
- "expo-local-authentication": "17.0.8-canary-20250912-b5ce2a8",
57
- "expo-localization": "17.0.8-canary-20250912-b5ce2a8",
58
- "expo-location": "19.0.8-canary-20250912-b5ce2a8",
59
- "expo-mail-composer": "15.0.8-canary-20250912-b5ce2a8",
60
- "expo-manifests": "1.0.9-canary-20250912-b5ce2a8",
61
- "expo-maps": "0.12.8-canary-20250912-b5ce2a8",
62
- "expo-media-library": "18.1.2-canary-20250912-b5ce2a8",
63
- "expo-mesh-gradient": "0.4.8-canary-20250912-b5ce2a8",
64
- "expo-module-template": "11.0.13-canary-20250912-b5ce2a8",
65
- "expo-modules-core": "3.0.16-canary-20250912-b5ce2a8",
66
- "expo-navigation-bar": "5.0.9-canary-20250912-b5ce2a8",
67
- "expo-network": "8.0.8-canary-20250912-b5ce2a8",
68
- "expo-notifications": "0.32.12-canary-20250912-b5ce2a8",
69
- "expo-print": "15.0.8-canary-20250912-b5ce2a8",
70
- "expo-live-photo": "1.0.8-canary-20250912-b5ce2a8",
71
- "expo-router": "6.0.2-canary-20250912-b5ce2a8",
72
- "expo-screen-capture": "8.0.8-canary-20250912-b5ce2a8",
73
- "expo-screen-orientation": "9.0.8-canary-20250912-b5ce2a8",
74
- "expo-secure-store": "15.0.8-canary-20250912-b5ce2a8",
75
- "expo-sensors": "15.0.8-canary-20250912-b5ce2a8",
76
- "expo-sharing": "14.0.8-canary-20250912-b5ce2a8",
77
- "expo-sms": "14.0.8-canary-20250912-b5ce2a8",
78
- "expo-speech": "14.0.8-canary-20250912-b5ce2a8",
79
- "expo-splash-screen": "31.0.10-canary-20250912-b5ce2a8",
80
- "expo-sqlite": "16.0.9-canary-20250912-b5ce2a8",
81
- "expo-status-bar": "3.0.9-canary-20250912-b5ce2a8",
82
- "expo-store-review": "9.0.8-canary-20250912-b5ce2a8",
83
- "expo-symbols": "1.0.8-canary-20250912-b5ce2a8",
84
- "expo-system-ui": "6.0.8-canary-20250912-b5ce2a8",
85
- "expo-task-manager": "14.0.8-canary-20250912-b5ce2a8",
86
- "expo-tracking-transparency": "6.0.8-canary-20250912-b5ce2a8",
87
- "expo-updates": "29.0.11-canary-20250912-b5ce2a8",
88
- "expo-video-thumbnails": "10.0.8-canary-20250912-b5ce2a8",
89
- "expo-video": "3.0.12-canary-20250912-b5ce2a8",
90
- "expo-web-browser": "15.0.8-canary-20250912-b5ce2a8",
91
- "jest-expo": "55.0.0-canary-20250912-b5ce2a8",
47
+ "expo-haptics": "15.0.8-canary-20250930-9dc59d3",
48
+ "expo-image": "3.1.0-canary-20250930-9dc59d3",
49
+ "expo-image-loader": "6.0.1-canary-20250930-9dc59d3",
50
+ "expo-image-manipulator": "14.0.8-canary-20250930-9dc59d3",
51
+ "expo-image-picker": "17.0.9-canary-20250930-9dc59d3",
52
+ "expo-intent-launcher": "13.0.8-canary-20250930-9dc59d3",
53
+ "expo-insights": "0.10.8-canary-20250930-9dc59d3",
54
+ "expo-keep-awake": "15.0.8-canary-20250930-9dc59d3",
55
+ "expo-linear-gradient": "15.0.8-canary-20250930-9dc59d3",
56
+ "expo-linking": "8.0.9-canary-20250930-9dc59d3",
57
+ "expo-local-authentication": "17.0.8-canary-20250930-9dc59d3",
58
+ "expo-localization": "17.0.8-canary-20250930-9dc59d3",
59
+ "expo-location": "19.0.8-canary-20250930-9dc59d3",
60
+ "expo-mail-composer": "15.0.8-canary-20250930-9dc59d3",
61
+ "expo-manifests": "1.0.9-canary-20250930-9dc59d3",
62
+ "expo-maps": "0.12.8-canary-20250930-9dc59d3",
63
+ "expo-media-library": "18.3.0-canary-20250930-9dc59d3",
64
+ "expo-mesh-gradient": "0.4.8-canary-20250930-9dc59d3",
65
+ "expo-module-template": "11.0.15-canary-20250930-9dc59d3",
66
+ "expo-modules-core": "3.1.0-canary-20250930-9dc59d3",
67
+ "expo-navigation-bar": "5.0.9-canary-20250930-9dc59d3",
68
+ "expo-network": "8.0.8-canary-20250930-9dc59d3",
69
+ "expo-notifications": "1.0.0-canary-20250930-9dc59d3",
70
+ "expo-print": "15.0.8-canary-20250930-9dc59d3",
71
+ "expo-live-photo": "1.0.8-canary-20250930-9dc59d3",
72
+ "expo-router": "6.1.0-canary-20250930-9dc59d3",
73
+ "expo-screen-capture": "8.0.9-canary-20250930-9dc59d3",
74
+ "expo-screen-orientation": "9.0.8-canary-20250930-9dc59d3",
75
+ "expo-secure-store": "15.0.8-canary-20250930-9dc59d3",
76
+ "expo-sensors": "15.0.8-canary-20250930-9dc59d3",
77
+ "expo-sharing": "14.0.8-canary-20250930-9dc59d3",
78
+ "expo-sms": "14.0.8-canary-20250930-9dc59d3",
79
+ "expo-speech": "14.0.8-canary-20250930-9dc59d3",
80
+ "expo-splash-screen": "31.0.11-canary-20250930-9dc59d3",
81
+ "expo-sqlite": "16.0.9-canary-20250930-9dc59d3",
82
+ "expo-status-bar": "3.0.9-canary-20250930-9dc59d3",
83
+ "expo-store-review": "9.0.8-canary-20250930-9dc59d3",
84
+ "expo-symbols": "1.0.8-canary-20250930-9dc59d3",
85
+ "expo-system-ui": "6.0.8-canary-20250930-9dc59d3",
86
+ "expo-task-manager": "14.0.8-canary-20250930-9dc59d3",
87
+ "expo-tracking-transparency": "6.0.8-canary-20250930-9dc59d3",
88
+ "expo-updates": "29.1.0-canary-20250930-9dc59d3",
89
+ "expo-video-thumbnails": "10.0.8-canary-20250930-9dc59d3",
90
+ "expo-video": "3.0.12-canary-20250930-9dc59d3",
91
+ "expo-web-browser": "15.0.8-canary-20250930-9dc59d3",
92
+ "jest-expo": "55.0.0-canary-20250930-9dc59d3",
92
93
  "lottie-react-native": "~7.3.1",
93
- "react": "19.1.0",
94
- "react-dom": "19.1.0",
95
- "react-native": "0.81.4",
94
+ "react": "19.1.1",
95
+ "react-dom": "19.1.1",
96
+ "react-native": "0.82.0-rc.4",
96
97
  "react-native-web": "~0.21.0",
97
98
  "react-native-gesture-handler": "~2.28.0",
98
99
  "react-native-get-random-values": "~1.11.0",
@@ -100,17 +101,17 @@
100
101
  "react-native-maps": "1.20.1",
101
102
  "react-native-pager-view": "6.9.1",
102
103
  "react-native-worklets": "0.5.1",
103
- "react-native-reanimated": "~4.1.0",
104
+ "react-native-reanimated": "~4.1.1",
104
105
  "react-native-screens": "~4.16.0",
105
106
  "react-native-safe-area-context": "~5.6.0",
106
107
  "react-native-svg": "15.12.1",
107
108
  "react-native-view-shot": "4.0.3",
108
109
  "react-native-webview": "13.15.0",
109
110
  "sentry-expo": "~7.0.0",
110
- "unimodules-app-loader": "6.0.8-canary-20250912-b5ce2a8",
111
+ "unimodules-app-loader": "6.0.8-canary-20250930-9dc59d3",
111
112
  "unimodules-image-loader-interface": "~6.1.0",
112
113
  "@shopify/react-native-skia": "2.2.12",
113
114
  "@shopify/flash-list": "2.0.2",
114
- "@sentry/react-native": "~6.20.0",
115
+ "@sentry/react-native": "~7.1.0",
115
116
  "react-native-bootsplash": "^6.3.10"
116
117
  }
@@ -15,7 +15,7 @@ NS_SWIFT_NAME(ExpoReactRootViewFactory)
15
15
  @property (nonatomic, weak, nullable) EXReactDelegate *reactDelegate;
16
16
 
17
17
  /**
18
- Initializer for EXAppDelegateWrapper integration
18
+ Initializer for ExpoReactNativeFactory integration
19
19
  */
20
20
  - (instancetype)initWithReactDelegate:(nullable EXReactDelegate *)reactDelegate
21
21
  configuration:(RCTRootViewFactoryConfiguration *)configuration
@@ -4,68 +4,13 @@ import ExpoModulesCore
4
4
  import ReactAppDependencyProvider
5
5
 
6
6
  /**
7
- Note: you cannot subclass Swift from Objective-C, use EXAppDelegateWrapper with Obj-C app delegates
8
7
  Allows classes extending `ExpoAppDelegateSubscriber` to hook into project's app delegate
9
8
  by forwarding `UIApplicationDelegate` events to the subscribers.
10
9
 
11
10
  Keep functions and markers in sync with https://developer.apple.com/documentation/uikit/uiapplicationdelegate
12
11
  */
13
12
  @objc(EXExpoAppDelegate)
14
- open class ExpoAppDelegate: NSObject, @preconcurrency ReactNativeFactoryProvider, UIApplicationDelegate {
15
- @objc public var factory: RCTReactNativeFactory?
16
- private let defaultModuleName = "main"
17
- private let defaultInitialProps = [AnyHashable: Any]()
18
-
19
- public func bindReactNativeFactory(_ factory: RCTReactNativeFactory) {
20
- self.factory = factory
21
- }
22
-
23
- public func recreateRootView(
24
- withBundleURL: URL?,
25
- moduleName: String?,
26
- initialProps: [AnyHashable: Any]?,
27
- launchOptions: [AnyHashable: Any]?
28
- ) -> UIView {
29
- guard let delegate = self.factory?.delegate,
30
- let rootViewFactory = self.factory?.rootViewFactory else {
31
- fatalError("recreateRootView: Missing factory in ExpoAppDelegate")
32
- }
33
-
34
- if delegate.newArchEnabled() {
35
- // chrfalch: rootViewFactory.reactHost is not available here in swift due to the underlying RCTHost type of the property. (todo: check)
36
- assert(rootViewFactory.value(forKey: "reactHost") == nil, "recreateRootViewWithBundleURL: does not support when react instance is created")
37
- } else {
38
- assert(rootViewFactory.bridge == nil, "recreateRootViewWithBundleURL: does not support when react instance is created")
39
- }
40
-
41
- let configuration = rootViewFactory.value(forKey: "_configuration") as? RCTRootViewFactoryConfiguration
42
-
43
- if let bundleURL = withBundleURL {
44
- configuration?.bundleURLBlock = {
45
- return bundleURL
46
- }
47
- }
48
-
49
- let rootView: UIView
50
- if let factory = rootViewFactory as? ExpoReactRootViewFactory {
51
- // When calling `recreateRootViewWithBundleURL:` from `EXReactRootViewFactory`,
52
- // we don't want to loop the ReactDelegate again. Otherwise, it will be an infinite loop.
53
- rootView = factory.superView(
54
- withModuleName: moduleName ?? defaultModuleName,
55
- initialProperties: initialProps,
56
- launchOptions: launchOptions ?? [:]
57
- )
58
- } else {
59
- rootView = rootViewFactory.view(
60
- withModuleName: moduleName ?? defaultModuleName,
61
- initialProperties: initialProps,
62
- launchOptions: launchOptions
63
- )
64
- }
65
-
66
- return rootView
67
- }
68
-
13
+ open class ExpoAppDelegate: NSObject, UIApplicationDelegate {
69
14
  // MARK: - Initializing the App
70
15
  #if os(iOS) || os(tvOS)
71
16
 
@@ -73,52 +18,23 @@ open class ExpoAppDelegate: NSObject, @preconcurrency ReactNativeFactoryProvider
73
18
  _ application: UIApplication,
74
19
  willFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]? = nil
75
20
  ) -> Bool {
76
- let parsedSubscribers = ExpoAppDelegateSubscriberRepository.subscribers.filter {
77
- $0.responds(to: #selector(application(_:willFinishLaunchingWithOptions:)))
78
- }
79
-
80
- // If we can't find a subscriber that implements `willFinishLaunchingWithOptions`, we will delegate the decision if we can handel the passed URL to
81
- // the `didFinishLaunchingWithOptions` method by returning `true` here.
82
- // You can read more about how iOS handles deep links here: https://developer.apple.com/documentation/uikit/uiapplicationdelegate/1623112-application#discussion
83
- if parsedSubscribers.isEmpty {
84
- return true
85
- }
86
-
87
- return parsedSubscribers.reduce(false) { result, subscriber in
88
- return subscriber.application?(application, willFinishLaunchingWithOptions: launchOptions) ?? false || result
89
- }
21
+ return ExpoAppDelegateSubscriberManager.application(application, willFinishLaunchingWithOptions: launchOptions)
90
22
  }
91
23
 
92
24
  open func application(
93
25
  _ application: UIApplication,
94
26
  didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]? = nil
95
27
  ) -> Bool {
96
- ExpoAppDelegateSubscriberRepository.subscribers.forEach { subscriber in
97
- // Subscriber result is ignored as it doesn't matter if any subscriber handled the incoming URL – we always return `true` anyway.
98
- _ = subscriber.application?(application, didFinishLaunchingWithOptions: launchOptions)
99
- }
100
-
101
- return true
28
+ return ExpoAppDelegateSubscriberManager.application(application, didFinishLaunchingWithOptions: launchOptions)
102
29
  }
103
30
 
104
31
  #elseif os(macOS)
105
32
  open func applicationWillFinishLaunching(_ notification: Notification) {
106
- let parsedSubscribers = ExpoAppDelegateSubscriberRepository.subscribers.filter {
107
- $0.responds(to: #selector(applicationWillFinishLaunching(_:)))
108
- }
109
-
110
- parsedSubscribers.forEach { subscriber in
111
- subscriber.applicationWillFinishLaunching?(notification)
112
- }
33
+ ExpoAppDelegateSubscriberManager.applicationWillFinishLaunching(notification)
113
34
  }
114
35
 
115
36
  open func applicationDidFinishLaunching(_ notification: Notification) {
116
- ExpoAppDelegateSubscriberRepository
117
- .subscribers
118
- .forEach { subscriber in
119
- // Subscriber result is ignored as it doesn't matter if any subscriber handled the incoming URL – we always return `true` anyway.
120
- _ = subscriber.applicationDidFinishLaunching?(notification)
121
- }
37
+ ExpoAppDelegateSubscriberManager.applicationDidFinishLaunching(notification)
122
38
  }
123
39
 
124
40
  // TODO: - Configuring and Discarding Scenes
@@ -130,69 +46,49 @@ open class ExpoAppDelegate: NSObject, @preconcurrency ReactNativeFactoryProvider
130
46
 
131
47
  @objc
132
48
  open func applicationDidBecomeActive(_ application: UIApplication) {
133
- ExpoAppDelegateSubscriberRepository
134
- .subscribers
135
- .forEach { $0.applicationDidBecomeActive?(application) }
49
+ ExpoAppDelegateSubscriberManager.applicationDidBecomeActive(application)
136
50
  }
137
51
 
138
52
  @objc
139
53
  open func applicationWillResignActive(_ application: UIApplication) {
140
- ExpoAppDelegateSubscriberRepository
141
- .subscribers
142
- .forEach { $0.applicationWillResignActive?(application) }
54
+ ExpoAppDelegateSubscriberManager.applicationWillResignActive(application)
143
55
  }
144
56
 
145
57
  @objc
146
58
  open func applicationDidEnterBackground(_ application: UIApplication) {
147
- ExpoAppDelegateSubscriberRepository
148
- .subscribers
149
- .forEach { $0.applicationDidEnterBackground?(application) }
59
+ ExpoAppDelegateSubscriberManager.applicationDidEnterBackground(application)
150
60
  }
151
61
 
152
62
  open func applicationWillEnterForeground(_ application: UIApplication) {
153
- ExpoAppDelegateSubscriberRepository
154
- .subscribers
155
- .forEach { $0.applicationWillEnterForeground?(application) }
63
+ ExpoAppDelegateSubscriberManager.applicationWillEnterForeground(application)
156
64
  }
157
65
 
158
66
  open func applicationWillTerminate(_ application: UIApplication) {
159
- ExpoAppDelegateSubscriberRepository
160
- .subscribers
161
- .forEach { $0.applicationWillTerminate?(application) }
67
+ ExpoAppDelegateSubscriberManager.applicationWillTerminate(application)
162
68
  }
163
69
 
164
70
  #elseif os(macOS)
165
71
  @objc
166
72
  open func applicationDidBecomeActive(_ notification: Notification) {
167
- ExpoAppDelegateSubscriberRepository
168
- .subscribers
169
- .forEach { $0.applicationDidBecomeActive?(notification) }
73
+ ExpoAppDelegateSubscriberManager.applicationDidBecomeActive(notification)
170
74
  }
171
75
 
172
76
  @objc
173
77
  open func applicationWillResignActive(_ notification: Notification) {
174
- ExpoAppDelegateSubscriberRepository
175
- .subscribers
176
- .forEach { $0.applicationWillResignActive?(notification) }
78
+ ExpoAppDelegateSubscriberManager.applicationWillResignActive(notification)
177
79
  }
178
80
 
179
81
  @objc
180
82
  open func applicationDidHide(_ notification: Notification) {
181
- ExpoAppDelegateSubscriberRepository
182
- .subscribers
183
- .forEach { $0.applicationDidHide?(notification) }
83
+ ExpoAppDelegateSubscriberManager.applicationDidHide(notification)
184
84
  }
185
85
 
186
86
  open func applicationWillUnhide(_ notification: Notification) {
187
- ExpoAppDelegateSubscriberRepository
188
- .subscribers
189
- .forEach { $0.applicationWillUnhide?(notification) }
87
+ ExpoAppDelegateSubscriberManager.applicationWillUnhide(notification)
190
88
  }
191
89
 
192
90
  open func applicationWillTerminate(_ notification: Notification) {
193
- ExpoAppDelegateSubscriberRepository
194
- .subscribers
195
- .forEach { $0.applicationWillTerminate?(notification) }
91
+ ExpoAppDelegateSubscriberManager.applicationWillTerminate(notification)
196
92
  }
197
93
  #endif
198
94
 
@@ -208,28 +104,7 @@ open class ExpoAppDelegate: NSObject, @preconcurrency ReactNativeFactoryProvider
208
104
  handleEventsForBackgroundURLSession identifier: String,
209
105
  completionHandler: @escaping () -> Void
210
106
  ) {
211
- let selector = #selector(application(_:handleEventsForBackgroundURLSession:completionHandler:))
212
- let subs = ExpoAppDelegateSubscriberRepository.subscribers.filter { $0.responds(to: selector) }
213
- var subscribersLeft = subs.count
214
- let dispatchQueue = DispatchQueue(label: "expo.application.handleBackgroundEvents")
215
-
216
- let handler = {
217
- dispatchQueue.sync {
218
- subscribersLeft -= 1
219
-
220
- if subscribersLeft == 0 {
221
- completionHandler()
222
- }
223
- }
224
- }
225
-
226
- if subs.isEmpty {
227
- completionHandler()
228
- } else {
229
- subs.forEach {
230
- $0.application?(application, handleEventsForBackgroundURLSession: identifier, completionHandler: handler)
231
- }
232
- }
107
+ ExpoAppDelegateSubscriberManager.application(application, handleEventsForBackgroundURLSession: identifier, completionHandler: completionHandler)
233
108
  }
234
109
 
235
110
  #endif
@@ -237,15 +112,11 @@ open class ExpoAppDelegate: NSObject, @preconcurrency ReactNativeFactoryProvider
237
112
  // MARK: - Handling Remote Notification Registration
238
113
 
239
114
  open func application(_ application: UIApplication, didRegisterForRemoteNotificationsWithDeviceToken deviceToken: Data) {
240
- ExpoAppDelegateSubscriberRepository
241
- .subscribers
242
- .forEach { $0.application?(application, didRegisterForRemoteNotificationsWithDeviceToken: deviceToken) }
115
+ ExpoAppDelegateSubscriberManager.application(application, didRegisterForRemoteNotificationsWithDeviceToken: deviceToken)
243
116
  }
244
117
 
245
118
  open func application(_ application: UIApplication, didFailToRegisterForRemoteNotificationsWithError error: Error) {
246
- ExpoAppDelegateSubscriberRepository
247
- .subscribers
248
- .forEach { $0.application?(application, didFailToRegisterForRemoteNotificationsWithError: error) }
119
+ ExpoAppDelegateSubscriberManager.application(application, didFailToRegisterForRemoteNotificationsWithError: error)
249
120
  }
250
121
 
251
122
  #if os(iOS) || os(tvOS)
@@ -254,42 +125,7 @@ open class ExpoAppDelegate: NSObject, @preconcurrency ReactNativeFactoryProvider
254
125
  didReceiveRemoteNotification userInfo: [AnyHashable: Any],
255
126
  fetchCompletionHandler completionHandler: @escaping (UIBackgroundFetchResult) -> Void
256
127
  ) {
257
- let selector = #selector(application(_:didReceiveRemoteNotification:fetchCompletionHandler:))
258
- let subs = ExpoAppDelegateSubscriberRepository.subscribers.filter { $0.responds(to: selector) }
259
- var subscribersLeft = subs.count
260
- let dispatchQueue = DispatchQueue(label: "expo.application.remoteNotification", qos: .userInteractive)
261
- var failedCount = 0
262
- var newDataCount = 0
263
-
264
- let handler = { (result: UIBackgroundFetchResult) in
265
- dispatchQueue.sync {
266
- if result == .failed {
267
- failedCount += 1
268
- } else if result == .newData {
269
- newDataCount += 1
270
- }
271
-
272
- subscribersLeft -= 1
273
-
274
- if subscribersLeft == 0 {
275
- if newDataCount > 0 {
276
- completionHandler(.newData)
277
- } else if failedCount > 0 {
278
- completionHandler(.failed)
279
- } else {
280
- completionHandler(.noData)
281
- }
282
- }
283
- }
284
- }
285
-
286
- if subs.isEmpty {
287
- completionHandler(.noData)
288
- } else {
289
- subs.forEach { subscriber in
290
- subscriber.application?(application, didReceiveRemoteNotification: userInfo, fetchCompletionHandler: handler)
291
- }
292
- }
128
+ ExpoAppDelegateSubscriberManager.application(application, didReceiveRemoteNotification: userInfo, fetchCompletionHandler: completionHandler)
293
129
  }
294
130
 
295
131
  #elseif os(macOS)
@@ -297,23 +133,14 @@ open class ExpoAppDelegate: NSObject, @preconcurrency ReactNativeFactoryProvider
297
133
  _ application: NSApplication,
298
134
  didReceiveRemoteNotification userInfo: [String: Any]
299
135
  ) {
300
- let selector = #selector(application(_:didReceiveRemoteNotification:))
301
- let subs = ExpoAppDelegateSubscriberRepository.subscribers.filter { $0.responds(to: selector) }
302
-
303
- subs.forEach { subscriber in
304
- subscriber.application?(application, didReceiveRemoteNotification: userInfo)
305
- }
136
+ ExpoAppDelegateSubscriberManager.application(application, didReceiveRemoteNotification: userInfo)
306
137
  }
307
138
  #endif
308
139
 
309
140
  // MARK: - Continuing User Activity and Handling Quick Actions
310
141
 
311
142
  open func application(_ application: UIApplication, willContinueUserActivityWithType userActivityType: String) -> Bool {
312
- return ExpoAppDelegateSubscriberRepository
313
- .subscribers
314
- .reduce(false) { result, subscriber in
315
- return subscriber.application?(application, willContinueUserActivityWithType: userActivityType) ?? false || result
316
- }
143
+ return ExpoAppDelegateSubscriberManager.application(application, willContinueUserActivityWithType: userActivityType)
317
144
  }
318
145
 
319
146
  #if os(iOS) || os(tvOS)
@@ -322,29 +149,7 @@ open class ExpoAppDelegate: NSObject, @preconcurrency ReactNativeFactoryProvider
322
149
  continue userActivity: NSUserActivity,
323
150
  restorationHandler: @escaping ([UIUserActivityRestoring]?) -> Void
324
151
  ) -> Bool {
325
- let selector = #selector(application(_:continue:restorationHandler:))
326
- let subs = ExpoAppDelegateSubscriberRepository.subscribers.filter { $0.responds(to: selector) }
327
- var subscribersLeft = subs.count
328
- let dispatchQueue = DispatchQueue(label: "expo.application.continueUserActivity", qos: .userInteractive)
329
- var allRestorableObjects = [UIUserActivityRestoring]()
330
-
331
- let handler = { (restorableObjects: [UIUserActivityRestoring]?) in
332
- dispatchQueue.sync {
333
- if let restorableObjects = restorableObjects {
334
- allRestorableObjects.append(contentsOf: restorableObjects)
335
- }
336
-
337
- subscribersLeft -= 1
338
-
339
- if subscribersLeft == 0 {
340
- restorationHandler(allRestorableObjects)
341
- }
342
- }
343
- }
344
-
345
- return subs.reduce(false) { result, subscriber in
346
- return subscriber.application?(application, continue: userActivity, restorationHandler: handler) ?? false || result
347
- }
152
+ return ExpoAppDelegateSubscriberManager.application(application, continue: userActivity, restorationHandler: restorationHandler)
348
153
  }
349
154
  #elseif os(macOS)
350
155
  open func application(
@@ -352,44 +157,16 @@ open class ExpoAppDelegate: NSObject, @preconcurrency ReactNativeFactoryProvider
352
157
  continue userActivity: NSUserActivity,
353
158
  restorationHandler: @escaping ([any NSUserActivityRestoring]) -> Void
354
159
  ) -> Bool {
355
- let selector = #selector(application(_:continue:restorationHandler:))
356
- let subs = ExpoAppDelegateSubscriberRepository.subscribers.filter { $0.responds(to: selector) }
357
- var subscribersLeft = subs.count
358
- let dispatchQueue = DispatchQueue(label: "expo.application.continueUserActivity", qos: .userInteractive)
359
- var allRestorableObjects = [NSUserActivityRestoring]()
360
-
361
- let handler = { (restorableObjects: [NSUserActivityRestoring]?) in
362
- dispatchQueue.sync {
363
- if let restorableObjects = restorableObjects {
364
- allRestorableObjects.append(contentsOf: restorableObjects)
365
- }
366
-
367
- subscribersLeft -= 1
368
-
369
- if subscribersLeft == 0 {
370
- restorationHandler(allRestorableObjects)
371
- }
372
- }
373
- }
374
-
375
- return subs.reduce(false) { result, subscriber in
376
- return subscriber.application?(application, continue: userActivity, restorationHandler: handler) ?? false || result
377
- }
160
+ return ExpoAppDelegateSubscriberManager.application(application, continue: userActivity, restorationHandler: restorationHandler)
378
161
  }
379
162
  #endif
380
163
 
381
164
  open func application(_ application: UIApplication, didUpdate userActivity: NSUserActivity) {
382
- return ExpoAppDelegateSubscriberRepository
383
- .subscribers
384
- .forEach { $0.application?(application, didUpdate: userActivity) }
165
+ return ExpoAppDelegateSubscriberManager.application(application, didUpdate: userActivity)
385
166
  }
386
167
 
387
168
  open func application(_ application: UIApplication, didFailToContinueUserActivityWithType userActivityType: String, error: Error) {
388
- return ExpoAppDelegateSubscriberRepository
389
- .subscribers
390
- .forEach {
391
- $0.application?(application, didFailToContinueUserActivityWithType: userActivityType, error: error)
392
- }
169
+ return ExpoAppDelegateSubscriberManager.application(application, didFailToContinueUserActivityWithType: userActivityType, error: error)
393
170
  }
394
171
 
395
172
  #if os(iOS)
@@ -398,30 +175,7 @@ open class ExpoAppDelegate: NSObject, @preconcurrency ReactNativeFactoryProvider
398
175
  performActionFor shortcutItem: UIApplicationShortcutItem,
399
176
  completionHandler: @escaping (Bool) -> Void
400
177
  ) {
401
- let selector = #selector(application(_:performActionFor:completionHandler:))
402
- let subs = ExpoAppDelegateSubscriberRepository.subscribers.filter { $0.responds(to: selector) }
403
- var subscribersLeft = subs.count
404
- var result: Bool = false
405
- let dispatchQueue = DispatchQueue(label: "expo.application.performAction", qos: .userInteractive)
406
-
407
- let handler = { (succeeded: Bool) in
408
- dispatchQueue.sync {
409
- result = result || succeeded
410
- subscribersLeft -= 1
411
-
412
- if subscribersLeft == 0 {
413
- completionHandler(result)
414
- }
415
- }
416
- }
417
-
418
- if subs.isEmpty {
419
- completionHandler(result)
420
- } else {
421
- subs.forEach { subscriber in
422
- subscriber.application?(application, performActionFor: shortcutItem, completionHandler: handler)
423
- }
424
- }
178
+ ExpoAppDelegateSubscriberManager.application(application, performActionFor: shortcutItem, completionHandler: completionHandler)
425
179
  }
426
180
  #endif
427
181
 
@@ -432,42 +186,7 @@ open class ExpoAppDelegate: NSObject, @preconcurrency ReactNativeFactoryProvider
432
186
  _ application: UIApplication,
433
187
  performFetchWithCompletionHandler completionHandler: @escaping (UIBackgroundFetchResult) -> Void
434
188
  ) {
435
- let selector = #selector(application(_:performFetchWithCompletionHandler:))
436
- let subs = ExpoAppDelegateSubscriberRepository.subscribers.filter { $0.responds(to: selector) }
437
- var subscribersLeft = subs.count
438
- let dispatchQueue = DispatchQueue(label: "expo.application.performFetch", qos: .userInteractive)
439
- var failedCount = 0
440
- var newDataCount = 0
441
-
442
- let handler = { (result: UIBackgroundFetchResult) in
443
- dispatchQueue.sync {
444
- if result == .failed {
445
- failedCount += 1
446
- } else if result == .newData {
447
- newDataCount += 1
448
- }
449
-
450
- subscribersLeft -= 1
451
-
452
- if subscribersLeft == 0 {
453
- if newDataCount > 0 {
454
- completionHandler(.newData)
455
- } else if failedCount > 0 {
456
- completionHandler(.failed)
457
- } else {
458
- completionHandler(.noData)
459
- }
460
- }
461
- }
462
- }
463
-
464
- if subs.isEmpty {
465
- completionHandler(.noData)
466
- } else {
467
- subs.forEach { subscriber in
468
- subscriber.application?(application, performFetchWithCompletionHandler: handler)
469
- }
470
- }
189
+ ExpoAppDelegateSubscriberManager.application(application, performFetchWithCompletionHandler: completionHandler)
471
190
  }
472
191
 
473
192
  // TODO: - Interacting With WatchKit
@@ -479,15 +198,11 @@ open class ExpoAppDelegate: NSObject, @preconcurrency ReactNativeFactoryProvider
479
198
  #if os(iOS) || os(tvOS)
480
199
 
481
200
  open func application(_ app: UIApplication, open url: URL, options: [UIApplication.OpenURLOptionsKey: Any] = [:]) -> Bool {
482
- return ExpoAppDelegateSubscriberRepository.subscribers.reduce(false) { result, subscriber in
483
- return subscriber.application?(app, open: url, options: options) ?? false || result
484
- }
201
+ return ExpoAppDelegateSubscriberManager.application(app, open: url, options: options)
485
202
  }
486
203
  #elseif os(macOS)
487
204
  open func application(_ app: NSApplication, open urls: [URL]) {
488
- ExpoAppDelegateSubscriberRepository.subscribers.forEach { subscriber in
489
- subscriber.application?(app, open: urls)
490
- }
205
+ ExpoAppDelegateSubscriberManager.application(app, open: urls)
491
206
  }
492
207
  #endif
493
208
  // TODO: - Disallowing Specified App Extension Types
@@ -504,49 +219,7 @@ open class ExpoAppDelegate: NSObject, @preconcurrency ReactNativeFactoryProvider
504
219
  * a different orientation.
505
220
  */
506
221
  open func application(_ application: UIApplication, supportedInterfaceOrientationsFor window: UIWindow?) -> UIInterfaceOrientationMask {
507
- let deviceOrientationMask = allowedOrientations(for: UIDevice.current.userInterfaceIdiom)
508
- let universalOrientationMask = allowedOrientations(for: .unspecified)
509
- let infoPlistOrientations = deviceOrientationMask.isEmpty ? universalOrientationMask : deviceOrientationMask
510
-
511
- let parsedSubscribers = ExpoAppDelegateSubscriberRepository.subscribers.filter {
512
- $0.responds(to: #selector(application(_:supportedInterfaceOrientationsFor:)))
513
- }
514
-
515
- // We want to create an intersection of all orientations set by subscribers.
516
- let subscribersMask: UIInterfaceOrientationMask = parsedSubscribers.reduce(.all) { result, subscriber in
517
- guard let requestedOrientation = subscriber.application?(application, supportedInterfaceOrientationsFor: window) else {
518
- return result
519
- }
520
- return requestedOrientation.intersection(result)
521
- }
522
- return parsedSubscribers.isEmpty ? infoPlistOrientations : subscribersMask
222
+ return ExpoAppDelegateSubscriberManager.application(application, supportedInterfaceOrientationsFor: window)
523
223
  }
524
224
  #endif
525
225
  }
526
-
527
- #if os(iOS)
528
- private func allowedOrientations(for userInterfaceIdiom: UIUserInterfaceIdiom) -> UIInterfaceOrientationMask {
529
- // For now only iPad-specific orientations are supported
530
- let deviceString = userInterfaceIdiom == .pad ? "~pad" : ""
531
- var mask: UIInterfaceOrientationMask = []
532
- guard let orientations = Bundle.main.infoDictionary?["UISupportedInterfaceOrientations\(deviceString)"] as? [String] else {
533
- return mask
534
- }
535
-
536
- for orientation in orientations {
537
- switch orientation {
538
- case "UIInterfaceOrientationPortrait":
539
- mask.insert(.portrait)
540
- case "UIInterfaceOrientationLandscapeLeft":
541
- mask.insert(.landscapeLeft)
542
- case "UIInterfaceOrientationLandscapeRight":
543
- mask.insert(.landscapeRight)
544
- case "UIInterfaceOrientationPortraitUpsideDown":
545
- mask.insert(.portraitUpsideDown)
546
- default:
547
- break
548
- }
549
- }
550
- return mask
551
- }
552
- #endif // os(iOS)
@@ -2,8 +2,14 @@
2
2
 
3
3
  import React
4
4
 
5
- @MainActor public class ExpoReactNativeFactory: RCTReactNativeFactory {
6
- private let reactDelegate = ExpoReactDelegate(handlers: ExpoAppDelegateSubscriberRepository.reactDelegateHandlers)
5
+ public class ExpoReactNativeFactory: RCTReactNativeFactory, ExpoReactNativeFactoryProtocol {
6
+ private let defaultModuleName = "main"
7
+ private lazy var reactDelegate: ExpoReactDelegate = {
8
+ ExpoReactDelegate(
9
+ handlers: ExpoAppDelegateSubscriberRepository.reactDelegateHandlers,
10
+ reactNativeFactory: self
11
+ )
12
+ }()
7
13
 
8
14
  // TODO: Remove check when react-native-macos 0.81 is released
9
15
  #if !os(macOS)
@@ -21,6 +27,7 @@ import React
21
27
  }
22
28
  #endif
23
29
 
30
+ @MainActor
24
31
  @objc func createRCTRootViewFactory() -> RCTRootViewFactory {
25
32
  // Alan: This is temporary. We need to cast to ExpoReactNativeFactoryDelegate here because currently, if you extend RCTReactNativeFactory
26
33
  // from Swift, customizeRootView will not work on the new arch because the cast to RCTRootView will never
@@ -83,4 +90,49 @@ import React
83
90
  turboModuleManagerDelegate: self
84
91
  )
85
92
  }
93
+
94
+ public func recreateRootView(
95
+ withBundleURL: URL?,
96
+ moduleName: String?,
97
+ initialProps: [AnyHashable: Any]?,
98
+ launchOptions: [AnyHashable: Any]?
99
+ ) -> UIView {
100
+ guard let delegate = self.delegate else {
101
+ fatalError("recreateRootView: Missing RCTReactNativeFactoryDelegate")
102
+ }
103
+
104
+ if RCTIsNewArchEnabled() {
105
+ // chrfalch: rootViewFactory.reactHost is not available here in swift due to the underlying RCTHost type of the property. (todo: check)
106
+ assert(self.rootViewFactory.value(forKey: "reactHost") == nil, "recreateRootViewWithBundleURL: does not support when react instance is created")
107
+ } else {
108
+ assert(self.rootViewFactory.bridge == nil, "recreateRootViewWithBundleURL: does not support when react instance is created")
109
+ }
110
+
111
+ let configuration = self.rootViewFactory.value(forKey: "_configuration") as? RCTRootViewFactoryConfiguration
112
+
113
+ if let bundleURL = withBundleURL {
114
+ configuration?.bundleURLBlock = {
115
+ return bundleURL
116
+ }
117
+ }
118
+
119
+ let rootView: UIView
120
+ if let factory = self.rootViewFactory as? ExpoReactRootViewFactory {
121
+ // When calling `recreateRootViewWithBundleURL:` from `EXReactRootViewFactory`,
122
+ // we don't want to loop the ReactDelegate again. Otherwise, it will be an infinite loop.
123
+ rootView = factory.superView(
124
+ withModuleName: moduleName ?? defaultModuleName,
125
+ initialProperties: initialProps,
126
+ launchOptions: launchOptions ?? [:]
127
+ )
128
+ } else {
129
+ rootView = rootViewFactory.view(
130
+ withModuleName: moduleName ?? defaultModuleName,
131
+ initialProperties: initialProps,
132
+ launchOptions: launchOptions
133
+ )
134
+ }
135
+
136
+ return rootView
137
+ }
86
138
  }
package/ios/Expo.h CHANGED
@@ -1,3 +1,3 @@
1
1
  #import <ExpoModulesCore/ExpoModulesCore.h>
2
2
  #import <Expo/EXAppDefinesLoader.h>
3
- #import <Expo/EXAppDelegateWrapper.h>
3
+ #import <Expo/RCTAppDelegateUmbrella.h>
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "expo",
3
- "version": "55.0.0-canary-20250912-b5ce2a8",
3
+ "version": "55.0.0-canary-20250930-9dc59d3",
4
4
  "description": "The Expo SDK",
5
5
  "main": "src/Expo.ts",
6
6
  "module": "src/Expo.ts",
@@ -75,23 +75,23 @@
75
75
  "homepage": "https://github.com/expo/expo/tree/main/packages/expo",
76
76
  "dependencies": {
77
77
  "@babel/runtime": "^7.20.0",
78
- "@expo/cli": "54.0.3-canary-20250912-b5ce2a8",
79
- "@expo/config": "12.0.9-canary-20250912-b5ce2a8",
80
- "@expo/config-plugins": "54.0.2-canary-20250912-b5ce2a8",
81
- "@expo/devtools": "0.1.8-canary-20250912-b5ce2a8",
82
- "@expo/fingerprint": "0.15.1-canary-20250912-b5ce2a8",
83
- "@expo/metro": "~0.1.1",
84
- "@expo/metro-config": "54.0.3-canary-20250912-b5ce2a8",
78
+ "@expo/cli": "54.1.0-canary-20250930-9dc59d3",
79
+ "@expo/config": "12.0.10-canary-20250930-9dc59d3",
80
+ "@expo/config-plugins": "54.1.0-canary-20250930-9dc59d3",
81
+ "@expo/devtools": "0.1.8-canary-20250930-9dc59d3",
82
+ "@expo/fingerprint": "0.15.2-canary-20250930-9dc59d3",
83
+ "@expo/metro": "~54.0.0",
84
+ "@expo/metro-config": "54.0.6-canary-20250930-9dc59d3",
85
85
  "@expo/vector-icons": "^15.0.2",
86
86
  "@ungap/structured-clone": "^1.3.0",
87
- "babel-preset-expo": "54.0.1-canary-20250912-b5ce2a8",
88
- "expo-asset": "12.0.9-canary-20250912-b5ce2a8",
89
- "expo-constants": "18.0.9-canary-20250912-b5ce2a8",
90
- "expo-file-system": "19.0.13-canary-20250912-b5ce2a8",
91
- "expo-font": "14.0.9-canary-20250912-b5ce2a8",
92
- "expo-keep-awake": "15.0.8-canary-20250912-b5ce2a8",
93
- "expo-modules-autolinking": "3.0.11-canary-20250912-b5ce2a8",
94
- "expo-modules-core": "3.0.16-canary-20250912-b5ce2a8",
87
+ "babel-preset-expo": "54.0.4-canary-20250930-9dc59d3",
88
+ "expo-asset": "12.0.10-canary-20250930-9dc59d3",
89
+ "expo-constants": "18.0.10-canary-20250930-9dc59d3",
90
+ "expo-file-system": "19.1.0-canary-20250930-9dc59d3",
91
+ "expo-font": "14.0.9-canary-20250930-9dc59d3",
92
+ "expo-keep-awake": "15.0.8-canary-20250930-9dc59d3",
93
+ "expo-modules-autolinking": "3.1.0-canary-20250930-9dc59d3",
94
+ "expo-modules-core": "3.1.0-canary-20250930-9dc59d3",
95
95
  "pretty-format": "^29.7.0",
96
96
  "react-refresh": "^0.14.2",
97
97
  "whatwg-url-without-unicode": "8.0.0-3"
@@ -100,15 +100,15 @@
100
100
  "@types/node": "^22.14.0",
101
101
  "@types/react": "~19.1.10",
102
102
  "@types/react-test-renderer": "~19.1.0",
103
- "expo-module-scripts": "5.0.8-canary-20250912-b5ce2a8",
104
- "react": "19.1.0",
105
- "react-dom": "19.1.0",
106
- "react-native": "0.81.4",
103
+ "expo-module-scripts": "5.0.8-canary-20250930-9dc59d3",
104
+ "react": "19.1.1",
105
+ "react-dom": "19.1.1",
106
+ "react-native": "0.82.0-rc.4",
107
107
  "web-streams-polyfill": "^3.3.2"
108
108
  },
109
109
  "peerDependencies": {
110
- "@expo/dom-webview": "0.2.8-canary-20250912-b5ce2a8",
111
- "@expo/metro-runtime": "6.1.2-canary-20250912-b5ce2a8",
110
+ "@expo/dom-webview": "0.2.8-canary-20250930-9dc59d3",
111
+ "@expo/metro-runtime": "6.1.3-canary-20250930-9dc59d3",
112
112
  "react": "*",
113
113
  "react-native": "*",
114
114
  "react-native-webview": "*"
@@ -19,7 +19,7 @@ export function buildUrlForBundle(bundlePath: string): string {
19
19
  if (
20
20
  typeof window !== 'undefined' &&
21
21
  // @ts-expect-error
22
- typeof window.ReactNativeWebView !== 'undefined'
22
+ typeof window.$$EXPO_INITIAL_PROPS !== 'undefined'
23
23
  ) {
24
24
  // In a webview, you cannot read from an absolute path.
25
25
  return bundlePath;
@@ -3,7 +3,7 @@ import HMRClient from './hmr';
3
3
  if (
4
4
  typeof window !== 'undefined' &&
5
5
  // @ts-expect-error: Added via react-native-webview
6
- typeof window.ReactNativeWebView !== 'undefined'
6
+ typeof window.$$EXPO_INITIAL_PROPS !== 'undefined'
7
7
  ) {
8
8
  // Sets up developer tools for web platforms when running in a webview. This ensures that logs are visible in the terminal.
9
9
  // We assume full control over the console and send JavaScript logs to Metro.
@@ -16,8 +16,11 @@ export function useDOMImperativeHandle<T extends DOMImperativeFactory>(
16
16
  init: () => T,
17
17
  deps?: DependencyList
18
18
  ) {
19
- // @ts-expect-error: Added via react-native-webview
20
- const isTargetWeb = typeof window.ReactNativeWebView === 'undefined';
19
+ const isTargetWeb =
20
+ // @ts-expect-error: Added via react-native-webview
21
+ typeof window.ReactNativeWebView === 'undefined' &&
22
+ // @ts-expect-error: Added via expo/dom
23
+ typeof window.$$EXPO_INITIAL_PROPS === 'undefined';
21
24
 
22
25
  const stubHandlerFactory = useCallback(() => ({}) as T, deps ?? []);
23
26
 
@@ -3,4 +3,4 @@ export * from './dom-hooks';
3
3
  // TODO: Maybe this could be a bundler global instead.
4
4
  export const IS_DOM =
5
5
  // @ts-expect-error: Added via react-native-webview
6
- typeof ReactNativeWebView !== 'undefined';
6
+ typeof $$EXPO_INITIAL_PROPS !== 'undefined';
@@ -3,6 +3,8 @@ import { DOM_EVENT, NATIVE_ACTION, NATIVE_ACTION_RESULT } from './injection';
3
3
 
4
4
  const IS_DOM =
5
5
  typeof window !== 'undefined' &&
6
+ // @ts-expect-error: Added via expo/dom
7
+ typeof window.$$EXPO_INITIAL_PROPS !== 'undefined' &&
6
8
  // @ts-expect-error: Added via react-native-webview
7
9
  typeof window.ReactNativeWebView !== 'undefined';
8
10
 
package/template.tgz CHANGED
Binary file
@@ -17,5 +17,5 @@
17
17
  "target": "ESNext"
18
18
  },
19
19
 
20
- "exclude": ["node_modules", "babel.config.js", "metro.config.js", "jest.config.js"]
20
+ "exclude": ["${configDir}/node_modules", "${configDir}/babel.config.js", "${configDir}/metro.config.js", "${configDir}/jest.config.js", "${configDir}/android", "${configDir}/ios"]
21
21
  }
@@ -45,16 +45,10 @@ declare module 'react-native' {
45
45
  /** @platform web */
46
46
  backgroundSize?: string;
47
47
  /** @platform web */
48
- boxShadow?: string;
49
- /** @platform web */
50
48
  boxSizing?: string;
51
49
  /** @platform web */
52
50
  clip?: string;
53
51
  /** @platform web */
54
- cursor?: string;
55
- /** @platform web */
56
- filter?: string;
57
- /** @platform web */
58
52
  gridAutoColumns?: string;
59
53
  /** @platform web */
60
54
  gridAutoFlow?: string;
@@ -81,8 +75,6 @@ declare module 'react-native' {
81
75
  /** @platform web */
82
76
  outline?: string;
83
77
  /** @platform web */
84
- outlineColor?: string;
85
- /** @platform web */
86
78
  overflowX?: string;
87
79
  /** @platform web */
88
80
  overflowY?: string;
@@ -226,8 +218,6 @@ declare module 'react-native' {
226
218
  /** @platform web */
227
219
  transitionTimingFunction?: string | Function | (string | Function)[];
228
220
  /** @platform web */
229
- userSelect?: string;
230
- /** @platform web */
231
221
  visibility?: string;
232
222
  /** @platform web */
233
223
  willChange?: string;
@@ -1,30 +0,0 @@
1
- // Copyright 2018-present 650 Industries. All rights reserved.
2
-
3
- #import <ExpoModulesCore/Platform.h>
4
- #import <ExpoModulesCore/EXReactDelegateWrapper.h>
5
- #import <Expo/RCTAppDelegateUmbrella.h>
6
-
7
- NS_ASSUME_NONNULL_BEGIN
8
-
9
- @interface RCTAppDelegate ()
10
-
11
- @end
12
-
13
- __deprecated_msg("EXAppDelegateWrapper is deprecated. Migrate your AppDelegate to Swift and use ExpoAppDelegate instead. EXAppDelegateWrapper will be removed in SDK 55.")
14
- @interface EXAppDelegateWrapper : NSObject <UIApplicationDelegate, UISceneDelegate, RCTReactNativeFactoryDelegate>
15
-
16
- @property (nonatomic, strong, readonly) EXReactDelegateWrapper *reactDelegate;
17
-
18
- @property (nonatomic, strong, nullable) NSString *moduleName;
19
- @property (nonatomic, strong, nullable) NSDictionary *initialProps;
20
-
21
- /**
22
- Currently (RN 0.76) `customizeRootView` signature in `RCTAppDelegate` is broken as it uses `RCTRootView` type,
23
- but this type is no longer used. It should rather be `RCTSurfaceHostingView`, but for simplicity it could be just `UIView`.
24
- We need a helper function in Objective-C to actually make it to work, otherwise the types will conflict in Swift.
25
- */
26
- + (void)customizeRootView:(nonnull UIView *)rootView byAppDelegate:(nonnull RCTAppDelegate *)appDelegate;
27
-
28
- @end
29
-
30
- NS_ASSUME_NONNULL_END
@@ -1,112 +0,0 @@
1
- // Copyright 2018-present 650 Industries. All rights reserved.
2
-
3
- #import <Expo/EXAppDelegateWrapper.h>
4
-
5
- #import <Expo/RCTAppDelegateUmbrella.h>
6
- #import <Expo/Swift.h>
7
-
8
- #import <React/RCTComponentViewFactory.h> // Allows non-umbrella since it's coming from React-RCTFabric
9
- #import <ReactCommon/RCTHost.h> // Allows non-umbrella because the header is not inside a clang module
10
-
11
- @implementation EXAppDelegateWrapper {
12
- EXExpoAppDelegate *_expoAppDelegate;
13
- }
14
-
15
- - (instancetype)init
16
- {
17
- if (self = [super init]) {
18
- _expoAppDelegate = [EXExpoAppDelegate new];
19
- }
20
- return self;
21
- }
22
-
23
- // This needs to be implemented, otherwise forwarding won't be called.
24
- // When the app starts, `UIApplication` uses it to check beforehand
25
- // which `UIApplicationDelegate` selectors are implemented.
26
- - (BOOL)respondsToSelector:(SEL)selector
27
- {
28
- return [super respondsToSelector:selector] || [_expoAppDelegate respondsToSelector:selector];
29
- }
30
-
31
- // Forwards all invocations to `ExpoAppDelegate` object.
32
- - (id)forwardingTargetForSelector:(SEL)selector
33
- {
34
- return _expoAppDelegate;
35
- }
36
-
37
- #pragma mark - UIApplicationDelegate
38
-
39
- // Make sure to override all necessary methods from `RCTAppDelegate` here, explicitly forwarding everything to `_expoAppDelegate`.
40
- // `forwardingTargetForSelector` works only for methods that are not specified in this and `RCTAppDelegate` classes.
41
-
42
- #if !TARGET_OS_OSX
43
- - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
44
- {
45
- return [_expoAppDelegate application:application didFinishLaunchingWithOptions:launchOptions];
46
- }
47
-
48
- - (void)applicationDidBecomeActive:(UIApplication *)application
49
- {
50
- return [_expoAppDelegate applicationDidBecomeActive:application];
51
- }
52
- #else
53
- - (void)applicationDidFinishLaunching:(NSNotification *)notification
54
- {
55
- return [_expoAppDelegate applicationDidFinishLaunching:notification];
56
- }
57
-
58
- - (void)applicationDidBecomeActive:(NSNotification *)notification
59
- {
60
- return [_expoAppDelegate applicationDidBecomeActive:notification];
61
- }
62
- #endif
63
-
64
- - (UIViewController *)createRootViewController
65
- {
66
- return [_expoAppDelegate.factory.delegate createRootViewController];
67
- }
68
-
69
- - (void)customizeRootView:(UIView *)rootView
70
- {
71
- [_expoAppDelegate.factory.delegate customizeRootView:rootView];
72
- }
73
-
74
- #pragma mark - RCTComponentViewFactoryComponentProvider
75
-
76
- - (NSDictionary<NSString *, Class<RCTComponentViewProtocol>> *)thirdPartyFabricComponents
77
- {
78
- return self.dependencyProvider.thirdPartyFabricComponents;
79
- }
80
-
81
- #pragma mark - RCTHostDelegate
82
-
83
- - (void)hostDidStart:(RCTHost *)host
84
- {
85
- }
86
-
87
- - (void)host:(RCTHost *)host
88
- didReceiveJSErrorStack:(NSArray<NSDictionary<NSString *, id> *> *)stack
89
- message:(NSString *)message
90
- exceptionId:(NSUInteger)exceptionId
91
- isFatal:(BOOL)isFatal
92
- {
93
- }
94
-
95
- - (id<RCTTurboModule>)getModuleInstanceFromClass:(Class)moduleClass
96
- {
97
- return [_expoAppDelegate.factory.delegate getModuleInstanceFromClass:moduleClass];
98
- }
99
-
100
- - (Class)getModuleClassFromName:(const char *)name {
101
- return [_expoAppDelegate.factory.delegate getModuleClassFromName:name];
102
- }
103
-
104
-
105
- #pragma mark - Helpers
106
-
107
- + (void)customizeRootView:(nonnull UIView *)rootView byAppDelegate:(nonnull RCTAppDelegate *)appDelegate
108
- {
109
- [appDelegate customizeRootView:(RCTRootView *)rootView];
110
- }
111
-
112
- @end