expo 58.0.0-preview.1 → 58.0.0-preview.3
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/android/build.gradle +2 -2
- package/android/src/main/java/expo/modules/ExpoModulesV2ModuleList.kt +15 -0
- package/bundledNativeModules.json +33 -33
- package/ios/Expo/ExpoAppSceneDelegate.swift +10 -2
- package/ios/Expo/SceneEventForwarder.swift +16 -4
- package/ios/Tests/ExpoAppSceneDelegateTests.swift +45 -0
- package/package.json +20 -17
- package/react-native.config.js +7 -0
- package/scripts/spm/README.md +118 -0
- package/scripts/spm/__tests__/classify.test.js +211 -0
- package/scripts/spm/__tests__/diagnostics.test.js +639 -0
- package/scripts/spm/__tests__/flavored-frameworks.test.js +191 -0
- package/scripts/spm/__tests__/manifests.test.js +986 -0
- package/scripts/spm/__tests__/plugin.test.js +233 -0
- package/scripts/spm/__tests__/podspec.test.js +378 -0
- package/scripts/spm/__tests__/react-descriptor.test.js +68 -0
- package/scripts/spm/classify.js +161 -0
- package/scripts/spm/cli.js +82 -0
- package/scripts/spm/diagnostics.js +408 -0
- package/scripts/spm/flavored-frameworks.js +290 -0
- package/scripts/spm/jest.config.js +12 -0
- package/scripts/spm/manifests.js +619 -0
- package/scripts/spm/plugin.js +315 -0
- package/scripts/spm/podspec.js +342 -0
- package/scripts/spm/react-descriptor.js +42 -0
- package/template.tgz +0 -0
- package/android/.gitignore +0 -262
package/android/build.gradle
CHANGED
|
@@ -10,7 +10,7 @@ buildscript {
|
|
|
10
10
|
}
|
|
11
11
|
|
|
12
12
|
group = 'host.exp.exponent'
|
|
13
|
-
version = '58.0.0-preview.
|
|
13
|
+
version = '58.0.0-preview.3'
|
|
14
14
|
|
|
15
15
|
expoModule {
|
|
16
16
|
// We can't prebuild the module because it depends on the generated files.
|
|
@@ -21,7 +21,7 @@ android {
|
|
|
21
21
|
namespace "expo.core"
|
|
22
22
|
defaultConfig {
|
|
23
23
|
versionCode 1
|
|
24
|
-
versionName "58.0.0-preview.
|
|
24
|
+
versionName "58.0.0-preview.3"
|
|
25
25
|
consumerProguardFiles("proguard-rules.pro")
|
|
26
26
|
}
|
|
27
27
|
testOptions {
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
package expo.modules
|
|
2
|
+
|
|
3
|
+
import expo.modules.v2.ExpoModulesV2Provider
|
|
4
|
+
import io.github.expo.modules.v2.Module
|
|
5
|
+
import io.github.expo.modules.v2.discoveredExpoModules
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* The list is filled in at compile time: the Expo Modules v2 compiler plugin, which autolinking
|
|
9
|
+
* applies to this project, expands `discoveredExpoModules()` into every `@ExpoModule` on the compile
|
|
10
|
+
* classpath. Autolinking adds every module project as a dependency of this one, so the classpath is
|
|
11
|
+
* exactly the set of installed modules and nothing has to be listed anywhere.
|
|
12
|
+
*/
|
|
13
|
+
class ExpoModulesV2ModuleList : ExpoModulesV2Provider {
|
|
14
|
+
override fun getModules(): List<Class<out Module>> = discoveredExpoModules()
|
|
15
|
+
}
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
{
|
|
2
2
|
"@expo/dom-webview": "~58.0.0",
|
|
3
|
-
"@expo/fingerprint": "~0.21.
|
|
4
|
-
"@expo/metro-runtime": "~58.0.
|
|
3
|
+
"@expo/fingerprint": "~0.21.1",
|
|
4
|
+
"@expo/metro-runtime": "~58.0.3",
|
|
5
5
|
"@expo/vector-icons": "^15.0.2",
|
|
6
|
-
"@expo/ui": "~58.0.
|
|
6
|
+
"@expo/ui": "~58.0.3",
|
|
7
7
|
"@react-native-async-storage/async-storage": "2.2.0",
|
|
8
8
|
"@react-native-community/datetimepicker": "9.2.1",
|
|
9
9
|
"@react-native-masked-view/masked-view": "0.3.2",
|
|
@@ -16,88 +16,88 @@
|
|
|
16
16
|
"eslint-config-expo": "~58.0.1",
|
|
17
17
|
"expo-analytics-amplitude": "~11.3.0",
|
|
18
18
|
"expo-app-auth": "~11.1.0",
|
|
19
|
-
"expo-app-intents": "~0.
|
|
19
|
+
"expo-app-intents": "~0.4.1",
|
|
20
20
|
"expo-app-loader-provider": "~8.0.0",
|
|
21
|
-
"expo-app-metrics": "~58.0.
|
|
21
|
+
"expo-app-metrics": "~58.0.3",
|
|
22
22
|
"expo-apple-authentication": "~58.0.0",
|
|
23
23
|
"expo-application": "~58.0.1",
|
|
24
|
-
"expo-asset": "~58.0.
|
|
24
|
+
"expo-asset": "~58.0.3",
|
|
25
25
|
"expo-audio": "~58.0.0",
|
|
26
|
-
"expo-auth-session": "~58.0.
|
|
27
|
-
"expo-background-fetch": "~58.0.
|
|
28
|
-
"expo-background-task": "~58.0.
|
|
26
|
+
"expo-auth-session": "~58.0.2",
|
|
27
|
+
"expo-background-fetch": "~58.0.3",
|
|
28
|
+
"expo-background-task": "~58.0.3",
|
|
29
29
|
"expo-battery": "~58.0.0",
|
|
30
30
|
"expo-blur": "~58.0.0",
|
|
31
31
|
"expo-brightness": "~58.0.0",
|
|
32
|
-
"expo-brownfield": "~58.0.
|
|
33
|
-
"expo-build-properties": "~58.0.
|
|
34
|
-
"expo-calendar": "~58.0.
|
|
32
|
+
"expo-brownfield": "~58.0.3",
|
|
33
|
+
"expo-build-properties": "~58.0.3",
|
|
34
|
+
"expo-calendar": "~58.0.2",
|
|
35
35
|
"expo-camera": "~58.0.1",
|
|
36
36
|
"expo-cellular": "~58.0.0",
|
|
37
37
|
"expo-checkbox": "~58.0.0",
|
|
38
38
|
"expo-clipboard": "~58.0.0",
|
|
39
|
-
"expo-constants": "~58.0.
|
|
39
|
+
"expo-constants": "~58.0.3",
|
|
40
40
|
"expo-contacts": "~58.0.1",
|
|
41
41
|
"expo-crypto": "~58.0.1",
|
|
42
|
-
"expo-dev-client": "~58.0.
|
|
42
|
+
"expo-dev-client": "~58.0.3",
|
|
43
43
|
"expo-device": "~58.0.0",
|
|
44
44
|
"expo-document-picker": "~58.0.0",
|
|
45
45
|
"expo-eas-client": "~58.0.0",
|
|
46
46
|
"expo-file-system": "~58.0.0",
|
|
47
|
-
"expo-font": "~58.0.
|
|
47
|
+
"expo-font": "~58.0.1",
|
|
48
48
|
"expo-gl": "~58.0.0",
|
|
49
49
|
"expo-glass-effect": "~58.0.0",
|
|
50
50
|
"expo-google-app-auth": "~8.3.0",
|
|
51
51
|
"expo-haptics": "~58.0.1",
|
|
52
|
-
"expo-image": "~58.0.
|
|
52
|
+
"expo-image": "~58.0.2",
|
|
53
53
|
"expo-image-loader": "~58.0.1",
|
|
54
|
-
"expo-image-manipulator": "~58.0.
|
|
55
|
-
"expo-image-picker": "~58.0.
|
|
54
|
+
"expo-image-manipulator": "~58.0.4",
|
|
55
|
+
"expo-image-picker": "~58.0.3",
|
|
56
56
|
"expo-intent-launcher": "~58.0.0",
|
|
57
|
-
"expo-insights": "~58.0.
|
|
57
|
+
"expo-insights": "~58.0.3",
|
|
58
58
|
"expo-keep-awake": "~58.0.0",
|
|
59
59
|
"expo-linear-gradient": "~58.0.0",
|
|
60
|
-
"expo-linking": "~58.0.
|
|
60
|
+
"expo-linking": "~58.0.3",
|
|
61
61
|
"expo-local-authentication": "~58.0.0",
|
|
62
62
|
"expo-localization": "~58.0.0",
|
|
63
|
-
"expo-location": "~58.0.
|
|
63
|
+
"expo-location": "~58.0.3",
|
|
64
64
|
"expo-mail-composer": "~58.0.0",
|
|
65
65
|
"expo-manifests": "~58.0.0",
|
|
66
66
|
"expo-maps": "~58.0.1",
|
|
67
67
|
"expo-mcp": "~0.2.1",
|
|
68
68
|
"expo-media-library": "~58.0.0",
|
|
69
69
|
"expo-mesh-gradient": "~58.0.0",
|
|
70
|
-
"expo-module-template": "~58.0.
|
|
71
|
-
"expo-modules-core": "~58.0.
|
|
70
|
+
"expo-module-template": "~58.0.2",
|
|
71
|
+
"expo-modules-core": "~58.0.3",
|
|
72
72
|
"expo-navigation-bar": "~58.0.0",
|
|
73
73
|
"expo-network": "~58.0.0",
|
|
74
|
-
"expo-notifications": "~58.0.
|
|
75
|
-
"expo-observe": "~58.0.
|
|
74
|
+
"expo-notifications": "~58.0.3",
|
|
75
|
+
"expo-observe": "~58.0.5",
|
|
76
76
|
"expo-print": "~58.0.0",
|
|
77
77
|
"expo-live-photo": "~58.0.0",
|
|
78
|
-
"expo-router": "~58.0.
|
|
78
|
+
"expo-router": "~58.0.4",
|
|
79
79
|
"expo-screen-capture": "~58.0.0",
|
|
80
80
|
"expo-screen-orientation": "~58.0.0",
|
|
81
81
|
"expo-secure-store": "~58.0.0",
|
|
82
82
|
"expo-sensors": "~58.0.0",
|
|
83
83
|
"expo-server": "~58.0.0",
|
|
84
|
-
"expo-sharing": "~58.0.
|
|
84
|
+
"expo-sharing": "~58.0.5",
|
|
85
85
|
"expo-sms": "~58.0.0",
|
|
86
86
|
"expo-speech": "~58.0.0",
|
|
87
87
|
"expo-splash-screen": "~58.0.0",
|
|
88
|
-
"expo-sqlite": "~58.0.
|
|
88
|
+
"expo-sqlite": "~58.0.3",
|
|
89
89
|
"expo-status-bar": "~58.0.0",
|
|
90
90
|
"expo-store-review": "~58.0.0",
|
|
91
|
-
"expo-symbols": "~58.0.
|
|
91
|
+
"expo-symbols": "~58.0.1",
|
|
92
92
|
"expo-system-ui": "~58.0.1",
|
|
93
|
-
"expo-task-manager": "~58.0.
|
|
93
|
+
"expo-task-manager": "~58.0.4",
|
|
94
94
|
"expo-tracking-transparency": "~58.0.0",
|
|
95
|
-
"expo-updates": "~58.0.
|
|
95
|
+
"expo-updates": "~58.0.5",
|
|
96
96
|
"expo-video-thumbnails": "~58.0.0",
|
|
97
97
|
"expo-video": "~58.0.1",
|
|
98
98
|
"expo-web-browser": "~58.0.0",
|
|
99
|
-
"expo-widgets": "~58.0.
|
|
100
|
-
"jest-expo": "~58.0.
|
|
99
|
+
"expo-widgets": "~58.0.3",
|
|
100
|
+
"jest-expo": "~58.0.2",
|
|
101
101
|
"lottie-react-native": "~7.5.0",
|
|
102
102
|
"react": "19.2.3",
|
|
103
103
|
"react-dom": "19.2.3",
|
|
@@ -104,7 +104,13 @@ open class ExpoAppSceneDelegate: UIResponder, UIWindowSceneDelegate {
|
|
|
104
104
|
}
|
|
105
105
|
|
|
106
106
|
open func scene(_ scene: UIScene, openURLContexts URLContexts: Set<UIOpenURLContext>) {
|
|
107
|
-
|
|
107
|
+
// React Native ignores the app-delegate URL API under the scene life cycle, so a warm link only
|
|
108
|
+
// reaches JS through the scene API. It reads a single context, hence one call per context.
|
|
109
|
+
URLContexts.forEach { context in
|
|
110
|
+
forwarder.open(url: context.url, options: Self.openURLOptions(from: context.options)) {
|
|
111
|
+
RCTLinkingManager.scene(scene, openURLContexts: [context])
|
|
112
|
+
}
|
|
113
|
+
}
|
|
108
114
|
}
|
|
109
115
|
|
|
110
116
|
open func scene(_ scene: UIScene, willContinueUserActivityWithType userActivityType: String) {
|
|
@@ -112,7 +118,9 @@ open class ExpoAppSceneDelegate: UIResponder, UIWindowSceneDelegate {
|
|
|
112
118
|
}
|
|
113
119
|
|
|
114
120
|
open func scene(_ scene: UIScene, continue userActivity: NSUserActivity) {
|
|
115
|
-
forwarder.continue(userActivity)
|
|
121
|
+
forwarder.continue(userActivity) {
|
|
122
|
+
RCTLinkingManager.scene(scene, continue: userActivity)
|
|
123
|
+
}
|
|
116
124
|
}
|
|
117
125
|
|
|
118
126
|
open func scene(
|
|
@@ -19,18 +19,26 @@ import React
|
|
|
19
19
|
struct SceneEventForwarder {
|
|
20
20
|
var appDelegate: () -> ExpoAppDelegate? = { UIApplication.shared.delegate as? ExpoAppDelegate }
|
|
21
21
|
|
|
22
|
-
func open(
|
|
22
|
+
func open(
|
|
23
|
+
url: URL,
|
|
24
|
+
options: [UIApplication.OpenURLOptionsKey: Any],
|
|
25
|
+
notifyReactNative: (() -> Void)? = nil
|
|
26
|
+
) {
|
|
23
27
|
let application = UIApplication.shared
|
|
24
28
|
let delegate = appDelegate()
|
|
25
29
|
|
|
26
30
|
notifyLinkingManagerUnlessAlreadyNotified(of: url) {
|
|
27
31
|
_ = delegate?.application(application, open: url, options: options)
|
|
28
32
|
} notify: {
|
|
29
|
-
|
|
33
|
+
if let notifyReactNative {
|
|
34
|
+
notifyReactNative()
|
|
35
|
+
} else {
|
|
36
|
+
RCTLinkingManager.application(application, open: url, options: options)
|
|
37
|
+
}
|
|
30
38
|
}
|
|
31
39
|
}
|
|
32
40
|
|
|
33
|
-
func `continue`(_ userActivity: NSUserActivity) {
|
|
41
|
+
func `continue`(_ userActivity: NSUserActivity, notifyReactNative: (() -> Void)? = nil) {
|
|
34
42
|
let application = UIApplication.shared
|
|
35
43
|
let delegate = appDelegate()
|
|
36
44
|
|
|
@@ -39,7 +47,11 @@ struct SceneEventForwarder {
|
|
|
39
47
|
notifyLinkingManagerUnlessAlreadyNotified(of: userActivity.webpageURL) {
|
|
40
48
|
_ = delegate?.application(application, continue: userActivity, restorationHandler: { _ in })
|
|
41
49
|
} notify: {
|
|
42
|
-
|
|
50
|
+
if let notifyReactNative {
|
|
51
|
+
notifyReactNative()
|
|
52
|
+
} else {
|
|
53
|
+
RCTLinkingManager.application(application, continue: userActivity, restorationHandler: { _ in })
|
|
54
|
+
}
|
|
43
55
|
}
|
|
44
56
|
}
|
|
45
57
|
|
|
@@ -244,6 +244,51 @@ struct ExpoAppSceneDelegateTests {
|
|
|
244
244
|
#expect(recorder.count(of: webpageURL) == 1)
|
|
245
245
|
}
|
|
246
246
|
|
|
247
|
+
@Test
|
|
248
|
+
@MainActor
|
|
249
|
+
func `notifies React Native through the injected notifier for a warm URL`() {
|
|
250
|
+
let spy = SpyAppDelegate()
|
|
251
|
+
let url = URL(string: "bareexpo://scene-delegate/warm-open-url")!
|
|
252
|
+
let recorder = OpenURLNotificationRecorder()
|
|
253
|
+
var notifications = 0
|
|
254
|
+
SceneEventForwarder(appDelegate: { spy }).open(url: url, options: [:]) {
|
|
255
|
+
notifications += 1
|
|
256
|
+
}
|
|
257
|
+
#expect(spy.openedURLs.first?.url == url)
|
|
258
|
+
#expect(notifications == 1)
|
|
259
|
+
#expect(recorder.count(of: url) == 0)
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
@Test
|
|
263
|
+
@MainActor
|
|
264
|
+
func `does not notify React Native when the app delegate already notified for a URL`() {
|
|
265
|
+
let delegate = LegacyLinkingAppDelegate()
|
|
266
|
+
let url = URL(string: "bareexpo://scene-delegate/warm-legacy-open-url")!
|
|
267
|
+
var notifications = 0
|
|
268
|
+
SceneEventForwarder(appDelegate: { delegate }).open(url: url, options: [:]) {
|
|
269
|
+
notifications += 1
|
|
270
|
+
}
|
|
271
|
+
#expect(delegate.openedURLs == [url])
|
|
272
|
+
#expect(notifications == 0)
|
|
273
|
+
}
|
|
274
|
+
|
|
275
|
+
@Test
|
|
276
|
+
@MainActor
|
|
277
|
+
func `notifies React Native through the injected notifier for a warm user activity`() {
|
|
278
|
+
let spy = SpyAppDelegate()
|
|
279
|
+
let userActivity = NSUserActivity(activityType: NSUserActivityTypeBrowsingWeb)
|
|
280
|
+
let webpageURL = URL(string: "https://expo.dev/scene-delegate/warm-activity")!
|
|
281
|
+
userActivity.webpageURL = webpageURL
|
|
282
|
+
let recorder = OpenURLNotificationRecorder()
|
|
283
|
+
var notifications = 0
|
|
284
|
+
SceneEventForwarder(appDelegate: { spy }).continue(userActivity) {
|
|
285
|
+
notifications += 1
|
|
286
|
+
}
|
|
287
|
+
#expect(spy.continuedUserActivities.first === userActivity)
|
|
288
|
+
#expect(notifications == 1)
|
|
289
|
+
#expect(recorder.count(of: webpageURL) == 0)
|
|
290
|
+
}
|
|
291
|
+
|
|
247
292
|
@Test
|
|
248
293
|
@MainActor
|
|
249
294
|
func `does not notify subscribers without an ExpoAppDelegate`() {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "expo",
|
|
3
|
-
"version": "58.0.0-preview.
|
|
3
|
+
"version": "58.0.0-preview.3",
|
|
4
4
|
"description": "The Expo SDK",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"expo"
|
|
@@ -23,6 +23,8 @@
|
|
|
23
23
|
},
|
|
24
24
|
"files": [
|
|
25
25
|
"android",
|
|
26
|
+
"!android/.gitignore",
|
|
27
|
+
"!android/build/**",
|
|
26
28
|
"bin",
|
|
27
29
|
"build",
|
|
28
30
|
"internal",
|
|
@@ -198,36 +200,36 @@
|
|
|
198
200
|
},
|
|
199
201
|
"dependencies": {
|
|
200
202
|
"@babel/runtime": "^7.20.0",
|
|
201
|
-
"@expo/cli": "^58.0.
|
|
203
|
+
"@expo/cli": "^58.0.3",
|
|
202
204
|
"@expo/config": "~58.0.0",
|
|
203
|
-
"@expo/config-plugins": "~58.0.
|
|
205
|
+
"@expo/config-plugins": "~58.0.1",
|
|
204
206
|
"@expo/devtools": "~58.0.0",
|
|
205
207
|
"@expo/dom-webview": "~58.0.0",
|
|
206
|
-
"@expo/fingerprint": "^0.21.
|
|
208
|
+
"@expo/fingerprint": "^0.21.1",
|
|
207
209
|
"@expo/local-build-cache-provider": "^58.0.0",
|
|
208
|
-
"@expo/log-box": "^58.0.
|
|
210
|
+
"@expo/log-box": "^58.0.3",
|
|
209
211
|
"@expo/log-box-utils": "^58.0.0",
|
|
210
|
-
"@expo/metro": "~
|
|
211
|
-
"@expo/metro-config": "~58.0.
|
|
212
|
+
"@expo/metro": "~58.0.0-rc.0",
|
|
213
|
+
"@expo/metro-config": "~58.0.3",
|
|
212
214
|
"@ungap/structured-clone": "^1.3.0",
|
|
213
|
-
"babel-preset-expo": "~58.0.
|
|
214
|
-
"expo-asset": "~58.0.
|
|
215
|
-
"expo-constants": "~58.0.
|
|
215
|
+
"babel-preset-expo": "~58.0.3",
|
|
216
|
+
"expo-asset": "~58.0.3",
|
|
217
|
+
"expo-constants": "~58.0.3",
|
|
216
218
|
"expo-file-system": "~58.0.0",
|
|
217
|
-
"expo-font": "~58.0.
|
|
219
|
+
"expo-font": "~58.0.1",
|
|
218
220
|
"expo-keep-awake": "~58.0.0",
|
|
219
|
-
"expo-modules-autolinking": "~58.0.
|
|
220
|
-
"expo-modules-core": "~58.0.
|
|
221
|
+
"expo-modules-autolinking": "~58.0.2",
|
|
222
|
+
"expo-modules-core": "~58.0.3",
|
|
221
223
|
"pretty-format": "^29.7.0",
|
|
222
224
|
"react-refresh": "^0.14.2",
|
|
223
225
|
"whatwg-url-minimum": "^0.2.0"
|
|
224
226
|
},
|
|
225
227
|
"devDependencies": {
|
|
226
228
|
"@expo/dom-webview": "58.0.0",
|
|
227
|
-
"@expo/metro-runtime": "58.0.
|
|
229
|
+
"@expo/metro-runtime": "58.0.3",
|
|
228
230
|
"@types/node": "^22.14.0",
|
|
229
231
|
"@types/react": "~19.2.0",
|
|
230
|
-
"expo-updates": "58.0.
|
|
232
|
+
"expo-updates": "58.0.5",
|
|
231
233
|
"npm-run-all2": "^8.0.4",
|
|
232
234
|
"react": "19.2.3",
|
|
233
235
|
"react-dom": "19.2.3",
|
|
@@ -267,13 +269,14 @@
|
|
|
267
269
|
"engines": {
|
|
268
270
|
"node": "^22.13.0 || ^24.3.0 || ^26.0.0 || >=27.0.0"
|
|
269
271
|
},
|
|
270
|
-
"gitHead": "
|
|
272
|
+
"gitHead": "917a61b581baeb852aa4fe331da2c1395d824351",
|
|
271
273
|
"scripts": {
|
|
272
274
|
"build": "../expo-module-scripts/bin/expo-build src",
|
|
273
275
|
"clean": "rimraf build",
|
|
274
276
|
"lint": "oxlint --config oxlint.config.mjs .",
|
|
275
277
|
"format": "expo-module format",
|
|
276
|
-
"test": "jest",
|
|
278
|
+
"test": "jest && jest --config scripts/spm/jest.config.js",
|
|
279
|
+
"test:spm-plugin": "jest --config scripts/spm/jest.config.js",
|
|
277
280
|
"depscheck": "expo-module depscheck",
|
|
278
281
|
"typecheck": "tsc -p tsconfig.json"
|
|
279
282
|
}
|
package/react-native.config.js
CHANGED
|
@@ -47,4 +47,11 @@ module.exports = {
|
|
|
47
47
|
windows: null,
|
|
48
48
|
},
|
|
49
49
|
},
|
|
50
|
+
// React Native SwiftPM autolinking plugin (preview). Discovered transitively by
|
|
51
|
+
// `react-native spm` when `expo` is an autolinked dependency; contributes Expo's
|
|
52
|
+
// native modules (precompiled xcframeworks + source packages) and the generated
|
|
53
|
+
// ExpoModulesProvider into the SwiftPM autolinking graph.
|
|
54
|
+
spm: {
|
|
55
|
+
autolinkingPlugin: './scripts/spm/plugin.js',
|
|
56
|
+
},
|
|
50
57
|
};
|
|
@@ -0,0 +1,118 @@
|
|
|
1
|
+
# Expo SwiftPM autolinking plugin
|
|
2
|
+
|
|
3
|
+
This preview plugin is discovered through `expo/react-native.config.js` by
|
|
4
|
+
React Native's `react-native spm` tooling. It contributes Expo source packages,
|
|
5
|
+
the generated `ExpoModulesProvider.swift`, and precompiled Expo frameworks.
|
|
6
|
+
|
|
7
|
+
## Precompiled framework contract
|
|
8
|
+
|
|
9
|
+
Every precompiled Expo framework is returned as one immutable pair:
|
|
10
|
+
|
|
11
|
+
```js
|
|
12
|
+
{
|
|
13
|
+
id: 'expo-modules-core',
|
|
14
|
+
frameworkName: 'ExpoModulesCore',
|
|
15
|
+
linkage: 'dynamic',
|
|
16
|
+
flavors: {
|
|
17
|
+
debug: '/absolute/path/ExpoModulesCore.xcframework',
|
|
18
|
+
release: '/absolute/path/ExpoModulesCore.xcframework',
|
|
19
|
+
},
|
|
20
|
+
}
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
Both flavors are mandatory. A partial build, relative path, wrong XCFramework
|
|
24
|
+
name, missing `Info.plist`, or non-dynamic declaration aborts autolinking. The
|
|
25
|
+
plugin does not create or mutate a “current flavor” symlink.
|
|
26
|
+
|
|
27
|
+
Artifact lookup is deterministic:
|
|
28
|
+
|
|
29
|
+
1. `EXPO_PRECOMPILED_MODULES_PATH/<package>/output/<flavor>/xcframeworks`
|
|
30
|
+
2. `packages/precompile/.build/<package>/output/<flavor>/xcframeworks`
|
|
31
|
+
3. `<installed-package>/prebuilds/output/<flavor>/xcframeworks`
|
|
32
|
+
|
|
33
|
+
An artifact may be an expanded `.xcframework` or a bundled `.tar.gz`. Bundled
|
|
34
|
+
tarballs are expanded into `ios/build/expo-xcframeworks` before the plugin
|
|
35
|
+
returns, so `spm add` and `spm update` always receive both real paths. Expo's
|
|
36
|
+
prebuild command builds Debug and Release by default; do not use a single-flavor
|
|
37
|
+
prebuild for an app that consumes the SwiftPM plugin.
|
|
38
|
+
|
|
39
|
+
Lookup runs for **every** resolved pod, not just the core frameworks. Any module
|
|
40
|
+
with a `spm.config.json` product is consumed as a precompiled framework the
|
|
41
|
+
moment its artifact exists — no `Package.swift` is generated for it, and none is
|
|
42
|
+
needed. Path 3 is the published-package case: a module shipping
|
|
43
|
+
`prebuilds/output/<flavor>/xcframeworks` is picked up with no local build.
|
|
44
|
+
|
|
45
|
+
React Native normalizes the declared pairs into immutable app-local Debug and
|
|
46
|
+
Release slots. Xcode then selects, exact-links, embeds, and signs the right slot
|
|
47
|
+
for each configuration. Configurations containing `debug` or `development` use
|
|
48
|
+
Debug; all others use Release. Adding, removing, or changing a flavored Expo
|
|
49
|
+
framework requires `npx react-native spm update`; build-time `spm sync` only
|
|
50
|
+
refreshes invariant source/codegen output.
|
|
51
|
+
|
|
52
|
+
## SwiftPM graph
|
|
53
|
+
|
|
54
|
+
Flavored runtime frameworks are not SwiftPM products or binary targets. The
|
|
55
|
+
plugin creates a binary-free compile interface tree containing their public
|
|
56
|
+
headers, module maps, and Swift module interfaces. Expo source packages compile
|
|
57
|
+
against that stable tree, while React Native owns the only runtime link/embed
|
|
58
|
+
path.
|
|
59
|
+
|
|
60
|
+
Every Expo source target uses exactly the products supplied in
|
|
61
|
+
`context.react.products`: `ReactHeaders`, `ReactNativeHeaders`,
|
|
62
|
+
`ReactNativeDependenciesHeaders`, and `ReactAppHeaders` when available. The
|
|
63
|
+
ExpoModulesCore module map itself declares `use React`, so these invariant
|
|
64
|
+
carriers are needed even when a module does not import React directly. The
|
|
65
|
+
plugin does not infer runtime React or Hermes products.
|
|
66
|
+
|
|
67
|
+
## Unsupported modules
|
|
68
|
+
|
|
69
|
+
A module the plugin cannot contribute aborts autolinking. Dropping it would not
|
|
70
|
+
fail the build — it would fail at runtime with `Cannot find native module`, far
|
|
71
|
+
from the cause. Each one is reported as an `error:`-prefixed block, which Xcode
|
|
72
|
+
surfaces as a build error, naming the fix:
|
|
73
|
+
|
|
74
|
+
- **A prebuildable product with no built artifact** — the module declares a
|
|
75
|
+
product in its `spm.config.json`, so it needs no `Package.swift`; the artifact
|
|
76
|
+
is simply missing. `et prebuild <package>` (both flavors, so omit `--flavor`)
|
|
77
|
+
fixes it. This is the preferred route for a mixed-language module: the prebuild
|
|
78
|
+
pipeline compiles Swift, Objective-C, and C++ targets into one XCFramework, so
|
|
79
|
+
no source split is required.
|
|
80
|
+
- **Mixed Swift and Objective-C/C++ sources with neither manifest** — SwiftPM
|
|
81
|
+
compiles the two languages in separate targets, and only the module can
|
|
82
|
+
declare where its split goes. Either an `spm.config.json`
|
|
83
|
+
(`packages/expo-sensors` is a worked example) or a checked-in `Package.swift`
|
|
84
|
+
(`packages/expo-constants`) makes it consumable, persisted with
|
|
85
|
+
`patch-package` for a module you do not own, and upstreamed so every consumer
|
|
86
|
+
gets it. This mirrors what React Native's autolinker asks of community
|
|
87
|
+
libraries.
|
|
88
|
+
- **No `ios` or `apple` source directory** — an incomplete install, or a
|
|
89
|
+
non-standard layout that needs a `Package.swift` naming the real path.
|
|
90
|
+
- **Manifest targets whose sources are not on disk** — a checked-in
|
|
91
|
+
`Package.swift` whose targets declare no `path:` and have no sources under a
|
|
92
|
+
predefined source directory, so the generated targets would point at nothing.
|
|
93
|
+
- **Native linkage declared only in the podspec** — `s.frameworks`,
|
|
94
|
+
`s.weak_frameworks` or `s.libraries`. The plugin never reads linkage out of
|
|
95
|
+
Ruby: a misread link line fails in a shipped app, not here. Declare it as
|
|
96
|
+
`linkerSettings` in a `Package.swift`, which the plugin mirrors verbatim, or
|
|
97
|
+
ship an `spm.config.json` and prebuild the module.
|
|
98
|
+
- **An iOS deployment floor that is not a literal** — the plugin reads
|
|
99
|
+
`s.platforms = { :ios => '16.4' }` and `s.ios.deployment_target = '16.4'` as
|
|
100
|
+
text; anything computed would need the podspec to be run.
|
|
101
|
+
- **No precompiled ExpoModulesCore** — a single project-level fault reported
|
|
102
|
+
once, not per module, since every source module compiles against its interface
|
|
103
|
+
tree.
|
|
104
|
+
|
|
105
|
+
One podspec finding is a warning rather than an error: linker flags set through
|
|
106
|
+
`OTHER_LDFLAGS` in a `pod_target_xcconfig`. The module is still emitted, because
|
|
107
|
+
Swift links the system frameworks and C++ runtime its sources import; if it does
|
|
108
|
+
fail to link, declare the missing library as `linkerSettings` in a
|
|
109
|
+
`Package.swift`.
|
|
110
|
+
|
|
111
|
+
To build without a module, exclude it from autolinking in the app's
|
|
112
|
+
`package.json` (`expo.autolinking.exclude`); its native module is then
|
|
113
|
+
unavailable at runtime.
|
|
114
|
+
|
|
115
|
+
Emitted modules are also scanned for CocoaPods dependencies with no SwiftPM
|
|
116
|
+
counterpart (`SDWebImage`, `ZXingObjC`, …). Those are a warning, not an error:
|
|
117
|
+
the module enters the graph and compiles until a source file reaches the missing
|
|
118
|
+
dependency.
|