@psync/notifee 9.2.0

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.
Files changed (193) hide show
  1. package/LICENSE +44 -0
  2. package/README.md +142 -0
  3. package/RNNotifee.podspec +39 -0
  4. package/RNNotifeeCore.podspec +33 -0
  5. package/android/.editorconfig +10 -0
  6. package/android/build.gradle +116 -0
  7. package/android/gradle.properties +1 -0
  8. package/android/lint.xml +5 -0
  9. package/android/proguard-rules.pro +82 -0
  10. package/android/settings.gradle +1 -0
  11. package/android/src/main/AndroidManifest.xml +12 -0
  12. package/android/src/main/java/io/invertase/notifee/HeadlessTask.java +431 -0
  13. package/android/src/main/java/io/invertase/notifee/NotifeeApiModule.java +365 -0
  14. package/android/src/main/java/io/invertase/notifee/NotifeeEventSubscriber.java +118 -0
  15. package/android/src/main/java/io/invertase/notifee/NotifeeInitProvider.java +55 -0
  16. package/android/src/main/java/io/invertase/notifee/NotifeePackage.java +30 -0
  17. package/android/src/main/java/io/invertase/notifee/NotifeeReactUtils.java +135 -0
  18. package/dist/NotifeeApiModule.d.ts +53 -0
  19. package/dist/NotifeeApiModule.js +609 -0
  20. package/dist/NotifeeApiModule.js.map +1 -0
  21. package/dist/NotifeeJSEventEmitter.d.ts +3 -0
  22. package/dist/NotifeeJSEventEmitter.js +14 -0
  23. package/dist/NotifeeJSEventEmitter.js.map +1 -0
  24. package/dist/NotifeeNativeError.d.ts +15 -0
  25. package/dist/NotifeeNativeError.js +59 -0
  26. package/dist/NotifeeNativeError.js.map +1 -0
  27. package/dist/NotifeeNativeModule.d.ts +14 -0
  28. package/dist/NotifeeNativeModule.js +42 -0
  29. package/dist/NotifeeNativeModule.js.map +1 -0
  30. package/dist/NotifeeNativeModule.web.d.ts +12 -0
  31. package/dist/NotifeeNativeModule.web.js +18 -0
  32. package/dist/NotifeeNativeModule.web.js.map +1 -0
  33. package/dist/index.d.ts +8 -0
  34. package/dist/index.js +43 -0
  35. package/dist/index.js.map +1 -0
  36. package/dist/types/Library.d.ts +21 -0
  37. package/dist/types/Library.js +6 -0
  38. package/dist/types/Library.js.map +1 -0
  39. package/dist/types/Module.d.ts +576 -0
  40. package/dist/types/Module.js +6 -0
  41. package/dist/types/Module.js.map +1 -0
  42. package/dist/types/Notification.d.ts +462 -0
  43. package/dist/types/Notification.js +111 -0
  44. package/dist/types/Notification.js.map +1 -0
  45. package/dist/types/NotificationAndroid.d.ts +1311 -0
  46. package/dist/types/NotificationAndroid.js +409 -0
  47. package/dist/types/NotificationAndroid.js.map +1 -0
  48. package/dist/types/NotificationIOS.d.ts +539 -0
  49. package/dist/types/NotificationIOS.js +92 -0
  50. package/dist/types/NotificationIOS.js.map +1 -0
  51. package/dist/types/NotificationWeb.d.ts +5 -0
  52. package/dist/types/NotificationWeb.js +6 -0
  53. package/dist/types/NotificationWeb.js.map +1 -0
  54. package/dist/types/PowerManagerInfo.d.ts +36 -0
  55. package/dist/types/PowerManagerInfo.js +6 -0
  56. package/dist/types/PowerManagerInfo.js.map +1 -0
  57. package/dist/types/Trigger.d.ts +124 -0
  58. package/dist/types/Trigger.js +51 -0
  59. package/dist/types/Trigger.js.map +1 -0
  60. package/dist/utils/id.d.ts +1 -0
  61. package/dist/utils/id.js +15 -0
  62. package/dist/utils/id.js.map +1 -0
  63. package/dist/utils/index.d.ts +16 -0
  64. package/dist/utils/index.js +51 -0
  65. package/dist/utils/index.js.map +1 -0
  66. package/dist/utils/validate.d.ts +15 -0
  67. package/dist/utils/validate.js +90 -0
  68. package/dist/utils/validate.js.map +1 -0
  69. package/dist/validators/iosCommunicationInfo/validateIOSCommunicationInfo.d.ts +2 -0
  70. package/dist/validators/iosCommunicationInfo/validateIOSCommunicationInfo.js +51 -0
  71. package/dist/validators/iosCommunicationInfo/validateIOSCommunicationInfo.js.map +1 -0
  72. package/dist/validators/iosCommunicationInfo/validateIOSCommunicationInfoPerson.d.ts +2 -0
  73. package/dist/validators/iosCommunicationInfo/validateIOSCommunicationInfoPerson.js +27 -0
  74. package/dist/validators/iosCommunicationInfo/validateIOSCommunicationInfoPerson.js.map +1 -0
  75. package/dist/validators/validate.d.ts +25 -0
  76. package/dist/validators/validate.js +74 -0
  77. package/dist/validators/validate.js.map +1 -0
  78. package/dist/validators/validateAndroidAction.d.ts +2 -0
  79. package/dist/validators/validateAndroidAction.js +52 -0
  80. package/dist/validators/validateAndroidAction.js.map +1 -0
  81. package/dist/validators/validateAndroidChannel.d.ts +2 -0
  82. package/dist/validators/validateAndroidChannel.js +155 -0
  83. package/dist/validators/validateAndroidChannel.js.map +1 -0
  84. package/dist/validators/validateAndroidChannelGroup.d.ts +2 -0
  85. package/dist/validators/validateAndroidChannelGroup.js +42 -0
  86. package/dist/validators/validateAndroidChannelGroup.js.map +1 -0
  87. package/dist/validators/validateAndroidFullScreenAction.d.ts +2 -0
  88. package/dist/validators/validateAndroidFullScreenAction.js +50 -0
  89. package/dist/validators/validateAndroidFullScreenAction.js.map +1 -0
  90. package/dist/validators/validateAndroidInput.d.ts +2 -0
  91. package/dist/validators/validateAndroidInput.js +54 -0
  92. package/dist/validators/validateAndroidInput.js.map +1 -0
  93. package/dist/validators/validateAndroidNotification.d.ts +2 -0
  94. package/dist/validators/validateAndroidNotification.js +547 -0
  95. package/dist/validators/validateAndroidNotification.js.map +1 -0
  96. package/dist/validators/validateAndroidPressAction.d.ts +2 -0
  97. package/dist/validators/validateAndroidPressAction.js +50 -0
  98. package/dist/validators/validateAndroidPressAction.js.map +1 -0
  99. package/dist/validators/validateAndroidStyle.d.ts +22 -0
  100. package/dist/validators/validateAndroidStyle.js +230 -0
  101. package/dist/validators/validateAndroidStyle.js.map +1 -0
  102. package/dist/validators/validateIOSAttachment.d.ts +6 -0
  103. package/dist/validators/validateIOSAttachment.js +100 -0
  104. package/dist/validators/validateIOSAttachment.js.map +1 -0
  105. package/dist/validators/validateIOSCategory.d.ts +2 -0
  106. package/dist/validators/validateIOSCategory.js +124 -0
  107. package/dist/validators/validateIOSCategory.js.map +1 -0
  108. package/dist/validators/validateIOSCategoryAction.d.ts +2 -0
  109. package/dist/validators/validateIOSCategoryAction.js +59 -0
  110. package/dist/validators/validateIOSCategoryAction.js.map +1 -0
  111. package/dist/validators/validateIOSInput.d.ts +2 -0
  112. package/dist/validators/validateIOSInput.js +32 -0
  113. package/dist/validators/validateIOSInput.js.map +1 -0
  114. package/dist/validators/validateIOSNotification.d.ts +2 -0
  115. package/dist/validators/validateIOSNotification.js +211 -0
  116. package/dist/validators/validateIOSNotification.js.map +1 -0
  117. package/dist/validators/validateIOSPermissions.d.ts +2 -0
  118. package/dist/validators/validateIOSPermissions.js +62 -0
  119. package/dist/validators/validateIOSPermissions.js.map +1 -0
  120. package/dist/validators/validateNotification.d.ts +11 -0
  121. package/dist/validators/validateNotification.js +129 -0
  122. package/dist/validators/validateNotification.js.map +1 -0
  123. package/dist/validators/validateTrigger.d.ts +2 -0
  124. package/dist/validators/validateTrigger.js +110 -0
  125. package/dist/validators/validateTrigger.js.map +1 -0
  126. package/dist/version.d.ts +1 -0
  127. package/dist/version.js +6 -0
  128. package/dist/version.js.map +1 -0
  129. package/ios/NotifeeCore/Info.plist +22 -0
  130. package/ios/NotifeeCore/NotifeeCore+NSNotificationCenter.h +28 -0
  131. package/ios/NotifeeCore/NotifeeCore+NSNotificationCenter.m +76 -0
  132. package/ios/NotifeeCore/NotifeeCore+NSURLSession.h +25 -0
  133. package/ios/NotifeeCore/NotifeeCore+NSURLSession.m +55 -0
  134. package/ios/NotifeeCore/NotifeeCore+UNUserNotificationCenter.h +44 -0
  135. package/ios/NotifeeCore/NotifeeCore+UNUserNotificationCenter.m +270 -0
  136. package/ios/NotifeeCore/NotifeeCore.h +118 -0
  137. package/ios/NotifeeCore/NotifeeCore.m +843 -0
  138. package/ios/NotifeeCore/NotifeeCoreDelegateHolder.h +34 -0
  139. package/ios/NotifeeCore/NotifeeCoreDelegateHolder.m +70 -0
  140. package/ios/NotifeeCore/NotifeeCoreDownloadDelegate.h +39 -0
  141. package/ios/NotifeeCore/NotifeeCoreDownloadDelegate.m +68 -0
  142. package/ios/NotifeeCore/NotifeeCoreExtensionHelper.h +38 -0
  143. package/ios/NotifeeCore/NotifeeCoreExtensionHelper.m +224 -0
  144. package/ios/NotifeeCore/NotifeeCoreUtil.h +81 -0
  145. package/ios/NotifeeCore/NotifeeCoreUtil.m +834 -0
  146. package/ios/RNNotifee/NotifeeApiModule.h +25 -0
  147. package/ios/RNNotifee/NotifeeApiModule.m +371 -0
  148. package/ios/RNNotifee/NotifeeExtensionHelper.h +37 -0
  149. package/ios/RNNotifee/NotifeeExtensionHelper.m +37 -0
  150. package/ios/RNNotifee.xcodeproj/project.pbxproj +318 -0
  151. package/ios/RNNotifee.xcodeproj/project.xcworkspace/contents.xcworkspacedata +7 -0
  152. package/ios/RNNotifee.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist +8 -0
  153. package/jest-mock.js +161 -0
  154. package/package.json +74 -0
  155. package/react-native.config.js +11 -0
  156. package/src/NotifeeApiModule.ts +827 -0
  157. package/src/NotifeeJSEventEmitter.ts +10 -0
  158. package/src/NotifeeNativeError.ts +68 -0
  159. package/src/NotifeeNativeModule.ts +54 -0
  160. package/src/NotifeeNativeModule.web.ts +24 -0
  161. package/src/index.ts +33 -0
  162. package/src/types/Library.ts +28 -0
  163. package/src/types/Module.ts +629 -0
  164. package/src/types/Notification.ts +518 -0
  165. package/src/types/NotificationAndroid.ts +1478 -0
  166. package/src/types/NotificationIOS.ts +683 -0
  167. package/src/types/NotificationWeb.ts +5 -0
  168. package/src/types/PowerManagerInfo.ts +43 -0
  169. package/src/types/Trigger.ts +136 -0
  170. package/src/utils/id.ts +13 -0
  171. package/src/utils/index.ts +46 -0
  172. package/src/utils/validate.ts +90 -0
  173. package/src/validators/iosCommunicationInfo/validateIOSCommunicationInfo.ts +61 -0
  174. package/src/validators/iosCommunicationInfo/validateIOSCommunicationInfoPerson.ts +33 -0
  175. package/src/validators/validate.ts +85 -0
  176. package/src/validators/validateAndroidAction.ts +54 -0
  177. package/src/validators/validateAndroidChannel.ts +188 -0
  178. package/src/validators/validateAndroidChannelGroup.ts +49 -0
  179. package/src/validators/validateAndroidFullScreenAction.ts +65 -0
  180. package/src/validators/validateAndroidInput.ts +67 -0
  181. package/src/validators/validateAndroidNotification.ts +734 -0
  182. package/src/validators/validateAndroidPressAction.ts +65 -0
  183. package/src/validators/validateAndroidStyle.ts +315 -0
  184. package/src/validators/validateIOSAttachment.ts +135 -0
  185. package/src/validators/validateIOSCategory.ts +150 -0
  186. package/src/validators/validateIOSCategoryAction.ts +65 -0
  187. package/src/validators/validateIOSInput.ts +38 -0
  188. package/src/validators/validateIOSNotification.ts +296 -0
  189. package/src/validators/validateIOSPermissions.ts +78 -0
  190. package/src/validators/validateNotification.ts +156 -0
  191. package/src/validators/validateTrigger.ts +142 -0
  192. package/src/version.ts +2 -0
  193. package/tsconfig.json +23 -0
@@ -0,0 +1,843 @@
1
+ /**
2
+ * Copyright (c) 2016-present Invertase Limited & Contributors
3
+ *
4
+ * Licensed under the Apache License, Version 2.0 (the "License");
5
+ * you may not use this library except in compliance with the License.
6
+ * You may obtain a copy of the License at
7
+ *
8
+ * http://www.apache.org/licenses/LICENSE-2.0
9
+ *
10
+ * Unless required by applicable law or agreed to in writing, software
11
+ * distributed under the License is distributed on an "AS IS" BASIS,
12
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
+ * See the License for the specific language governing permissions and
14
+ * limitations under the License.
15
+ *
16
+ */
17
+
18
+ #import <UIKit/UIKit.h>
19
+ #import <dispatch/dispatch.h>
20
+
21
+ #import "Intents/Intents.h"
22
+ #import "NotifeeCore+UNUserNotificationCenter.h"
23
+ #import "NotifeeCore.h"
24
+ #import "NotifeeCoreDelegateHolder.h"
25
+ #import "NotifeeCoreExtensionHelper.h"
26
+ #import "NotifeeCoreUtil.h"
27
+
28
+ @implementation NotifeeCore
29
+
30
+ #pragma mark - Library Methods
31
+
32
+ + (void)setCoreDelegate:(id<NotifeeCoreDelegate>)coreDelegate {
33
+ [NotifeeCoreDelegateHolder instance].delegate = coreDelegate;
34
+ }
35
+
36
+ /**
37
+ * Cancel a currently displayed or pending trigger notification.
38
+ *
39
+ * @param notificationId NSString id of the notification to cancel
40
+ * @param block notifeeMethodVoidBlock
41
+ */
42
+ + (void)cancelNotification:(NSString *)notificationId
43
+ withNotificationType:(NSInteger)notificationType
44
+ withBlock:(notifeeMethodVoidBlock)block {
45
+ UNUserNotificationCenter *center = [UNUserNotificationCenter currentNotificationCenter];
46
+ // cancel displayed notification
47
+ if (notificationType == NotifeeCoreNotificationTypeDisplayed ||
48
+ notificationType == NotifeeCoreNotificationTypeAll)
49
+ [center removeDeliveredNotificationsWithIdentifiers:@[ notificationId ]];
50
+
51
+ // cancel trigger notification
52
+ if (notificationType == NotifeeCoreNotificationTypeTrigger ||
53
+ notificationType == NotifeeCoreNotificationTypeAll)
54
+ [center removePendingNotificationRequestsWithIdentifiers:@[ notificationId ]];
55
+ block(nil);
56
+ }
57
+
58
+ /**
59
+ * Cancel all currently displayed or pending trigger notifications.
60
+ *
61
+ * @param notificationType NSInteger
62
+ * @param block notifeeMethodVoidBlock
63
+ */
64
+ + (void)cancelAllNotifications:(NSInteger)notificationType withBlock:(notifeeMethodVoidBlock)block {
65
+ UNUserNotificationCenter *center = [UNUserNotificationCenter currentNotificationCenter];
66
+
67
+ // cancel displayed notifications
68
+ if (notificationType == NotifeeCoreNotificationTypeDisplayed ||
69
+ notificationType == NotifeeCoreNotificationTypeAll)
70
+ [center removeAllDeliveredNotifications];
71
+
72
+ // cancel trigger notifications
73
+ if (notificationType == NotifeeCoreNotificationTypeTrigger ||
74
+ notificationType == NotifeeCoreNotificationTypeAll)
75
+ [center removeAllPendingNotificationRequests];
76
+ block(nil);
77
+ }
78
+
79
+ /**
80
+ * Cancel currently displayed or pending trigger notifications by ids.
81
+ *
82
+ * @param notificationType NSInteger
83
+ * @param ids NSInteger
84
+ * @param block notifeeMethodVoidBlock
85
+ */
86
+ + (void)cancelAllNotificationsWithIds:(NSInteger)notificationType
87
+ withIds:(NSArray<NSString *> *)ids
88
+ withBlock:(notifeeMethodVoidBlock)block {
89
+ UNUserNotificationCenter *center = [UNUserNotificationCenter currentNotificationCenter];
90
+
91
+ // cancel displayed notifications
92
+ if (notificationType == NotifeeCoreNotificationTypeDisplayed ||
93
+ notificationType == NotifeeCoreNotificationTypeAll)
94
+ [center removeDeliveredNotificationsWithIdentifiers:ids];
95
+
96
+ // cancel trigger notifications
97
+ if (notificationType == NotifeeCoreNotificationTypeTrigger ||
98
+ notificationType == NotifeeCoreNotificationTypeAll)
99
+ [center removePendingNotificationRequestsWithIdentifiers:ids];
100
+ block(nil);
101
+ }
102
+
103
+ + (void)getDisplayedNotifications:(notifeeMethodNSArrayBlock)block {
104
+ UNUserNotificationCenter *center = [UNUserNotificationCenter currentNotificationCenter];
105
+ NSMutableArray *triggerNotifications = [[NSMutableArray alloc] init];
106
+ [center getDeliveredNotificationsWithCompletionHandler:^(
107
+ NSArray<UNNotification *> *_Nonnull deliveredNotifications) {
108
+ for (UNNotification *deliveredNotification in deliveredNotifications) {
109
+ NSMutableDictionary *triggerNotification = [NSMutableDictionary dictionary];
110
+ triggerNotification[@"id"] = deliveredNotification.request.identifier;
111
+
112
+ triggerNotification[@"date"] =
113
+ [NotifeeCoreUtil convertToTimestamp:deliveredNotification.date];
114
+ triggerNotification[@"notification"] =
115
+ deliveredNotification.request.content.userInfo[kNotifeeUserInfoNotification];
116
+ triggerNotification[@"trigger"] =
117
+ deliveredNotification.request.content.userInfo[kNotifeeUserInfoTrigger];
118
+
119
+ if (triggerNotification[@"notification"] == nil) {
120
+ // parse remote notification
121
+ triggerNotification[@"notification"] =
122
+ [NotifeeCoreUtil parseUNNotificationRequest:deliveredNotification.request];
123
+ }
124
+
125
+ [triggerNotifications addObject:triggerNotification];
126
+ }
127
+ block(nil, triggerNotifications);
128
+ }];
129
+ }
130
+
131
+ + (void)getTriggerNotifications:(notifeeMethodNSArrayBlock)block {
132
+ UNUserNotificationCenter *center = [UNUserNotificationCenter currentNotificationCenter];
133
+
134
+ [center getPendingNotificationRequestsWithCompletionHandler:^(
135
+ NSArray<UNNotificationRequest *> *_Nonnull requests) {
136
+ NSMutableArray *triggerNotifications = [[NSMutableArray alloc] init];
137
+
138
+ for (UNNotificationRequest *request in requests) {
139
+ NSMutableDictionary *triggerNotification = [NSMutableDictionary dictionary];
140
+
141
+ triggerNotification[@"notification"] = request.content.userInfo[kNotifeeUserInfoNotification];
142
+ triggerNotification[@"trigger"] = request.content.userInfo[kNotifeeUserInfoTrigger];
143
+
144
+ [triggerNotifications addObject:triggerNotification];
145
+ }
146
+
147
+ block(nil, triggerNotifications);
148
+ }];
149
+ }
150
+
151
+ /**
152
+ * Retrieve a NSArray of pending UNNotificationRequest for the application.
153
+ * Resolves a NSArray of UNNotificationRequest identifiers.
154
+ *
155
+ * @param block notifeeMethodNSArrayBlock
156
+ */
157
+ + (void)getTriggerNotificationIds:(notifeeMethodNSArrayBlock)block {
158
+ UNUserNotificationCenter *center = [UNUserNotificationCenter currentNotificationCenter];
159
+ [center getPendingNotificationRequestsWithCompletionHandler:^(
160
+ NSArray<UNNotificationRequest *> *_Nonnull requests) {
161
+ NSMutableArray<NSString *> *idsArray = [[NSMutableArray alloc] init];
162
+
163
+ for (UNNotificationRequest *request in requests) {
164
+ NSString *notificationId = request.identifier;
165
+ [idsArray addObject:notificationId];
166
+ }
167
+
168
+ block(nil, idsArray);
169
+ }];
170
+ }
171
+
172
+ /**
173
+ * Display a local notification immediately.
174
+ *
175
+ * @param notification NSDictionary representation of
176
+ * UNMutableNotificationContent
177
+ * @param block notifeeMethodVoidBlock
178
+ */
179
+ + (void)displayNotification:(NSDictionary *)notification withBlock:(notifeeMethodVoidBlock)block {
180
+ dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
181
+ UNMutableNotificationContent *content = [self buildNotificationContent:notification
182
+ withTrigger:nil];
183
+
184
+ UNUserNotificationCenter *center = [UNUserNotificationCenter currentNotificationCenter];
185
+
186
+ NSMutableDictionary *notificationDetail = [notification mutableCopy];
187
+ notificationDetail[@"remote"] = @NO;
188
+
189
+ if (@available(iOS 15.0, *)) {
190
+ if (notification[@"ios"][@"communicationInfo"] != nil) {
191
+ INSendMessageIntent *intent = [NotifeeCoreUtil
192
+ generateSenderIntentForCommunicationNotification:notification[@"ios"]
193
+ [@"communicationInfo"]];
194
+
195
+ // Use the intent to initialize the interaction.
196
+ INInteraction *interaction = [[INInteraction alloc] initWithIntent:intent response:nil];
197
+ interaction.direction = INInteractionDirectionIncoming;
198
+ [interaction donateInteractionWithCompletion:^(NSError *error) {
199
+ if (error)
200
+ NSLog(@"NotifeeCore: Could not donate interaction for communication notification: %@",
201
+ error);
202
+ }];
203
+
204
+ content = [[content contentByUpdatingWithProvider:intent error:nil] mutableCopy];
205
+ }
206
+ }
207
+
208
+ UNNotificationRequest *request =
209
+ [UNNotificationRequest requestWithIdentifier:notification[@"id"]
210
+ content:content
211
+ trigger:nil];
212
+
213
+ dispatch_async(dispatch_get_main_queue(), ^{
214
+ [center addNotificationRequest:request
215
+ withCompletionHandler:^(NSError *error) {
216
+ if (error == nil) {
217
+ [[NotifeeCoreDelegateHolder instance] didReceiveNotifeeCoreEvent:@{
218
+ @"type" : @(NotifeeCoreEventTypeDelivered),
219
+ @"detail" : @{
220
+ @"notification" : notificationDetail,
221
+ }
222
+ }];
223
+ }
224
+ block(error);
225
+ }];
226
+ });
227
+ });
228
+ }
229
+
230
+ /* Create a trigger notification .
231
+ *
232
+ * @param notification NSDictionary representation of
233
+ * UNMutableNotificationContent
234
+ * @param block notifeeMethodVoidBlock
235
+ */
236
+ + (void)createTriggerNotification:(NSDictionary *)notification
237
+ withTrigger:(NSDictionary *)trigger
238
+ withBlock:(notifeeMethodVoidBlock)block {
239
+ dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
240
+ UNMutableNotificationContent *content = [self buildNotificationContent:notification
241
+ withTrigger:trigger];
242
+ UNNotificationTrigger *unTrigger = [NotifeeCoreUtil triggerFromDictionary:trigger];
243
+
244
+ if (unTrigger == nil) {
245
+ // do nothing if trigger is null
246
+ return dispatch_async(dispatch_get_main_queue(), ^{
247
+ block(nil);
248
+ });
249
+ }
250
+
251
+ UNUserNotificationCenter *center = [UNUserNotificationCenter currentNotificationCenter];
252
+
253
+ NSMutableDictionary *notificationDetail = [notification mutableCopy];
254
+ notificationDetail[@"remote"] = @NO;
255
+
256
+ if (@available(iOS 15.0, *)) {
257
+ if (notification[@"ios"][@"communicationInfo"] != nil) {
258
+ INSendMessageIntent *intent = [NotifeeCoreUtil
259
+ generateSenderIntentForCommunicationNotification:notification[@"ios"]
260
+ [@"communicationInfo"]];
261
+
262
+ // Use the intent to initialize the interaction.
263
+ INInteraction *interaction = [[INInteraction alloc] initWithIntent:intent response:nil];
264
+ interaction.direction = INInteractionDirectionIncoming;
265
+ [interaction donateInteractionWithCompletion:^(NSError *error) {
266
+ if (error)
267
+ NSLog(@"NotifeeCore: Could not donate interaction for communication notification: %@",
268
+ error);
269
+ }];
270
+
271
+ content = [[content contentByUpdatingWithProvider:intent error:nil] mutableCopy];
272
+ }
273
+ }
274
+
275
+ UNNotificationRequest *request =
276
+ [UNNotificationRequest requestWithIdentifier:notification[@"id"]
277
+ content:content
278
+ trigger:unTrigger];
279
+
280
+ dispatch_async(dispatch_get_main_queue(), ^{
281
+ [center addNotificationRequest:request
282
+ withCompletionHandler:^(NSError *error) {
283
+ if (error == nil) {
284
+ [[NotifeeCoreDelegateHolder instance] didReceiveNotifeeCoreEvent:@{
285
+ @"type" : @(NotifeeCoreEventTypeTriggerNotificationCreated),
286
+ @"detail" : @{
287
+ @"notification" : notificationDetail,
288
+ }
289
+ }];
290
+ }
291
+ block(error);
292
+ }];
293
+ });
294
+ });
295
+ }
296
+
297
+ /**
298
+ * Builds a UNMutableNotificationContent from a NSDictionary.
299
+ *
300
+ * @param notification NSDictionary representation of UNNotificationContent
301
+ */
302
+
303
+ + (UNMutableNotificationContent *)buildNotificationContent:(NSDictionary *)notification
304
+ withTrigger:(NSDictionary *)trigger {
305
+ NSDictionary *iosDict = notification[@"ios"];
306
+ UNMutableNotificationContent *content = [[UNMutableNotificationContent alloc] init];
307
+
308
+ // title
309
+ if (notification[@"title"] != nil) {
310
+ content.title = notification[@"title"];
311
+ }
312
+
313
+ // subtitle
314
+ if (notification[@"subtitle"] != nil) {
315
+ content.subtitle = notification[@"subtitle"];
316
+ }
317
+
318
+ // body
319
+ if (notification[@"body"] != nil) {
320
+ content.body = notification[@"body"];
321
+ }
322
+
323
+ NSMutableDictionary *userInfo = [[NSMutableDictionary alloc] init];
324
+
325
+ // data
326
+ if (notification[@"data"] != nil) {
327
+ userInfo = [notification[@"data"] mutableCopy];
328
+ }
329
+
330
+ // attach a copy of the original notification payload into the data object,
331
+ // for internal use
332
+ userInfo[kNotifeeUserInfoNotification] = [notification mutableCopy];
333
+ if (trigger != nil) {
334
+ userInfo[kNotifeeUserInfoTrigger] = [trigger mutableCopy];
335
+ }
336
+
337
+ content.userInfo = userInfo;
338
+
339
+ // badgeCount - nil is an acceptable value so no need to check key existence
340
+ content.badge = iosDict[@"badgeCount"];
341
+
342
+ // categoryId
343
+ if (iosDict[@"categoryId"] != nil && iosDict[@"categoryId"] != [NSNull null]) {
344
+ content.categoryIdentifier = iosDict[@"categoryId"];
345
+ }
346
+
347
+ // launchImageName
348
+ if (iosDict[@"launchImageName"] != nil && iosDict[@"launchImageName"] != [NSNull null]) {
349
+ content.launchImageName = iosDict[@"launchImageName"];
350
+ }
351
+
352
+ // interruptionLevel
353
+ if (@available(iOS 15.0, *)) {
354
+ if (iosDict[@"interruptionLevel"] != nil) {
355
+ if ([iosDict[@"interruptionLevel"] isEqualToString:@"passive"]) {
356
+ content.interruptionLevel = UNNotificationInterruptionLevelPassive;
357
+ } else if ([iosDict[@"interruptionLevel"] isEqualToString:@"active"]) {
358
+ content.interruptionLevel = UNNotificationInterruptionLevelActive;
359
+ } else if ([iosDict[@"interruptionLevel"] isEqualToString:@"timeSensitive"]) {
360
+ content.interruptionLevel = UNNotificationInterruptionLevelTimeSensitive;
361
+ } else if ([iosDict[@"interruptionLevel"] isEqualToString:@"critical"]) {
362
+ content.interruptionLevel = UNNotificationInterruptionLevelCritical;
363
+ }
364
+ }
365
+ }
366
+
367
+ // critical, criticalVolume, sound
368
+ if (iosDict[@"critical"] != nil && iosDict[@"critical"] != [NSNull null]) {
369
+ UNNotificationSound *notificationSound;
370
+ BOOL criticalSound = [iosDict[@"critical"] boolValue];
371
+ NSNumber *criticalSoundVolume = iosDict[@"criticalVolume"];
372
+ NSString *soundName = iosDict[@"sound"] != nil ? iosDict[@"sound"] : @"default";
373
+
374
+ if ([soundName isEqualToString:@"default"]) {
375
+ if (criticalSound) {
376
+ if (@available(iOS 12.0, *)) {
377
+ if (criticalSoundVolume != nil) {
378
+ notificationSound = [UNNotificationSound
379
+ defaultCriticalSoundWithAudioVolume:[criticalSoundVolume floatValue]];
380
+ } else {
381
+ notificationSound = [UNNotificationSound defaultCriticalSound];
382
+ }
383
+ } else {
384
+ notificationSound = [UNNotificationSound defaultSound];
385
+ }
386
+ } else {
387
+ notificationSound = [UNNotificationSound defaultSound];
388
+ }
389
+ } else {
390
+ if (criticalSound) {
391
+ if (@available(iOS 12.0, *)) {
392
+ if (criticalSoundVolume != nil) {
393
+ notificationSound =
394
+ [UNNotificationSound criticalSoundNamed:soundName
395
+ withAudioVolume:[criticalSoundVolume floatValue]];
396
+ } else {
397
+ notificationSound = [UNNotificationSound criticalSoundNamed:soundName];
398
+ }
399
+ } else {
400
+ notificationSound = [UNNotificationSound soundNamed:soundName];
401
+ }
402
+ } else {
403
+ notificationSound = [UNNotificationSound soundNamed:soundName];
404
+ }
405
+ }
406
+ content.sound = notificationSound;
407
+ } else if (iosDict[@"sound"] != nil) {
408
+ UNNotificationSound *notificationSound;
409
+ NSString *soundName = iosDict[@"sound"];
410
+
411
+ if ([soundName isEqualToString:@"default"]) {
412
+ notificationSound = [UNNotificationSound defaultSound];
413
+ } else {
414
+ notificationSound = [UNNotificationSound soundNamed:soundName];
415
+ }
416
+
417
+ content.sound = notificationSound;
418
+
419
+ } // critical, criticalVolume, sound
420
+
421
+ // threadId
422
+ if (iosDict[@"threadId"] != nil) {
423
+ content.threadIdentifier = iosDict[@"threadId"];
424
+ }
425
+
426
+ if (@available(iOS 12.0, *)) {
427
+ // summaryArgument
428
+ if (iosDict[@"summaryArgument"] != nil) {
429
+ content.summaryArgument = iosDict[@"summaryArgument"];
430
+ }
431
+
432
+ // summaryArgumentCount
433
+ if (iosDict[@"summaryArgumentCount"] != nil) {
434
+ content.summaryArgumentCount = [iosDict[@"summaryArgumentCount"] unsignedIntValue];
435
+ }
436
+ }
437
+
438
+ if (@available(iOS 13.0, *)) {
439
+ // targetContentId
440
+ if (iosDict[@"targetContentId"] != nil) {
441
+ content.targetContentIdentifier = iosDict[@"targetContentId"];
442
+ }
443
+ }
444
+
445
+ // Ignore downloading attachments here if remote notifications via NSE
446
+ BOOL remote = [notification[@"remote"] boolValue];
447
+
448
+ if (iosDict[@"attachments"] != nil && !remote) {
449
+ content.attachments =
450
+ [NotifeeCoreUtil notificationAttachmentsFromDictionaryArray:iosDict[@"attachments"]];
451
+ }
452
+
453
+ return content;
454
+ }
455
+
456
+ + (void)getNotificationCategories:(notifeeMethodNSArrayBlock)block {
457
+ UNUserNotificationCenter *center = [UNUserNotificationCenter currentNotificationCenter];
458
+ [center getNotificationCategoriesWithCompletionHandler:^(
459
+ NSSet<UNNotificationCategory *> *categories) {
460
+ NSMutableArray<NSDictionary *> *categoriesArray = [[NSMutableArray alloc] init];
461
+
462
+ for (UNNotificationCategory *notificationCategory in categories) {
463
+ NSMutableDictionary *categoryDictionary = [NSMutableDictionary dictionary];
464
+
465
+ categoryDictionary[@"id"] = notificationCategory.identifier;
466
+ categoryDictionary[@"allowInCarPlay"] =
467
+ @(((notificationCategory.options & UNNotificationCategoryOptionAllowInCarPlay) != 0));
468
+
469
+ if (@available(iOS 11.0, *)) {
470
+ categoryDictionary[@"hiddenPreviewsShowTitle"] =
471
+ @(((notificationCategory.options &
472
+ UNNotificationCategoryOptionHiddenPreviewsShowTitle) != 0));
473
+ categoryDictionary[@"hiddenPreviewsShowSubtitle"] =
474
+ @(((notificationCategory.options &
475
+ UNNotificationCategoryOptionHiddenPreviewsShowSubtitle) != 0));
476
+ if (notificationCategory.hiddenPreviewsBodyPlaceholder != nil) {
477
+ categoryDictionary[@"hiddenPreviewsBodyPlaceholder"] =
478
+ notificationCategory.hiddenPreviewsBodyPlaceholder;
479
+ }
480
+ } else {
481
+ categoryDictionary[@"hiddenPreviewsShowTitle"] = @(NO);
482
+ categoryDictionary[@"hiddenPreviewsShowSubtitle"] = @(NO);
483
+ }
484
+
485
+ if (@available(iOS 12.0, *)) {
486
+ if (notificationCategory.categorySummaryFormat != nil) {
487
+ categoryDictionary[@"summaryFormat"] = notificationCategory.categorySummaryFormat;
488
+ }
489
+ }
490
+
491
+ if (@available(iOS 13.0, *)) {
492
+ categoryDictionary[@"allowAnnouncement"] = @(
493
+ ((notificationCategory.options & UNNotificationCategoryOptionAllowAnnouncement) != 0));
494
+ } else {
495
+ categoryDictionary[@"allowAnnouncement"] = @(NO);
496
+ }
497
+
498
+ categoryDictionary[@"actions"] =
499
+ [NotifeeCoreUtil notificationActionsToDictionaryArray:notificationCategory.actions];
500
+ categoryDictionary[@"intentIdentifiers"] =
501
+ [NotifeeCoreUtil intentIdentifiersFromStringArray:notificationCategory.intentIdentifiers];
502
+
503
+ [categoriesArray addObject:categoryDictionary];
504
+ }
505
+
506
+ block(nil, categoriesArray);
507
+ }];
508
+ }
509
+
510
+ /**
511
+ * Builds and replaces the existing notification categories on
512
+ * UNUserNotificationCenter
513
+ *
514
+ * @param categories NSArray<NSDictionary *> *
515
+ * @param block notifeeMethodVoidBlock
516
+ */
517
+ + (void)setNotificationCategories:(NSArray<NSDictionary *> *)categories
518
+ withBlock:(notifeeMethodVoidBlock)block {
519
+ NSMutableSet *UNNotificationCategories = [[NSMutableSet alloc] init];
520
+
521
+ for (NSDictionary *categoryDictionary in categories) {
522
+ UNNotificationCategory *category;
523
+
524
+ NSString *id = categoryDictionary[@"id"];
525
+ NSString *summaryFormat = categoryDictionary[@"summaryFormat"];
526
+ NSString *bodyPlaceHolder = categoryDictionary[@"hiddenPreviewsBodyPlaceholder"];
527
+
528
+ NSArray<UNNotificationAction *> *actions =
529
+ [NotifeeCoreUtil notificationActionsFromDictionaryArray:categoryDictionary[@"actions"]];
530
+ NSArray<NSString *> *intentIdentifiers =
531
+ [NotifeeCoreUtil intentIdentifiersFromNumberArray:categoryDictionary[@"intentIdentifiers"]];
532
+
533
+ UNNotificationCategoryOptions options = UNNotificationCategoryOptionCustomDismissAction;
534
+
535
+ if ([categoryDictionary[@"allowInCarPlay"] isEqual:@(YES)]) {
536
+ options |= UNNotificationCategoryOptionAllowInCarPlay;
537
+ }
538
+
539
+ if (@available(iOS 11.0, *)) {
540
+ if ([categoryDictionary[@"hiddenPreviewsShowTitle"] isEqual:@(YES)]) {
541
+ options |= UNNotificationCategoryOptionHiddenPreviewsShowTitle;
542
+ }
543
+
544
+ if ([categoryDictionary[@"hiddenPreviewsShowSubtitle"] isEqual:@(YES)]) {
545
+ options |= UNNotificationCategoryOptionHiddenPreviewsShowSubtitle;
546
+ }
547
+ }
548
+
549
+ if (@available(iOS 13.0, *)) {
550
+ if ([categoryDictionary[@"allowAnnouncement"] isEqual:@(YES)]) {
551
+ options |= UNNotificationCategoryOptionAllowAnnouncement;
552
+ }
553
+ }
554
+
555
+ if (@available(iOS 12.0, *)) {
556
+ category = [UNNotificationCategory categoryWithIdentifier:id
557
+ actions:actions
558
+ intentIdentifiers:intentIdentifiers
559
+ hiddenPreviewsBodyPlaceholder:bodyPlaceHolder
560
+ categorySummaryFormat:summaryFormat
561
+ options:options];
562
+ } else if (@available(iOS 11.0, *)) {
563
+ category = [UNNotificationCategory categoryWithIdentifier:id
564
+ actions:actions
565
+ intentIdentifiers:intentIdentifiers
566
+ hiddenPreviewsBodyPlaceholder:bodyPlaceHolder
567
+ options:options];
568
+ } else {
569
+ category = [UNNotificationCategory categoryWithIdentifier:id
570
+ actions:actions
571
+ intentIdentifiers:intentIdentifiers
572
+ options:options];
573
+ }
574
+
575
+ [UNNotificationCategories addObject:category];
576
+ }
577
+
578
+ UNUserNotificationCenter *center = [UNUserNotificationCenter currentNotificationCenter];
579
+ [center setNotificationCategories:UNNotificationCategories];
580
+ block(nil);
581
+ }
582
+
583
+ /**
584
+ * Request UNAuthorizationOptions for user notifications.
585
+ * Resolves a NSDictionary representation of UNNotificationSettings.
586
+ *
587
+ * @param permissions NSDictionary
588
+ * @param block NSDictionary block
589
+ */
590
+ + (void)requestPermission:(NSDictionary *)permissions
591
+ withBlock:(notifeeMethodNSDictionaryBlock)block {
592
+ UNUserNotificationCenter *center = [UNUserNotificationCenter currentNotificationCenter];
593
+
594
+ UNAuthorizationOptions options = UNAuthorizationOptionNone;
595
+
596
+ if ([permissions[@"alert"] isEqual:@(YES)]) {
597
+ options |= UNAuthorizationOptionAlert;
598
+ }
599
+
600
+ if ([permissions[@"badge"] isEqual:@(YES)]) {
601
+ options |= UNAuthorizationOptionBadge;
602
+ }
603
+
604
+ if ([permissions[@"sound"] isEqual:@(YES)]) {
605
+ options |= UNAuthorizationOptionSound;
606
+ }
607
+
608
+ if ([permissions[@"inAppNotificationSettings"] isEqual:@(YES)]) {
609
+ if (@available(iOS 12.0, *)) {
610
+ options |= UNAuthorizationOptionProvidesAppNotificationSettings;
611
+ }
612
+ }
613
+
614
+ if ([permissions[@"provisional"] isEqual:@(YES)]) {
615
+ if (@available(iOS 12.0, *)) {
616
+ options |= UNAuthorizationOptionProvisional;
617
+ }
618
+ }
619
+
620
+ if ([permissions[@"announcement"] isEqual:@(YES)]) {
621
+ if (@available(iOS 13.0, *)) {
622
+ options |= UNAuthorizationOptionAnnouncement;
623
+ }
624
+ }
625
+
626
+ if ([permissions[@"carPlay"] isEqual:@(YES)]) {
627
+ options |= UNAuthorizationOptionCarPlay;
628
+ }
629
+
630
+ if ([permissions[@"criticalAlert"] isEqual:@(YES)]) {
631
+ if (@available(iOS 12.0, *)) {
632
+ options |= UNAuthorizationOptionCriticalAlert;
633
+ }
634
+ }
635
+
636
+ id handler = ^(BOOL granted, NSError *_Nullable error) {
637
+ if (error != nil) {
638
+ // TODO send error to notifeeMethodNSDictionaryBlock
639
+ }
640
+
641
+ [self getNotificationSettings:block];
642
+ };
643
+
644
+ [center requestAuthorizationWithOptions:options completionHandler:handler];
645
+ }
646
+
647
+ /**
648
+ * Retrieve UNNotificationSettings for the application.
649
+ * Resolves a NSDictionary representation of UNNotificationSettings.
650
+ *
651
+ * @param block NSDictionary block
652
+ */
653
+ + (void)getNotificationSettings:(notifeeMethodNSDictionaryBlock)block {
654
+ UNUserNotificationCenter *center = [UNUserNotificationCenter currentNotificationCenter];
655
+
656
+ [center
657
+ getNotificationSettingsWithCompletionHandler:^(UNNotificationSettings *_Nonnull settings) {
658
+ NSMutableDictionary *settingsDictionary = [NSMutableDictionary dictionary];
659
+ NSMutableDictionary *iosDictionary = [NSMutableDictionary dictionary];
660
+
661
+ // authorizationStatus
662
+ NSNumber *authorizationStatus = @-1;
663
+ if (settings.authorizationStatus == UNAuthorizationStatusNotDetermined) {
664
+ authorizationStatus = @-1;
665
+ } else if (settings.authorizationStatus == UNAuthorizationStatusDenied) {
666
+ authorizationStatus = @0;
667
+ } else if (settings.authorizationStatus == UNAuthorizationStatusAuthorized) {
668
+ authorizationStatus = @1;
669
+ }
670
+
671
+ if (@available(iOS 12.0, *)) {
672
+ if (settings.authorizationStatus == UNAuthorizationStatusProvisional) {
673
+ authorizationStatus = @2;
674
+ }
675
+ }
676
+
677
+ NSNumber *showPreviews = @-1;
678
+ if (@available(iOS 11.0, *)) {
679
+ if (settings.showPreviewsSetting == UNShowPreviewsSettingNever) {
680
+ showPreviews = @0;
681
+ } else if (settings.showPreviewsSetting == UNShowPreviewsSettingAlways) {
682
+ showPreviews = @1;
683
+ } else if (settings.showPreviewsSetting == UNShowPreviewsSettingWhenAuthenticated) {
684
+ showPreviews = @2;
685
+ }
686
+ }
687
+
688
+ if (@available(iOS 13.0, *)) {
689
+ iosDictionary[@"announcement"] =
690
+ [NotifeeCoreUtil numberForUNNotificationSetting:settings.announcementSetting];
691
+ } else {
692
+ iosDictionary[@"announcement"] = @-1;
693
+ }
694
+
695
+ if (@available(iOS 12.0, *)) {
696
+ iosDictionary[@"criticalAlert"] =
697
+ [NotifeeCoreUtil numberForUNNotificationSetting:settings.criticalAlertSetting];
698
+ } else {
699
+ iosDictionary[@"criticalAlert"] = @-1;
700
+ }
701
+
702
+ if (@available(iOS 12.0, *)) {
703
+ iosDictionary[@"inAppNotificationSettings"] =
704
+ settings.providesAppNotificationSettings ? @1 : @0;
705
+ } else {
706
+ iosDictionary[@"inAppNotificationSettings"] = @-1;
707
+ }
708
+
709
+ iosDictionary[@"showPreviews"] = showPreviews;
710
+ iosDictionary[@"authorizationStatus"] = authorizationStatus;
711
+ iosDictionary[@"alert"] =
712
+ [NotifeeCoreUtil numberForUNNotificationSetting:settings.alertSetting];
713
+ iosDictionary[@"badge"] =
714
+ [NotifeeCoreUtil numberForUNNotificationSetting:settings.badgeSetting];
715
+ iosDictionary[@"sound"] =
716
+ [NotifeeCoreUtil numberForUNNotificationSetting:settings.soundSetting];
717
+ iosDictionary[@"carPlay"] =
718
+ [NotifeeCoreUtil numberForUNNotificationSetting:settings.carPlaySetting];
719
+ iosDictionary[@"lockScreen"] =
720
+ [NotifeeCoreUtil numberForUNNotificationSetting:settings.lockScreenSetting];
721
+ iosDictionary[@"notificationCenter"] =
722
+ [NotifeeCoreUtil numberForUNNotificationSetting:settings.notificationCenterSetting];
723
+
724
+ settingsDictionary[@"authorizationStatus"] = authorizationStatus;
725
+ settingsDictionary[@"ios"] = iosDictionary;
726
+
727
+ block(nil, settingsDictionary);
728
+ }];
729
+ }
730
+
731
+ + (void)getInitialNotification:(notifeeMethodNSDictionaryBlock)block {
732
+ [NotifeeCoreUNUserNotificationCenter instance].initialNotificationBlock = block;
733
+ [[NotifeeCoreUNUserNotificationCenter instance] getInitialNotification];
734
+ }
735
+
736
+ + (void)setBadgeCount:(NSInteger)count withBlock:(notifeeMethodVoidBlock)block {
737
+ if (![NotifeeCoreUtil isAppExtension]) {
738
+ if (@available(iOS 16.0, macOS 10.13, macCatalyst 16.0, tvOS 16.0, visionOS 1.0, *)) {
739
+ UNUserNotificationCenter *center = [UNUserNotificationCenter currentNotificationCenter];
740
+ [center setBadgeCount:count
741
+ withCompletionHandler:^(NSError *error) {
742
+ if (error) {
743
+ NSLog(@"NotifeeCore: Could not setBadgeCount: %@", error);
744
+ block(error);
745
+ } else {
746
+ block(nil);
747
+ }
748
+ }];
749
+ return;
750
+ } else {
751
+ // If count is 0, set to -1 instead to avoid notifications in tray being cleared
752
+ // this breaks in iOS 18, but at that point we're using the new setBadge API
753
+ NSInteger newCount = count == 0 ? -1 : count;
754
+ UIApplication *application = (UIApplication *)[NotifeeCoreUtil notifeeUIApplication];
755
+ [application setApplicationIconBadgeNumber:newCount];
756
+ }
757
+ }
758
+ block(nil);
759
+ }
760
+
761
+ + (void)getBadgeCount:(notifeeMethodNSIntegerBlock)block {
762
+ if (![NotifeeCoreUtil isAppExtension]) {
763
+ UIApplication *application = (UIApplication *)[NotifeeCoreUtil notifeeUIApplication];
764
+ NSInteger badgeCount = application.applicationIconBadgeNumber;
765
+
766
+ block(nil, badgeCount == -1 ? 0 : badgeCount);
767
+ }
768
+ }
769
+
770
+ + (void)incrementBadgeCount:(NSInteger)incrementBy withBlock:(notifeeMethodVoidBlock)block {
771
+ if (![NotifeeCoreUtil isAppExtension]) {
772
+ UIApplication *application = (UIApplication *)[NotifeeCoreUtil notifeeUIApplication];
773
+ NSInteger currentCount = application.applicationIconBadgeNumber;
774
+ // If count -1 (to clear badge w/o clearing notifications),
775
+ // set currentCount to 0 before incrementing
776
+ if (currentCount == -1) {
777
+ currentCount = 0;
778
+ }
779
+
780
+ NSInteger newCount = currentCount + incrementBy;
781
+
782
+ if (@available(iOS 16.0, macOS 10.13, macCatalyst 16.0, tvOS 16.0, visionOS 1.0, *)) {
783
+ UNUserNotificationCenter *center = [UNUserNotificationCenter currentNotificationCenter];
784
+ [center setBadgeCount:newCount
785
+ withCompletionHandler:^(NSError *error) {
786
+ if (error) {
787
+ NSLog(@"NotifeeCore: Could not incrementBadgeCount: %@", error);
788
+ block(error);
789
+ } else {
790
+ block(nil);
791
+ }
792
+ }];
793
+ return;
794
+ } else {
795
+ [application setApplicationIconBadgeNumber:newCount];
796
+ }
797
+ }
798
+ block(nil);
799
+ }
800
+
801
+ + (void)decrementBadgeCount:(NSInteger)decrementBy withBlock:(notifeeMethodVoidBlock)block {
802
+ if (![NotifeeCoreUtil isAppExtension]) {
803
+ UIApplication *application = (UIApplication *)[NotifeeCoreUtil notifeeUIApplication];
804
+ NSInteger currentCount = application.applicationIconBadgeNumber;
805
+ NSInteger newCount = currentCount - decrementBy;
806
+ if (@available(iOS 16.0, macOS 10.13, macCatalyst 16.0, tvOS 16.0, visionOS 1.0, *)) {
807
+ UNUserNotificationCenter *center = [UNUserNotificationCenter currentNotificationCenter];
808
+ [center setBadgeCount:newCount
809
+ withCompletionHandler:^(NSError *error) {
810
+ if (error) {
811
+ NSLog(@"NotifeeCore: Could not incrementBadgeCount: %@", error);
812
+ block(error);
813
+ } else {
814
+ block(nil);
815
+ }
816
+ }];
817
+ return;
818
+ } else {
819
+ // If count is 0 or less, set to -1 instead to avoid notifications in tray being cleared
820
+ // this breaks in iOS 18, but at that point we're using the new setBadge API
821
+ if (newCount < 1) {
822
+ newCount = -1;
823
+ }
824
+ [application setApplicationIconBadgeNumber:newCount];
825
+ }
826
+ }
827
+
828
+ block(nil);
829
+ }
830
+
831
+ + (nullable instancetype)notifeeUIApplication {
832
+ return (NotifeeCore *)[NotifeeCoreUtil notifeeUIApplication];
833
+ };
834
+
835
+ + (void)populateNotificationContent:(UNNotificationRequest *)request
836
+ withContent:(UNMutableNotificationContent *)content
837
+ withContentHandler:(void (^)(UNNotificationContent *_Nonnull))contentHandler {
838
+ return [[NotifeeCoreExtensionHelper instance] populateNotificationContent:request
839
+ withContent:content
840
+ withContentHandler:contentHandler];
841
+ };
842
+
843
+ @end