expo-notifications 0.29.15-canary-20250402-161f57b → 0.29.15-canary-20250404-42b6263

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
@@ -8,6 +8,7 @@
8
8
 
9
9
  ### 🐛 Bug fixes
10
10
 
11
+ - event emitter should not influence notification presentation ([#35858](https://github.com/expo/expo/pull/35858) by [@vonovak](https://github.com/vonovak))
11
12
  - correctly serialize `null` trigger on iOS ([#35672](https://github.com/expo/expo/pull/35672) by [@vonovak](https://github.com/vonovak))
12
13
  - restore `useLastNotificationResponse` return value behavior ([#35504](https://github.com/expo/expo/pull/35504) by [@vonovak](https://github.com/vonovak))
13
14
  - fix ios textInput action missing title ([#34866](https://github.com/expo/expo/pull/34866) by [@vonovak](https://github.com/vonovak))
@@ -31,6 +32,7 @@
31
32
  - [iOS] Swift conversion 7: Handler and Emitter. ([#35564](https://github.com/expo/expo/pull/35564) by [@douglowder](https://github.com/douglowder))
32
33
  - [iOS] Swift conversion 8: Background module. ([#35695](https://github.com/expo/expo/pull/35695) by [@douglowder](https://github.com/douglowder))
33
34
  - [iOS] Swift conversion 9: Permissions module. ([#35719](https://github.com/expo/expo/pull/35719) by [@douglowder](https://github.com/douglowder))
35
+ - [iOS] Swift conversion 10: Refactor for Expo Go. ([#35862](https://github.com/expo/expo/pull/35862) by [@douglowder](https://github.com/douglowder))
34
36
 
35
37
  ## 0.29.14 - 2025-03-11
36
38
 
@@ -5,13 +5,13 @@ plugins {
5
5
  }
6
6
 
7
7
  group = 'host.exp.exponent'
8
- version = '0.29.15-canary-20250402-161f57b'
8
+ version = '0.29.8'
9
9
 
10
10
  android {
11
11
  namespace "expo.modules.notifications"
12
12
  defaultConfig {
13
13
  versionCode 21
14
- versionName '0.29.15-canary-20250402-161f57b'
14
+ versionName '0.29.8'
15
15
  testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
16
16
  }
17
17
 
@@ -29,12 +29,6 @@
29
29
  "expo.modules.notifications.notifications.scheduling.NotificationScheduler",
30
30
  "expo.modules.notifications.serverregistration.ServerRegistrationModule",
31
31
  "expo.modules.notifications.tokens.PushTokenModule"
32
- ],
33
- "publication": {
34
- "groupId": "host.exp.exponent",
35
- "artifactId": "expo.modules.notifications",
36
- "version": "0.29.15-canary-20250402-161f57b",
37
- "repository": "https://maven.pkg.github.com/expo/expo"
38
- }
32
+ ]
39
33
  }
40
34
  }
@@ -4,45 +4,54 @@ import ExpoModulesCore
4
4
  import UIKit
5
5
  import MachO
6
6
 
7
- public class CategoriesModule: Module {
7
+ open class CategoriesModule: Module {
8
8
  public func definition() -> ModuleDefinition {
9
9
  Name("ExpoNotificationCategoriesModule")
10
10
 
11
- AsyncFunction("getNotificationCategoriesAsync") { (promise: Promise) in
12
- UNUserNotificationCenter.current().getNotificationCategories { categories in
13
- let existingCategories = categories.map { category in
14
- return CategoryRecord(category)
15
- }
16
- promise.resolve(existingCategories)
17
- }
11
+ AsyncFunction("getNotificationCategoriesAsync") {
12
+ let categories = await UNUserNotificationCenter.current().notificationCategories()
13
+ return filterAndSerializeCategories(categories)
18
14
  }
19
15
 
20
- AsyncFunction("setNotificationCategoryAsync") { (identifier: String, actions: [CategoryActionRecord], options: CategoryOptionsRecord?, promise: Promise) in
21
- let categoryRecord = CategoryRecord(identifier, actions: actions, options: options)
22
- let newNotificationCategory = categoryRecord.toUNNotificationCategory()
23
- UNUserNotificationCenter.current().getNotificationCategories { oldcategories in
24
- let newCategories = Set(oldcategories.filter { oldCategory in
25
- return oldCategory.identifier != newNotificationCategory.identifier
26
- }
27
- .union([newNotificationCategory]))
28
- UNUserNotificationCenter.current().setNotificationCategories(newCategories)
29
- promise.resolve(CategoryRecord(newNotificationCategory))
30
- }
16
+ AsyncFunction("setNotificationCategoryAsync") { (identifier: String, actions: [CategoryActionRecord], options: CategoryOptionsRecord?) in
17
+ return await setNotificationCategoryAsync(identifier: identifier, actions: actions, options: options)
31
18
  }
32
19
 
33
- AsyncFunction("deleteNotificationCategoryAsync") { (identifier: String, promise: Promise) in
34
- UNUserNotificationCenter.current().getNotificationCategories { oldCategories in
35
- let didDelete = oldCategories.contains { oldCategory in
36
- return oldCategory.identifier == identifier
37
- }
38
- if didDelete {
39
- let newCategories = Set(oldCategories.filter { oldCategory in
40
- return oldCategory.identifier != identifier
41
- })
42
- UNUserNotificationCenter.current().setNotificationCategories(newCategories)
20
+ AsyncFunction("deleteNotificationCategoryAsync") { (identifier: String) in
21
+ return await deleteNotificationCategoryAsync(identifier: identifier)
22
+ }
23
+ }
24
+
25
+ open func filterAndSerializeCategories(_ categories: Set<UNNotificationCategory>) -> [CategoryRecord] {
26
+ return categories.map { CategoryRecord($0) }
27
+ }
28
+
29
+ open func setNotificationCategoryAsync(identifier: String, actions: [CategoryActionRecord], options: CategoryOptionsRecord?) async -> CategoryRecord {
30
+ let categoryRecord = CategoryRecord(identifier, actions: actions, options: options)
31
+ let newNotificationCategory = categoryRecord.toUNNotificationCategory()
32
+ let oldCategories = await UNUserNotificationCenter.current().notificationCategories()
33
+ let newCategories = Set(
34
+ oldCategories
35
+ .filter { oldCategory in
36
+ return oldCategory.identifier != newNotificationCategory.identifier
43
37
  }
44
- promise.resolve(didDelete)
45
- }
38
+ .union([newNotificationCategory])
39
+ )
40
+ UNUserNotificationCenter.current().setNotificationCategories(newCategories)
41
+ return CategoryRecord(newNotificationCategory)
42
+ }
43
+
44
+ open func deleteNotificationCategoryAsync(identifier: String) async -> Bool {
45
+ let oldCategories = await UNUserNotificationCenter.current().notificationCategories()
46
+ let didDelete = oldCategories.contains { oldCategory in
47
+ return oldCategory.identifier == identifier
48
+ }
49
+ if didDelete {
50
+ let newCategories = Set(oldCategories.filter { oldCategory in
51
+ return oldCategory.identifier != identifier
52
+ })
53
+ UNUserNotificationCenter.current().setNotificationCategories(newCategories)
46
54
  }
55
+ return didDelete
47
56
  }
48
57
  }
@@ -8,7 +8,7 @@ let onDidReceiveNotification = "onDidReceiveNotification"
8
8
  let onDidReceiveNotificationResponse = "onDidReceiveNotificationResponse"
9
9
  let onDidClearNotificationResponse = "onDidClearNotificationResponse"
10
10
 
11
- public class EmitterModule: Module, NotificationDelegate {
11
+ open class EmitterModule: Module, NotificationDelegate {
12
12
  public func definition() -> ModuleDefinition {
13
13
  Name("ExpoNotificationsEmitter")
14
14
 
@@ -22,16 +22,15 @@ public class EmitterModule: Module, NotificationDelegate {
22
22
  NotificationCenterManager.shared.removeDelegate(self)
23
23
  }
24
24
 
25
- AsyncFunction("getLastNotificationResponseAsync") {(promise: Promise) in
25
+ AsyncFunction("getLastNotificationResponseAsync") {() -> [String: Any]? in
26
26
  if let lastResponse: UNNotificationResponse = NotificationCenterManager.shared.lastResponse {
27
- promise.resolve(EXNotificationSerializer.serializedNotificationResponse(lastResponse))
27
+ return EXNotificationSerializer.serializedNotificationResponse(lastResponse)
28
28
  }
29
- promise.resolve(nil)
29
+ return nil
30
30
  }
31
31
 
32
- AsyncFunction("clearLastNotificationResponseAsync") {(promise: Promise) in
32
+ AsyncFunction("clearLastNotificationResponseAsync") {
33
33
  NotificationCenterManager.shared.lastResponse = nil
34
- promise.resolve(nil)
35
34
  }
36
35
  }
37
36
 
@@ -40,20 +39,25 @@ public class EmitterModule: Module, NotificationDelegate {
40
39
  return true
41
40
  }
42
41
 
43
- public func didReceive(_ response: UNNotificationResponse, completionHandler: @escaping () -> Void) -> Bool {
42
+ open func didReceive(_ response: UNNotificationResponse, completionHandler: @escaping () -> Void) -> Bool {
44
43
  NotificationCenterManager.shared.lastResponse = response
45
- // TODO: convert serialization to Records
46
- let serializedResponse = EXNotificationSerializer.serializedNotificationResponse(response)
47
- self.sendEvent(onDidReceiveNotificationResponse, serializedResponse as [String: Any])
44
+ self.sendEvent(onDidReceiveNotificationResponse, serializedResponse(response))
48
45
  completionHandler()
49
46
  return true
50
47
  }
51
48
 
52
- public func willPresent(_ notification: UNNotification, completionHandler: @escaping (UNNotificationPresentationOptions) -> Void) -> Bool {
49
+ open func willPresent(_ notification: UNNotification, completionHandler: @escaping (UNNotificationPresentationOptions) -> Void) -> Bool {
50
+ self.sendEvent(onDidReceiveNotification, serializedNotification(notification))
51
+ return false
52
+ }
53
+
54
+ open func serializedNotification(_ notification: UNNotification) -> [String: Any] {
53
55
  // TODO: convert serialization to Records
54
- let serializedNotification = EXNotificationSerializer.serializedNotification(notification)
55
- self.sendEvent(onDidReceiveNotification, serializedNotification as [String: Any])
56
- completionHandler([])
57
- return true
56
+ return EXNotificationSerializer.serializedNotification(notification)
57
+ }
58
+
59
+ open func serializedResponse(_ response: UNNotificationResponse) -> [String: Any] {
60
+ // TODO: convert serialization to Records
61
+ return EXNotificationSerializer.serializedNotificationResponse(response)
58
62
  }
59
63
  }
@@ -7,7 +7,7 @@ import MachO
7
7
  let onHandleNotification = "onHandleNotification"
8
8
  let onHandleNotificationTimeout = "onHandleNotificationTimeout"
9
9
 
10
- public class HandlerModule: Module, NotificationDelegate, SingleNotificationHandlerTaskDelegate {
10
+ open class HandlerModule: Module, NotificationDelegate, SingleNotificationHandlerTaskDelegate {
11
11
  var tasksMap: [String: SingleNotificationHandlerTask] = [:]
12
12
 
13
13
  public func definition() -> ModuleDefinition {
@@ -28,7 +28,7 @@ public class HandlerModule: Module, NotificationDelegate, SingleNotificationHand
28
28
  promise.reject("ERR_NOTIFICATION_HANDLED", "Failed to handle notification \(identifier) because it has already been handled")
29
29
  return
30
30
  }
31
- if task.handleResponse(behavior) {
31
+ if task.processNotificationWithBehavior(behavior) {
32
32
  promise.resolve(nil)
33
33
  } else {
34
34
  promise.reject("ERR_NOTIFICATION_RESPONSE_TIMEOUT", "Notification has already been handled. Most probably the request has timed out.")
@@ -38,7 +38,7 @@ public class HandlerModule: Module, NotificationDelegate, SingleNotificationHand
38
38
 
39
39
  // MARK: - NotificationDelegate
40
40
 
41
- public func willPresent(_ notification: UNNotification, completionHandler: @escaping (UNNotificationPresentationOptions) -> Void) -> Bool {
41
+ open func willPresent(_ notification: UNNotification, completionHandler: @escaping (UNNotificationPresentationOptions) -> Void) -> Bool {
42
42
  let task = SingleNotificationHandlerTask(notification: notification, completionHandler: completionHandler, delegate: self)
43
43
  tasksMap[task.identifier] = task
44
44
  task.start()
@@ -44,7 +44,7 @@ public class SingleNotificationHandlerTask {
44
44
  finish()
45
45
  }
46
46
 
47
- public func handleResponse(_ behavior: [String: Bool]) -> Bool {
47
+ public func processNotificationWithBehavior(_ behavior: [String: Bool]) -> Bool {
48
48
  if let completionHandler = completionHandler {
49
49
  let options = presentationOptions(behavior)
50
50
  completionHandler(options)
@@ -4,7 +4,7 @@ import ExpoModulesCore
4
4
  import UIKit
5
5
  import MachO
6
6
 
7
- public class PresentationModule: Module, NotificationDelegate {
7
+ open class PresentationModule: Module, NotificationDelegate {
8
8
  var presentedNotifications: Set<String> = []
9
9
 
10
10
  public func definition() -> ModuleDefinition {
@@ -18,53 +18,25 @@ public class PresentationModule: Module, NotificationDelegate {
18
18
  NotificationCenterManager.shared.removeDelegate(self)
19
19
  }
20
20
 
21
- AsyncFunction("presentNotificationAsync") { (identifier: String, notificationSpec: [String: Any], promise: Promise) in
22
- do {
23
- guard let appContext = appContext else {
24
- let error = NSError(domain: "ExpoNotificationPresenter", code: 0, userInfo: nil)
25
- promise.reject("ERR_NOTIF_PRESENT", error.localizedDescription)
26
- return
27
- }
28
- let requestContentRecord = try NotificationRequestContentRecord(from: notificationSpec, appContext: appContext)
29
- let content = requestContentRecord.toUNMutableNotificationContent()
30
- var request: UNNotificationRequest?
31
- try EXUtilities.catchException {
32
- request = UNNotificationRequest(identifier: identifier, content: content, trigger: nil)
33
- }
34
- guard let request = request else {
35
- promise.reject("ERR_NOTIF_PRESENT", "Notification could not be presented")
36
- return
37
- }
38
- presentedNotifications.insert(identifier)
39
- UNUserNotificationCenter.current().add(request) { error in
40
- if let error {
41
- promise.reject("ERR_NOTIF_PRESENT", error.localizedDescription)
42
- } else {
43
- promise.resolve()
44
- }
45
- }
46
- } catch {
47
- promise.reject("ERR_NOTIF_PRESENT", error.localizedDescription)
48
- }
21
+ AsyncFunction("presentNotificationAsync") { (identifier: String, notificationSpec: [String: Any]) in
22
+ try await presentNotificationAsync(identifier: identifier, notificationSpec: notificationSpec)
49
23
  }
50
- .runOnQueue(.main)
51
24
 
52
- AsyncFunction("getPresentedNotificationsAsync") { (promise: Promise) in
53
- UNUserNotificationCenter.current().getDeliveredNotifications { notifications in
54
- promise.resolve(self.serializeNotifications(notifications))
55
- }
25
+ AsyncFunction("getPresentedNotificationsAsync") {
26
+ let notifications = await UNUserNotificationCenter.current().deliveredNotifications()
27
+ return self.serializeNotifications(notifications)
56
28
  }
57
29
 
58
30
  AsyncFunction("dismissNotificationAsync") { (identifier: String) in
59
- UNUserNotificationCenter.current().removeDeliveredNotifications(withIdentifiers: [identifier])
31
+ removeDeliveredNotifications(identifier: identifier)
60
32
  }
61
33
 
62
34
  AsyncFunction("dismissAllNotificationsAsync") {
63
- UNUserNotificationCenter.current().removeAllDeliveredNotifications()
35
+ await removeAllDeliveredNotifications()
64
36
  }
65
37
  }
66
38
 
67
- public func willPresent(_ notification: UNNotification, completionHandler: @escaping (UNNotificationPresentationOptions) -> Void) -> Bool {
39
+ open func willPresent(_ notification: UNNotification, completionHandler: @escaping (UNNotificationPresentationOptions) -> Void) -> Bool {
68
40
  let identifier = notification.request.identifier
69
41
  if presentedNotifications.contains(identifier) {
70
42
  presentedNotifications.remove(identifier)
@@ -74,10 +46,40 @@ public class PresentationModule: Module, NotificationDelegate {
74
46
  return false
75
47
  }
76
48
 
77
- func serializeNotifications(_ notifications: [UNNotification]) -> [[AnyHashable: Any]] {
49
+ open func serializeNotifications(_ notifications: [UNNotification]) -> [[String: Any]] {
78
50
  return notifications.map { notification in
79
51
  // TODO: convert serialization to Records
80
52
  return EXNotificationSerializer.serializedNotification(notification)
81
53
  }
82
54
  }
55
+
56
+ open func removeDeliveredNotifications(identifier: String) {
57
+ UNUserNotificationCenter.current().removeDeliveredNotifications(withIdentifiers: [identifier])
58
+ }
59
+
60
+ open func removeAllDeliveredNotifications() async {
61
+ UNUserNotificationCenter.current().removeAllDeliveredNotifications()
62
+ }
63
+
64
+ open func presentNotificationAsync(identifier: String, notificationSpec: [String: Any]) async throws {
65
+ guard let appContext else {
66
+ let error = NSError(domain: "ExpoNotificationPresenter", code: 0, userInfo: nil)
67
+ throw Exception(name: "ERR_NOTIF_PRESENT", description: error.localizedDescription)
68
+ }
69
+ let requestContentRecord = try NotificationRequestContentRecord(from: notificationSpec, appContext: appContext)
70
+ let content = requestContentRecord.toUNMutableNotificationContent()
71
+ var request: UNNotificationRequest?
72
+ try EXUtilities.catchException {
73
+ request = UNNotificationRequest(identifier: identifier, content: content, trigger: nil)
74
+ }
75
+ guard let request else {
76
+ throw Exception(name: "ERR_NOTIF_PRESENT", description: "Notification could not be presented")
77
+ }
78
+ presentedNotifications.insert(identifier)
79
+ do {
80
+ try await UNUserNotificationCenter.current().add(request)
81
+ } catch {
82
+ throw Exception(name: "ERR_NOTIF_PRESENT", description: error.localizedDescription)
83
+ }
84
+ }
83
85
  }
@@ -4,11 +4,11 @@ import ExpoModulesCore
4
4
 
5
5
  // MARK: - NotificationBuilder record definitions
6
6
 
7
- protocol TriggerRecord: Record {
7
+ public protocol TriggerRecord: Record {
8
8
  func toUNNotificationTrigger() throws -> UNNotificationTrigger?
9
9
  }
10
10
 
11
- struct CalendarTriggerRecord: TriggerRecord {
11
+ public struct CalendarTriggerRecord: TriggerRecord {
12
12
  @Field
13
13
  var year: Int?
14
14
  @Field
@@ -47,6 +47,8 @@ struct CalendarTriggerRecord: TriggerRecord {
47
47
  "weekdayOrdinal": .weekdayOrdinal
48
48
  ]
49
49
 
50
+ public init() {}
51
+
50
52
  func dateComponentsFrom(_ calendarTrigger: CalendarTriggerRecord) -> DateComponents {
51
53
  var dateComponents = DateComponents()
52
54
  // TODO: Verify that DoW matches JS getDay()
@@ -63,7 +65,7 @@ struct CalendarTriggerRecord: TriggerRecord {
63
65
  return dateComponents
64
66
  }
65
67
 
66
- func toUNNotificationTrigger() throws -> UNNotificationTrigger? {
68
+ public func toUNNotificationTrigger() throws -> UNNotificationTrigger? {
67
69
  var trigger: UNNotificationTrigger?
68
70
  try EXUtilities.catchException {
69
71
  let dateComponents: DateComponents = dateComponentsFrom(self)
@@ -74,13 +76,15 @@ struct CalendarTriggerRecord: TriggerRecord {
74
76
  }
75
77
  }
76
78
 
77
- struct TimeIntervalTriggerRecord: TriggerRecord {
79
+ public struct TimeIntervalTriggerRecord: TriggerRecord {
78
80
  @Field
79
81
  var seconds: TimeInterval
80
82
  @Field
81
83
  var repeats: Bool
82
84
 
83
- func toUNNotificationTrigger() throws -> UNNotificationTrigger? {
85
+ public init() {}
86
+
87
+ public func toUNNotificationTrigger() throws -> UNNotificationTrigger? {
84
88
  var trigger: UNNotificationTrigger?
85
89
  try EXUtilities.catchException {
86
90
  trigger = UNTimeIntervalNotificationTrigger(timeInterval: self.seconds, repeats: self.repeats)
@@ -89,11 +93,13 @@ struct TimeIntervalTriggerRecord: TriggerRecord {
89
93
  }
90
94
  }
91
95
 
92
- struct DateTriggerRecord: TriggerRecord {
96
+ public struct DateTriggerRecord: TriggerRecord {
93
97
  @Field
94
98
  var timestamp: TimeInterval
95
99
 
96
- func toUNNotificationTrigger() throws -> UNNotificationTrigger? {
100
+ public init() {}
101
+
102
+ public func toUNNotificationTrigger() throws -> UNNotificationTrigger? {
97
103
  let timestamp: Int = Int(self.timestamp / 1000)
98
104
  let date: Date = Date(timeIntervalSince1970: TimeInterval(timestamp))
99
105
  var trigger: UNNotificationTrigger?
@@ -104,13 +110,15 @@ struct DateTriggerRecord: TriggerRecord {
104
110
  }
105
111
  }
106
112
 
107
- struct DailyTriggerRecord: TriggerRecord {
113
+ public struct DailyTriggerRecord: TriggerRecord {
108
114
  @Field
109
115
  var hour: Int
110
116
  @Field
111
117
  var minute: Int
112
118
 
113
- func toUNNotificationTrigger() throws -> UNNotificationTrigger? {
119
+ public init() {}
120
+
121
+ public func toUNNotificationTrigger() throws -> UNNotificationTrigger? {
114
122
  let dateComponents: DateComponents = DateComponents(hour: self.hour, minute: self.minute)
115
123
  var trigger: UNNotificationTrigger?
116
124
  try EXUtilities.catchException {
@@ -120,7 +128,7 @@ struct DailyTriggerRecord: TriggerRecord {
120
128
  }
121
129
  }
122
130
 
123
- struct WeeklyTriggerRecord: TriggerRecord {
131
+ public struct WeeklyTriggerRecord: TriggerRecord {
124
132
  @Field
125
133
  var weekday: Int
126
134
  @Field
@@ -128,7 +136,9 @@ struct WeeklyTriggerRecord: TriggerRecord {
128
136
  @Field
129
137
  var minute: Int
130
138
 
131
- func toUNNotificationTrigger() throws -> UNNotificationTrigger? {
139
+ public init() {}
140
+
141
+ public func toUNNotificationTrigger() throws -> UNNotificationTrigger? {
132
142
  let dateComponents: DateComponents = DateComponents(hour: self.hour, minute: self.minute, weekday: self.weekday)
133
143
  var trigger: UNNotificationTrigger?
134
144
  try EXUtilities.catchException {
@@ -137,7 +147,7 @@ struct WeeklyTriggerRecord: TriggerRecord {
137
147
  return trigger }
138
148
  }
139
149
 
140
- struct MonthlyTriggerRecord: TriggerRecord {
150
+ public struct MonthlyTriggerRecord: TriggerRecord {
141
151
  @Field
142
152
  var day: Int
143
153
  @Field
@@ -145,7 +155,9 @@ struct MonthlyTriggerRecord: TriggerRecord {
145
155
  @Field
146
156
  var minute: Int
147
157
 
148
- func toUNNotificationTrigger() throws -> UNNotificationTrigger? {
158
+ public init() {}
159
+
160
+ public func toUNNotificationTrigger() throws -> UNNotificationTrigger? {
149
161
  let dateComponents: DateComponents = DateComponents(day: self.day, hour: self.hour, minute: self.minute)
150
162
  var trigger: UNNotificationTrigger?
151
163
  try EXUtilities.catchException {
@@ -155,7 +167,7 @@ struct MonthlyTriggerRecord: TriggerRecord {
155
167
  }
156
168
  }
157
169
 
158
- struct YearlyTriggerRecord: TriggerRecord {
170
+ public struct YearlyTriggerRecord: TriggerRecord {
159
171
  @Field
160
172
  var month: Int
161
173
  @Field
@@ -165,7 +177,9 @@ struct YearlyTriggerRecord: TriggerRecord {
165
177
  @Field
166
178
  var minute: Int
167
179
 
168
- func toUNNotificationTrigger() throws -> UNNotificationTrigger? {
180
+ public init() {}
181
+
182
+ public func toUNNotificationTrigger() throws -> UNNotificationTrigger? {
169
183
  let dateComponents: DateComponents = DateComponents(
170
184
  month: self.month,
171
185
  day: self.day,
@@ -225,8 +239,8 @@ struct CategoryActionOptionsRecord: Record {
225
239
  }
226
240
  }
227
241
 
228
- struct CategoryActionRecord: Record {
229
- init() {}
242
+ public struct CategoryActionRecord: Record {
243
+ public init() {}
230
244
 
231
245
  @Field
232
246
  var identifier: String?
@@ -270,8 +284,8 @@ struct CategoryActionRecord: Record {
270
284
  }
271
285
  }
272
286
 
273
- struct CategoryOptionsRecord: Record {
274
- init() {}
287
+ public struct CategoryOptionsRecord: Record {
288
+ public init() {}
275
289
 
276
290
  // allowAnnouncement deprecated in iOS 15 and later
277
291
  /*
@@ -329,17 +343,17 @@ struct CategoryOptionsRecord: Record {
329
343
  }
330
344
  }
331
345
 
332
- struct CategoryRecord: Record {
333
- init() {}
346
+ public struct CategoryRecord: Record {
347
+ public init() {}
334
348
 
335
349
  @Field
336
- var identifier: String
350
+ public var identifier: String
337
351
  @Field
338
352
  var actions: [CategoryActionRecord]?
339
353
  @Field
340
354
  var options: CategoryOptionsRecord?
341
355
 
342
- init(_ category: UNNotificationCategory) {
356
+ public init(_ category: UNNotificationCategory) {
343
357
  self.identifier = category.identifier
344
358
  self.actions = category.actions.map { action in
345
359
  return CategoryActionRecord(action)
@@ -347,7 +361,7 @@ struct CategoryRecord: Record {
347
361
  self.options = CategoryOptionsRecord(category)
348
362
  }
349
363
 
350
- init(_ identifier: String, actions: [CategoryActionRecord], options: CategoryOptionsRecord?) {
364
+ public init(_ identifier: String, actions: [CategoryActionRecord], options: CategoryOptionsRecord?) {
351
365
  self.identifier = identifier
352
366
  self.actions = actions
353
367
  self.options = options
@@ -20,27 +20,21 @@ let calendarNotificationTriggerComponentsKey = "value"
20
20
  let calendarNotificationTriggerTimezoneKey = "timezone"
21
21
  // swiftlint:enable identifier_name
22
22
 
23
- public class SchedulerModule: Module {
23
+ open class SchedulerModule: Module {
24
24
  public func definition() -> ModuleDefinition {
25
25
  Name("ExpoNotificationScheduler")
26
26
 
27
- AsyncFunction("getAllScheduledNotificationsAsync") { (promise: Promise) in
28
- UNUserNotificationCenter.current().getPendingNotificationRequests { (requests: [UNNotificationRequest]) in
29
- var serializedRequests: [Any] = []
30
- requests.forEach {request in
31
- serializedRequests.append(EXNotificationSerializer.serializedNotificationRequest(request))
32
- }
33
- promise.resolve(serializedRequests)
34
- }
27
+ AsyncFunction("getAllScheduledNotificationsAsync") {
28
+ let requests = await UNUserNotificationCenter.current().pendingNotificationRequests()
29
+ return serializedNotificationRequests(requests)
35
30
  }
36
- .runOnQueue(.main)
37
31
 
38
32
  AsyncFunction("cancelScheduledNotificationAsync") { (identifier: String) in
39
- UNUserNotificationCenter.current().removePendingNotificationRequests(withIdentifiers: [identifier])
33
+ cancelScheduledNotification(identifier)
40
34
  }
41
35
 
42
36
  AsyncFunction("cancelAllScheduledNotificationsAsync") { () in
43
- UNUserNotificationCenter.current().removeAllPendingNotificationRequests()
37
+ cancelAllScheduledNotifications()
44
38
  }
45
39
 
46
40
  AsyncFunction("scheduleNotificationAsync") { (identifier: String, notificationSpec: [String: Any], triggerSpec: [String: Any]?, promise: Promise) in
@@ -125,15 +119,13 @@ public class SchedulerModule: Module {
125
119
  }
126
120
  }
127
121
 
128
- func serializeNotificationRequests(_ requests: [UNNotificationRequest]) -> [Any] {
129
- var serializedRequests: [[AnyHashable: Any]] = []
130
- requests.forEach {request in
131
- serializedRequests.append(EXNotificationSerializer .serializedNotificationRequest(request))
122
+ open func serializedNotificationRequests(_ requests: [UNNotificationRequest]) -> [[String: Any]] {
123
+ return requests.map {
124
+ EXNotificationSerializer.serializedNotificationRequest($0)
132
125
  }
133
- return serializedRequests
134
126
  }
135
127
 
136
- func buildNotificationRequest(
128
+ open func buildNotificationRequest(
137
129
  identifier: String,
138
130
  contentInput: [String: Any],
139
131
  triggerInput: [String: Any]?
@@ -148,4 +140,12 @@ public class SchedulerModule: Module {
148
140
  trigger: triggerFromParams(triggerInput, appContext: appContext)
149
141
  )
150
142
  }
143
+
144
+ open func cancelScheduledNotification(_ identifier: String) {
145
+ UNUserNotificationCenter.current().removePendingNotificationRequests(withIdentifiers: [identifier])
146
+ }
147
+
148
+ open func cancelAllScheduledNotifications() {
149
+ UNUserNotificationCenter.current().removeAllPendingNotificationRequests()
150
+ }
151
151
  }
@@ -24,7 +24,7 @@ public class PushTokenModule: Module, NotificationDelegate {
24
24
 
25
25
  AsyncFunction("getDevicePushTokenAsync") { (promise: Promise) in
26
26
  if promiseNotYetResolved != nil {
27
- promise.reject("E_AWAIT_PROMISE", "Another async call to this method is in progress. Await the first Promise.")
27
+ promise.reject("E_AWAIT_PROMISE", "Another async call to getDevicePushTokenAsync() is in progress. Await the first Promise.")
28
28
  return
29
29
  }
30
30
  promiseNotYetResolved = promise
@@ -4,7 +4,7 @@ import ExpoModulesCore
4
4
  import UIKit
5
5
  import MachO
6
6
 
7
- public class ServerRegistrationModule: Module {
7
+ open class ServerRegistrationModule: Module {
8
8
  public func definition() -> ModuleDefinition {
9
9
  Name("NotificationsServerRegistrationModule")
10
10
 
@@ -54,15 +54,15 @@ public class ServerRegistrationModule: Module {
54
54
  }
55
55
 
56
56
  private func getLegacyInstallationIdFromUserDefaults() -> String? {
57
- return UserDefaults.standard.string(forKey: kEXDeviceInstallationUUIDLegacyKey)
57
+ return UserDefaults.standard.string(forKey: ServerRegistrationModule.kEXDeviceInstallationUUIDLegacyKey)
58
58
  }
59
59
 
60
60
  private func removeLegacyInstallationIdFromUserDefaults() {
61
- UserDefaults.standard.removeObject(forKey: kEXDeviceInstallationUUIDLegacyKey)
61
+ UserDefaults.standard.removeObject(forKey: ServerRegistrationModule.kEXDeviceInstallationUUIDLegacyKey)
62
62
  }
63
63
 
64
64
  private func installationIdSearchQueryMerging(_ dictionaryToMerge: [AnyHashable: Any]) -> CFDictionary {
65
- return keychainSearchQueryFor(key: kEXDeviceInstallationUUIDKey, dictionaryToMerge: dictionaryToMerge)
65
+ return keychainSearchQueryFor(key: ServerRegistrationModule.kEXDeviceInstallationUUIDKey, dictionaryToMerge: dictionaryToMerge)
66
66
  }
67
67
 
68
68
  private func installationIdSearchQuery() -> CFDictionary {
@@ -93,8 +93,8 @@ public class ServerRegistrationModule: Module {
93
93
  return try storeStringWithQueries(search: registrationSearchQuery(), set: registrationSetQuery(registrationInfo))
94
94
  }
95
95
 
96
- private func registrationSearchQueryMerging(_ dictionaryToMerge: [AnyHashable: Any]) -> CFDictionary {
97
- return keychainSearchQueryFor(key: kEXRegistrationInfoKey, dictionaryToMerge: dictionaryToMerge)
96
+ open func registrationSearchQueryMerging(_ dictionaryToMerge: [AnyHashable: Any]) -> CFDictionary {
97
+ return keychainSearchQueryFor(key: ServerRegistrationModule.kEXRegistrationInfoKey, dictionaryToMerge: dictionaryToMerge)
98
98
  }
99
99
 
100
100
  private func registrationSearchQuery() -> CFDictionary {
@@ -117,7 +117,7 @@ public class ServerRegistrationModule: Module {
117
117
 
118
118
  // MARK: - Generic keychain methods
119
119
 
120
- private func keychainSearchQueryFor(key: String, dictionaryToMerge: [AnyHashable: Any]) -> CFDictionary {
120
+ public func keychainSearchQueryFor(key: String, dictionaryToMerge: [AnyHashable: Any]) -> CFDictionary {
121
121
  let encodedKey: Data = dataFromString(key)
122
122
  let bundleIdentifier = Bundle.main.bundleIdentifier ?? ""
123
123
  var query: [AnyHashable: Any] = [
@@ -172,8 +172,8 @@ public class ServerRegistrationModule: Module {
172
172
  return input.data(using: fastEncoding)!
173
173
  }
174
174
 
175
- private let kEXDeviceInstallationUUIDKey = "EXDeviceInstallationUUIDKey"
176
- private let kEXDeviceInstallationUUIDLegacyKey = "EXDeviceInstallationUUIDKey"
177
- private let kEXRegistrationInfoKey = "EXNotificationRegistrationInfoKey"
175
+ public static let kEXDeviceInstallationUUIDKey = "EXDeviceInstallationUUIDKey"
176
+ public static let kEXDeviceInstallationUUIDLegacyKey = "EXDeviceInstallationUUIDKey"
177
+ public static let kEXRegistrationInfoKey = "EXNotificationRegistrationInfoKey"
178
178
  private let CFTrue = true as CFBoolean
179
179
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "expo-notifications",
3
- "version": "0.29.15-canary-20250402-161f57b",
3
+ "version": "0.29.15-canary-20250404-42b6263",
4
4
  "description": "Provides an API to fetch push notification tokens and to present, schedule, receive, and respond to notifications.",
5
5
  "main": "build/index.js",
6
6
  "types": "build/index.d.ts",
@@ -42,22 +42,21 @@
42
42
  ]
43
43
  },
44
44
  "dependencies": {
45
- "@expo/image-utils": "0.6.6-canary-20250402-161f57b",
45
+ "@expo/image-utils": "0.6.6-canary-20250404-42b6263",
46
46
  "@ide/backoff": "^1.0.0",
47
47
  "abort-controller": "^3.0.0",
48
48
  "assert": "^2.0.0",
49
49
  "badgin": "^1.1.5",
50
- "expo-application": "6.0.3-canary-20250402-161f57b",
51
- "expo-constants": "18.0.0-canary-20250402-161f57b"
50
+ "expo-application": "6.0.3-canary-20250404-42b6263",
51
+ "expo-constants": "18.0.0-canary-20250404-42b6263"
52
52
  },
53
53
  "devDependencies": {
54
- "expo-module-scripts": "4.0.5-canary-20250402-161f57b",
54
+ "expo-module-scripts": "4.0.6-canary-20250404-42b6263",
55
55
  "memfs": "^3.2.0"
56
56
  },
57
57
  "peerDependencies": {
58
- "expo": "53.0.0-canary-20250402-161f57b",
58
+ "expo": "53.0.0-canary-20250404-42b6263",
59
59
  "react": "*",
60
60
  "react-native": "*"
61
- },
62
- "gitHead": "161f57bb5f579c84f8fa0337ec596034e21760f6"
61
+ }
63
62
  }