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 +2 -0
- package/android/build.gradle +2 -2
- package/expo-module.config.json +1 -7
- package/ios/EXNotifications/Notifications/Categories/CategoriesModule.swift +40 -31
- package/ios/EXNotifications/Notifications/Emitter/EmitterModule.swift +19 -15
- package/ios/EXNotifications/Notifications/Handler/HandlerModule.swift +3 -3
- package/ios/EXNotifications/Notifications/Handler/SingleNotificationHandlerTask.swift +1 -1
- package/ios/EXNotifications/Notifications/Presenting/PresentationModule.swift +40 -38
- package/ios/EXNotifications/Notifications/Records.swift +38 -24
- package/ios/EXNotifications/Notifications/Scheduling/SchedulerModule.swift +18 -18
- package/ios/EXNotifications/PushToken/PushTokenModule.swift +1 -1
- package/ios/EXNotifications/ServerRegistration/ServerRegistrationModule.swift +10 -10
- package/package.json +7 -8
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
|
|
package/android/build.gradle
CHANGED
|
@@ -5,13 +5,13 @@ plugins {
|
|
|
5
5
|
}
|
|
6
6
|
|
|
7
7
|
group = 'host.exp.exponent'
|
|
8
|
-
version = '0.29.
|
|
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.
|
|
14
|
+
versionName '0.29.8'
|
|
15
15
|
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
|
|
16
16
|
}
|
|
17
17
|
|
package/expo-module.config.json
CHANGED
|
@@ -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
|
-
|
|
7
|
+
open class CategoriesModule: Module {
|
|
8
8
|
public func definition() -> ModuleDefinition {
|
|
9
9
|
Name("ExpoNotificationCategoriesModule")
|
|
10
10
|
|
|
11
|
-
AsyncFunction("getNotificationCategoriesAsync") {
|
|
12
|
-
UNUserNotificationCenter.current().
|
|
13
|
-
|
|
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
|
|
21
|
-
|
|
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
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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") {(
|
|
25
|
+
AsyncFunction("getLastNotificationResponseAsync") {() -> [String: Any]? in
|
|
26
26
|
if let lastResponse: UNNotificationResponse = NotificationCenterManager.shared.lastResponse {
|
|
27
|
-
|
|
27
|
+
return EXNotificationSerializer.serializedNotificationResponse(lastResponse)
|
|
28
28
|
}
|
|
29
|
-
|
|
29
|
+
return nil
|
|
30
30
|
}
|
|
31
31
|
|
|
32
|
-
AsyncFunction("clearLastNotificationResponseAsync") {
|
|
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
|
-
|
|
42
|
+
open func didReceive(_ response: UNNotificationResponse, completionHandler: @escaping () -> Void) -> Bool {
|
|
44
43
|
NotificationCenterManager.shared.lastResponse = response
|
|
45
|
-
|
|
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
|
-
|
|
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
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
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
|
-
|
|
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.
|
|
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
|
-
|
|
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
|
|
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
|
-
|
|
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]
|
|
22
|
-
|
|
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") {
|
|
53
|
-
UNUserNotificationCenter.current().
|
|
54
|
-
|
|
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
|
-
|
|
31
|
+
removeDeliveredNotifications(identifier: identifier)
|
|
60
32
|
}
|
|
61
33
|
|
|
62
34
|
AsyncFunction("dismissAllNotificationsAsync") {
|
|
63
|
-
|
|
35
|
+
await removeAllDeliveredNotifications()
|
|
64
36
|
}
|
|
65
37
|
}
|
|
66
38
|
|
|
67
|
-
|
|
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]) -> [[
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
23
|
+
open class SchedulerModule: Module {
|
|
24
24
|
public func definition() -> ModuleDefinition {
|
|
25
25
|
Name("ExpoNotificationScheduler")
|
|
26
26
|
|
|
27
|
-
AsyncFunction("getAllScheduledNotificationsAsync") {
|
|
28
|
-
UNUserNotificationCenter.current().
|
|
29
|
-
|
|
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
|
-
|
|
33
|
+
cancelScheduledNotification(identifier)
|
|
40
34
|
}
|
|
41
35
|
|
|
42
36
|
AsyncFunction("cancelAllScheduledNotificationsAsync") { () in
|
|
43
|
-
|
|
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
|
|
129
|
-
|
|
130
|
-
|
|
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
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
176
|
-
|
|
177
|
-
|
|
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-
|
|
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-
|
|
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-
|
|
51
|
-
"expo-constants": "18.0.0-canary-
|
|
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.
|
|
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-
|
|
58
|
+
"expo": "53.0.0-canary-20250404-42b6263",
|
|
59
59
|
"react": "*",
|
|
60
60
|
"react-native": "*"
|
|
61
|
-
}
|
|
62
|
-
"gitHead": "161f57bb5f579c84f8fa0337ec596034e21760f6"
|
|
61
|
+
}
|
|
63
62
|
}
|