expo-modules-core 3.0.26 → 3.0.28

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,16 @@
10
10
 
11
11
  ### 💡 Others
12
12
 
13
+ ## 3.0.28 — 2025-12-05
14
+
15
+ _This version does not introduce any user-facing changes._
16
+
17
+ ## 3.0.27 — 2025-12-04
18
+
19
+ ### 🐛 Bug fixes
20
+
21
+ - [iOS] Universal links not working on cold start. ([#41185](https://github.com/expo/expo/pull/41185)) by [@jbaudanza](https://github.com/jbaudanza)
22
+
13
23
  ## 3.0.26 — 2025-11-17
14
24
 
15
25
  ### 🐛 Bug fixes
@@ -29,7 +29,7 @@ if (shouldIncludeCompose) {
29
29
  }
30
30
 
31
31
  group = 'host.exp.exponent'
32
- version = '3.0.26'
32
+ version = '3.0.28'
33
33
 
34
34
  def isExpoModulesCoreTests = {
35
35
  Gradle gradle = getGradle()
@@ -79,7 +79,7 @@ android {
79
79
  defaultConfig {
80
80
  consumerProguardFiles 'proguard-rules.pro'
81
81
  versionCode 1
82
- versionName "3.0.26"
82
+ versionName "3.0.28"
83
83
  buildConfigField "String", "EXPO_MODULES_CORE_VERSION", "\"${versionName}\""
84
84
  buildConfigField "boolean", "IS_NEW_ARCHITECTURE_ENABLED", isNewArchitectureEnabled.toString()
85
85
 
@@ -10,10 +10,10 @@ public class ExpoAppDelegateSubscriberManager: NSObject {
10
10
  willFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]? = nil
11
11
  ) -> Bool {
12
12
  let parsedSubscribers = ExpoAppDelegateSubscriberRepository.subscribers.filter {
13
- $0.responds(to: #selector(application(_:willFinishLaunchingWithOptions:)))
13
+ $0.responds(to: #selector(UIApplicationDelegate.application(_:willFinishLaunchingWithOptions:)))
14
14
  }
15
15
 
16
- // If we can't find a subscriber that implements `willFinishLaunchingWithOptions`, we will delegate the decision if we can handel the passed URL to
16
+ // If we can't find a subscriber that implements `willFinishLaunchingWithOptions`, we will delegate the decision if we can handle the passed URL to
17
17
  // the `didFinishLaunchingWithOptions` method by returning `true` here.
18
18
  // You can read more about how iOS handles deep links here: https://developer.apple.com/documentation/uikit/uiapplicationdelegate/1623112-application#discussion
19
19
  if parsedSubscribers.isEmpty {
@@ -42,7 +42,7 @@ public class ExpoAppDelegateSubscriberManager: NSObject {
42
42
  @objc
43
43
  public static func applicationWillFinishLaunching(_ notification: Notification) {
44
44
  let parsedSubscribers = ExpoAppDelegateSubscriberRepository.subscribers.filter {
45
- $0.responds(to: #selector(applicationWillFinishLaunching(_:)))
45
+ $0.responds(to: #selector(NSApplicationDelegate.applicationWillFinishLaunching(_:)))
46
46
  }
47
47
 
48
48
  parsedSubscribers.forEach { subscriber in
@@ -163,7 +163,7 @@ public class ExpoAppDelegateSubscriberManager: NSObject {
163
163
  handleEventsForBackgroundURLSession identifier: String,
164
164
  completionHandler: @escaping () -> Void
165
165
  ) {
166
- let selector = #selector(application(_:handleEventsForBackgroundURLSession:completionHandler:))
166
+ let selector = #selector(UIApplicationDelegate.application(_:handleEventsForBackgroundURLSession:completionHandler:))
167
167
  let subs = ExpoAppDelegateSubscriberRepository.subscribers.filter { $0.responds(to: selector) }
168
168
  var subscribersLeft = subs.count
169
169
  let dispatchQueue = DispatchQueue(label: "expo.application.handleBackgroundEvents")
@@ -212,7 +212,7 @@ public class ExpoAppDelegateSubscriberManager: NSObject {
212
212
  didReceiveRemoteNotification userInfo: [AnyHashable: Any],
213
213
  fetchCompletionHandler completionHandler: @escaping (UIBackgroundFetchResult) -> Void
214
214
  ) {
215
- let selector = #selector(application(_:didReceiveRemoteNotification:fetchCompletionHandler:))
215
+ let selector = #selector(UIApplicationDelegate.application(_:didReceiveRemoteNotification:fetchCompletionHandler:))
216
216
  let subs = ExpoAppDelegateSubscriberRepository.subscribers.filter { $0.responds(to: selector) }
217
217
  var subscribersLeft = subs.count
218
218
  let dispatchQueue = DispatchQueue(label: "expo.application.remoteNotification", qos: .userInteractive)
@@ -256,7 +256,7 @@ public class ExpoAppDelegateSubscriberManager: NSObject {
256
256
  _ application: NSApplication,
257
257
  didReceiveRemoteNotification userInfo: [String: Any]
258
258
  ) {
259
- let selector = #selector(application(_:didReceiveRemoteNotification:))
259
+ let selector = #selector(NSApplicationDelegate.application(_:didReceiveRemoteNotification:))
260
260
  let subs = ExpoAppDelegateSubscriberRepository.subscribers.filter { $0.responds(to: selector) }
261
261
 
262
262
  subs.forEach { subscriber in
@@ -283,7 +283,7 @@ public class ExpoAppDelegateSubscriberManager: NSObject {
283
283
  continue userActivity: NSUserActivity,
284
284
  restorationHandler: @escaping ([UIUserActivityRestoring]?) -> Void
285
285
  ) -> Bool {
286
- let selector = #selector(application(_:continue:restorationHandler:))
286
+ let selector = #selector(UIApplicationDelegate.application(_:continue:restorationHandler:))
287
287
  let subs = ExpoAppDelegateSubscriberRepository.subscribers.filter { $0.responds(to: selector) }
288
288
  var subscribersLeft = subs.count
289
289
  let dispatchQueue = DispatchQueue(label: "expo.application.continueUserActivity", qos: .userInteractive)
@@ -314,7 +314,7 @@ public class ExpoAppDelegateSubscriberManager: NSObject {
314
314
  continue userActivity: NSUserActivity,
315
315
  restorationHandler: @escaping ([any NSUserActivityRestoring]) -> Void
316
316
  ) -> Bool {
317
- let selector = #selector(application(_:continue:restorationHandler:))
317
+ let selector = #selector(NSApplicationDelegate.application(_:continue:restorationHandler:))
318
318
  let subs = ExpoAppDelegateSubscriberRepository.subscribers.filter { $0.responds(to: selector) }
319
319
  var subscribersLeft = subs.count
320
320
  let dispatchQueue = DispatchQueue(label: "expo.application.continueUserActivity", qos: .userInteractive)
@@ -363,7 +363,7 @@ public class ExpoAppDelegateSubscriberManager: NSObject {
363
363
  performActionFor shortcutItem: UIApplicationShortcutItem,
364
364
  completionHandler: @escaping (Bool) -> Void
365
365
  ) {
366
- let selector = #selector(application(_:performActionFor:completionHandler:))
366
+ let selector = #selector(UIApplicationDelegate.application(_:performActionFor:completionHandler:))
367
367
  let subs = ExpoAppDelegateSubscriberRepository.subscribers.filter { $0.responds(to: selector) }
368
368
  var subscribersLeft = subs.count
369
369
  var result: Bool = false
@@ -398,7 +398,7 @@ public class ExpoAppDelegateSubscriberManager: NSObject {
398
398
  _ application: UIApplication,
399
399
  performFetchWithCompletionHandler completionHandler: @escaping (UIBackgroundFetchResult) -> Void
400
400
  ) {
401
- let selector = #selector(application(_:performFetchWithCompletionHandler:))
401
+ let selector = #selector(UIApplicationDelegate.application(_:performFetchWithCompletionHandler:))
402
402
  let subs = ExpoAppDelegateSubscriberRepository.subscribers.filter { $0.responds(to: selector) }
403
403
  var subscribersLeft = subs.count
404
404
  let dispatchQueue = DispatchQueue(label: "expo.application.performFetch", qos: .userInteractive)
@@ -469,7 +469,7 @@ public class ExpoAppDelegateSubscriberManager: NSObject {
469
469
  let infoPlistOrientations = deviceOrientationMask.isEmpty ? universalOrientationMask : deviceOrientationMask
470
470
 
471
471
  let parsedSubscribers = ExpoAppDelegateSubscriberRepository.subscribers.filter {
472
- $0.responds(to: #selector(application(_:supportedInterfaceOrientationsFor:)))
472
+ $0.responds(to: #selector(UIApplicationDelegate.application(_:supportedInterfaceOrientationsFor:)))
473
473
  }
474
474
 
475
475
  // We want to create an intersection of all orientations set by subscribers.
@@ -0,0 +1,344 @@
1
+ import Testing
2
+ import UIKit
3
+
4
+ @testable import ExpoModulesCore
5
+
6
+ // Mock subscriber for testing
7
+ class MockAppDelegateSubscriber: ExpoAppDelegateSubscriber {
8
+ var didCallDidBecomeActive = false
9
+ var didCallWillResignActive = false
10
+ var didCallDidEnterBackground = false
11
+ var didCallWillEnterForeground = false
12
+ var didCallWillTerminate = false
13
+ var didCallDidReceiveMemoryWarning = false
14
+ var didCallHandleBackgroundURLSession = false
15
+ var didCallDidRegisterForRemoteNotifications = false
16
+ var didCallDidFailToRegisterForRemoteNotifications = false
17
+ var didCallDidReceiveRemoteNotification = false
18
+ var didCallDidUpdateUserActivity = false
19
+ var didCallDidFailToContinueUserActivity = false
20
+ var didCallPerformActionForShortcut = false
21
+ var didCallPerformFetch = false
22
+ var didCallFinishLaunchingWithOptions = false
23
+ var didCallwillFinishLaunchingWithOptions = false
24
+
25
+ func application(
26
+ _ application: UIApplication,
27
+ willFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]? = nil
28
+ ) -> Bool {
29
+ didCallwillFinishLaunchingWithOptions = true
30
+ return true
31
+ }
32
+
33
+ func application(
34
+ _ application: UIApplication,
35
+ didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]? = nil
36
+ ) -> Bool {
37
+ didCallFinishLaunchingWithOptions = true
38
+ return false // return value is ignored by ExpoAppDelegateSubscriberManager
39
+ }
40
+
41
+ func applicationDidBecomeActive(_ application: UIApplication) {
42
+ didCallDidBecomeActive = true
43
+ }
44
+
45
+ func applicationWillResignActive(_ application: UIApplication) {
46
+ didCallWillResignActive = true
47
+ }
48
+
49
+ func applicationDidEnterBackground(_ application: UIApplication) {
50
+ didCallDidEnterBackground = true
51
+ }
52
+
53
+ func applicationWillEnterForeground(_ application: UIApplication) {
54
+ didCallWillEnterForeground = true
55
+ }
56
+
57
+ func applicationWillTerminate(_ application: UIApplication) {
58
+ didCallWillTerminate = true
59
+ }
60
+
61
+ func applicationDidReceiveMemoryWarning(_ application: UIApplication) {
62
+ didCallDidReceiveMemoryWarning = true
63
+ }
64
+
65
+ func application(
66
+ _ application: UIApplication,
67
+ handleEventsForBackgroundURLSession identifier: String,
68
+ completionHandler: @escaping () -> Void
69
+ ) {
70
+ didCallHandleBackgroundURLSession = true
71
+ completionHandler()
72
+ }
73
+
74
+ func application(
75
+ _ application: UIApplication,
76
+ didRegisterForRemoteNotificationsWithDeviceToken deviceToken: Data
77
+ ) {
78
+ didCallDidRegisterForRemoteNotifications = true
79
+ }
80
+
81
+ func application(
82
+ _ application: UIApplication,
83
+ didFailToRegisterForRemoteNotificationsWithError error: Error
84
+ ) {
85
+ didCallDidFailToRegisterForRemoteNotifications = true
86
+ }
87
+
88
+ func application(
89
+ _ application: UIApplication,
90
+ didReceiveRemoteNotification userInfo: [AnyHashable: Any],
91
+ fetchCompletionHandler completionHandler: @escaping (UIBackgroundFetchResult) -> Void
92
+ ) {
93
+ didCallDidReceiveRemoteNotification = true
94
+ completionHandler(.noData)
95
+ }
96
+
97
+ func application(
98
+ _ application: UIApplication,
99
+ willContinueUserActivityWithType userActivityType: String
100
+ ) -> Bool {
101
+ return true
102
+ }
103
+
104
+ func application(
105
+ _ application: UIApplication,
106
+ continue userActivity: NSUserActivity,
107
+ restorationHandler: @escaping ([UIUserActivityRestoring]?) -> Void
108
+ ) -> Bool {
109
+ restorationHandler(nil)
110
+ return true
111
+ }
112
+
113
+ func application(_ application: UIApplication, didUpdate userActivity: NSUserActivity) {
114
+ didCallDidUpdateUserActivity = true
115
+ }
116
+
117
+ func application(
118
+ _ application: UIApplication,
119
+ didFailToContinueUserActivityWithType userActivityType: String,
120
+ error: Error
121
+ ) {
122
+ didCallDidFailToContinueUserActivity = true
123
+ }
124
+
125
+ func application(
126
+ _ application: UIApplication,
127
+ performActionFor shortcutItem: UIApplicationShortcutItem,
128
+ completionHandler: @escaping (Bool) -> Void
129
+ ) {
130
+ didCallPerformActionForShortcut = true
131
+ completionHandler(true)
132
+ }
133
+
134
+ func application(
135
+ _ application: UIApplication,
136
+ performFetchWithCompletionHandler completionHandler: @escaping (UIBackgroundFetchResult) -> Void
137
+ ) {
138
+ didCallPerformFetch = true
139
+ completionHandler(.noData)
140
+ }
141
+
142
+ func application(
143
+ _ app: UIApplication,
144
+ open url: URL,
145
+ options: [UIApplication.OpenURLOptionsKey: Any] = [:]
146
+ ) -> Bool {
147
+ return true
148
+ }
149
+
150
+ func application(
151
+ _ application: UIApplication,
152
+ supportedInterfaceOrientationsFor window: UIWindow?
153
+ ) -> UIInterfaceOrientationMask {
154
+ return .portrait
155
+ }
156
+ }
157
+
158
+ @Suite
159
+ @MainActor
160
+ final class ExpoAppDelegateSubscriberManagerTests {
161
+ let subscriber = MockAppDelegateSubscriber()
162
+
163
+ init() {
164
+ ExpoAppDelegateSubscriberRepository.registerSubscriber(subscriber)
165
+ }
166
+
167
+ // MARK: - Non-void returning methods
168
+
169
+ @Test
170
+ func willFinishLaunchingWithOptions() {
171
+ let result = ExpoAppDelegateSubscriberManager.application(UIApplication.shared, willFinishLaunchingWithOptions: nil)
172
+ #expect(result == true) // NOTE: could also be true if no subscribers respond to selector
173
+ #expect(subscriber.didCallwillFinishLaunchingWithOptions == true)
174
+ }
175
+
176
+ @Test
177
+ func openURL() {
178
+ let url = URL(string: "https://example.com")!
179
+ let result = ExpoAppDelegateSubscriberManager.application(UIApplication.shared, open: url, options: [:])
180
+ #expect(result == true)
181
+ }
182
+
183
+ @Test
184
+ func supportedInterfaceOrientationsFor() {
185
+ let result = ExpoAppDelegateSubscriberManager.application(UIApplication.shared, supportedInterfaceOrientationsFor: nil)
186
+ #expect(result == .portrait)
187
+ }
188
+
189
+ @Test
190
+ func willContinueUserActivityWithType() {
191
+ let result = ExpoAppDelegateSubscriberManager.application(UIApplication.shared, willContinueUserActivityWithType: "test")
192
+ #expect(result == true)
193
+ }
194
+
195
+ @Test
196
+ func didFinishLaunchingWithOptions() {
197
+ let result = ExpoAppDelegateSubscriberManager.application(UIApplication.shared, didFinishLaunchingWithOptions: nil)
198
+ #expect(result == true) // always true
199
+ #expect(subscriber.didCallFinishLaunchingWithOptions == true)
200
+ }
201
+
202
+ // MARK: - Void-returning methods
203
+
204
+ @Test
205
+ func applicationDidBecomeActive() {
206
+ ExpoAppDelegateSubscriberManager.applicationDidBecomeActive(UIApplication.shared)
207
+ #expect(subscriber.didCallDidBecomeActive == true)
208
+ }
209
+
210
+ @Test
211
+ func applicationWillResignActive() {
212
+ ExpoAppDelegateSubscriberManager.applicationWillResignActive(UIApplication.shared)
213
+ #expect(subscriber.didCallWillResignActive == true)
214
+ }
215
+
216
+ @Test
217
+ func applicationDidEnterBackground() {
218
+ ExpoAppDelegateSubscriberManager.applicationDidEnterBackground(UIApplication.shared)
219
+ #expect(subscriber.didCallDidEnterBackground == true)
220
+ }
221
+
222
+ @Test
223
+ func applicationWillEnterForeground() {
224
+ ExpoAppDelegateSubscriberManager.applicationWillEnterForeground(UIApplication.shared)
225
+ #expect(subscriber.didCallWillEnterForeground == true)
226
+ }
227
+
228
+ @Test
229
+ func applicationWillTerminate() {
230
+ ExpoAppDelegateSubscriberManager.applicationWillTerminate(UIApplication.shared)
231
+ #expect(subscriber.didCallWillTerminate == true)
232
+ }
233
+
234
+ @Test
235
+ func applicationDidReceiveMemoryWarning() {
236
+ ExpoAppDelegateSubscriberManager.applicationDidReceiveMemoryWarning(UIApplication.shared)
237
+ #expect(subscriber.didCallDidReceiveMemoryWarning == true)
238
+ }
239
+
240
+ @Test
241
+ func didRegisterForRemoteNotificationsWithDeviceToken() {
242
+ let deviceToken = Data()
243
+ ExpoAppDelegateSubscriberManager.application(UIApplication.shared, didRegisterForRemoteNotificationsWithDeviceToken: deviceToken)
244
+ #expect(subscriber.didCallDidRegisterForRemoteNotifications == true)
245
+ }
246
+
247
+ @Test
248
+ func didFailToRegisterForRemoteNotificationsWithError() {
249
+ let error = NSError(domain: "test", code: 0)
250
+ ExpoAppDelegateSubscriberManager.application(UIApplication.shared, didFailToRegisterForRemoteNotificationsWithError: error)
251
+ #expect(subscriber.didCallDidFailToRegisterForRemoteNotifications == true)
252
+ }
253
+
254
+ @Test
255
+ func didUpdateUserActivity() {
256
+ let userActivity = NSUserActivity(activityType: "test")
257
+ ExpoAppDelegateSubscriberManager.application(UIApplication.shared, didUpdate: userActivity)
258
+ #expect(subscriber.didCallDidUpdateUserActivity == true)
259
+ }
260
+
261
+ @Test
262
+ func didFailToContinueUserActivityWithType() {
263
+ let error = NSError(domain: "test", code: 0)
264
+ ExpoAppDelegateSubscriberManager.application(
265
+ UIApplication.shared,
266
+ didFailToContinueUserActivityWithType: "test",
267
+ error: error
268
+ )
269
+ #expect(subscriber.didCallDidFailToContinueUserActivity == true)
270
+ }
271
+
272
+ // MARK: - Completion-handler-based methods
273
+
274
+ @Test
275
+ func handleEventsForBackgroundURLSession() async {
276
+ await withCheckedContinuation { continuation in
277
+ ExpoAppDelegateSubscriberManager.application(
278
+ UIApplication.shared,
279
+ handleEventsForBackgroundURLSession: "test-session",
280
+ completionHandler: {
281
+ #expect(self.subscriber.didCallHandleBackgroundURLSession == true)
282
+ continuation.resume()
283
+ }
284
+ )
285
+ }
286
+ }
287
+
288
+ @Test
289
+ func didReceiveRemoteNotification() async {
290
+ await withCheckedContinuation { continuation in
291
+ ExpoAppDelegateSubscriberManager.application(
292
+ UIApplication.shared,
293
+ didReceiveRemoteNotification: [:],
294
+ fetchCompletionHandler: { _ in
295
+ #expect(self.subscriber.didCallDidReceiveRemoteNotification == true)
296
+ continuation.resume()
297
+ }
298
+ )
299
+ }
300
+ }
301
+
302
+ @Test
303
+ func continueUserActivity() async {
304
+ await withCheckedContinuation { continuation in
305
+ let userActivity = NSUserActivity(activityType: NSUserActivityTypeBrowsingWeb)
306
+ let result = ExpoAppDelegateSubscriberManager.application(
307
+ UIApplication.shared,
308
+ continue: userActivity,
309
+ restorationHandler: { _ in
310
+ continuation.resume()
311
+ }
312
+ )
313
+ #expect(result == true)
314
+ }
315
+ }
316
+
317
+ @Test
318
+ func performActionForShortcut() async {
319
+ await withCheckedContinuation { continuation in
320
+ let shortcutItem = UIApplicationShortcutItem(type: "test", localizedTitle: "Test")
321
+ ExpoAppDelegateSubscriberManager.application(
322
+ UIApplication.shared,
323
+ performActionFor: shortcutItem,
324
+ completionHandler: { _ in
325
+ #expect(self.subscriber.didCallPerformActionForShortcut == true)
326
+ continuation.resume()
327
+ }
328
+ )
329
+ }
330
+ }
331
+
332
+ @Test
333
+ func performFetch() async {
334
+ await withCheckedContinuation { continuation in
335
+ ExpoAppDelegateSubscriberManager.application(
336
+ UIApplication.shared,
337
+ performFetchWithCompletionHandler: { _ in
338
+ #expect(self.subscriber.didCallPerformFetch == true)
339
+ continuation.resume()
340
+ }
341
+ )
342
+ }
343
+ }
344
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "expo-modules-core",
3
- "version": "3.0.26",
3
+ "version": "3.0.28",
4
4
  "description": "The core of Expo Modules architecture",
5
5
  "main": "src/index.ts",
6
6
  "types": "build/index.d.ts",
@@ -63,7 +63,7 @@
63
63
  },
64
64
  "devDependencies": {
65
65
  "@testing-library/react-native": "^13.2.0",
66
- "expo-module-scripts": "^5.0.7"
66
+ "expo-module-scripts": "^5.0.8"
67
67
  },
68
- "gitHead": "6d7c221dc8a1994043c4f2ddcf33a16bf3e508dc"
68
+ "gitHead": "172a69f5f70c1d0e043e1532f924de97210cabc3"
69
69
  }
package/tsconfig.json CHANGED
@@ -5,5 +5,5 @@
5
5
  "emitDeclarationOnly": true
6
6
  },
7
7
  "include": ["./src"],
8
- "exclude": ["**/__mocks__/*", "**/__tests__/*"]
8
+ "exclude": ["**/__mocks__/*", "**/__tests__/*", "**/__rsc_tests__/*"]
9
9
  }