@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,834 @@
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 "NotifeeCoreUtil.h"
19
+ #include <CoreGraphics/CGGeometry.h>
20
+ #import <Intents/INIntentIdentifiers.h>
21
+ #import <UIKit/UIKit.h>
22
+ #import "NotifeeCore+NSURLSession.h"
23
+
24
+ @implementation NotifeeCoreUtil
25
+
26
+ + (NSNumber *)numberForUNNotificationSetting:(UNNotificationSetting)setting {
27
+ NSNumber *asNumber = @-1;
28
+ if (setting == UNNotificationSettingNotSupported) {
29
+ asNumber = @-1;
30
+ } else if (setting == UNNotificationSettingDisabled) {
31
+ asNumber = @0;
32
+ } else if (setting == UNNotificationSettingEnabled) {
33
+ asNumber = @1;
34
+ }
35
+ return asNumber;
36
+ }
37
+
38
+ + (NSMutableArray<NSDictionary *> *)notificationActionsToDictionaryArray:
39
+ (NSArray<UNNotificationAction *> *)notificationActions {
40
+ NSMutableArray<NSDictionary *> *notificationActionDicts = [[NSMutableArray alloc] init];
41
+
42
+ for (UNNotificationAction *notificationAction in notificationActions) {
43
+ NSMutableDictionary *notificationActionDict = [NSMutableDictionary dictionary];
44
+
45
+ notificationActionDict[@"id"] = notificationAction.identifier;
46
+ notificationActionDict[@"title"] = notificationAction.title;
47
+
48
+ notificationActionDict[@"destructive"] =
49
+ @(((notificationAction.options & UNNotificationActionOptionDestructive) != 0));
50
+ notificationActionDict[@"foreground"] =
51
+ @(((notificationAction.options & UNNotificationActionOptionForeground) != 0));
52
+ notificationActionDict[@"authenticationRequired"] =
53
+ @(((notificationAction.options & UNNotificationActionOptionAuthenticationRequired) != 0));
54
+
55
+ if ([[notificationAction class] isKindOfClass:[UNTextInputNotificationAction class]]) {
56
+ UNTextInputNotificationAction *notificationInputAction =
57
+ (UNTextInputNotificationAction *)notificationAction;
58
+ if ([notificationInputAction textInputButtonTitle] == nil &&
59
+ [notificationInputAction textInputPlaceholder] == nil) {
60
+ notificationActionDict[@"input"] = @(YES);
61
+ } else {
62
+ NSMutableDictionary *inputDict = [NSMutableDictionary dictionary];
63
+ inputDict[@"buttonText"] = [notificationInputAction textInputButtonTitle];
64
+ inputDict[@"placeholderText"] = [notificationInputAction textInputPlaceholder];
65
+ notificationActionDict[@"input"] = inputDict;
66
+ }
67
+ } else {
68
+ notificationActionDict[@"input"] = @(NO);
69
+ }
70
+
71
+ [notificationActionDicts addObject:notificationActionDict];
72
+ }
73
+
74
+ return notificationActionDicts;
75
+ }
76
+
77
+ + (NSMutableArray<UNNotificationAction *> *)notificationActionsFromDictionaryArray:
78
+ (NSArray<NSDictionary *> *)actionDictionaries {
79
+ NSMutableArray<UNNotificationAction *> *notificationActions = [[NSMutableArray alloc] init];
80
+
81
+ for (NSDictionary *actionDictionary in actionDictionaries) {
82
+ UNNotificationAction *notificationAction;
83
+
84
+ NSString *id = actionDictionary[@"id"];
85
+ NSString *title = actionDictionary[@"title"];
86
+
87
+ UNNotificationActionOptions options = 0;
88
+
89
+ if ([actionDictionary[@"destructive"] isEqual:@(YES)]) {
90
+ options |= UNNotificationActionOptionDestructive;
91
+ }
92
+
93
+ if ([actionDictionary[@"foreground"] isEqual:@(YES)]) {
94
+ options |= UNNotificationActionOptionForeground;
95
+ }
96
+
97
+ if ([actionDictionary[@"authenticationRequired"] isEqual:@(YES)]) {
98
+ options |= UNNotificationActionOptionAuthenticationRequired;
99
+ }
100
+
101
+ if (actionDictionary[@"input"] != nil &&
102
+ [actionDictionary[@"input"] isKindOfClass:NSDictionary.class]) {
103
+ NSDictionary *inputDictionary = actionDictionary[@"input"];
104
+ NSString *buttonText = inputDictionary[@"buttonText"];
105
+ NSString *placeholderText = inputDictionary[@"placeholderText"];
106
+ notificationAction = [UNTextInputNotificationAction actionWithIdentifier:id
107
+ title:title
108
+ options:options
109
+ textInputButtonTitle:buttonText
110
+ textInputPlaceholder:placeholderText];
111
+ } else if (actionDictionary[@"input"] != nil) { // BOOL
112
+ notificationAction = [UNTextInputNotificationAction actionWithIdentifier:id
113
+ title:title
114
+ options:options];
115
+ } else {
116
+ notificationAction = [UNNotificationAction actionWithIdentifier:id
117
+ title:title
118
+ options:options];
119
+ }
120
+
121
+ [notificationActions addObject:notificationAction];
122
+ }
123
+
124
+ return notificationActions;
125
+ }
126
+
127
+ /**
128
+ * Builds the notification attachments
129
+ * If no attachments are resolved, an empty array will be returned
130
+ *
131
+ * @return NSArray<UNNotificationAttachment *> *
132
+ */
133
+ + (NSMutableArray<UNNotificationAttachment *> *)notificationAttachmentsFromDictionaryArray:
134
+ (NSArray<NSDictionary *> *)attachmentDictionaries {
135
+ NSMutableArray<UNNotificationAttachment *> *attachments = [[NSMutableArray alloc] init];
136
+
137
+ for (NSDictionary *attachmentDict in attachmentDictionaries) {
138
+ UNNotificationAttachment *attachment = [self attachmentFromDictionary:attachmentDict];
139
+ if (attachment) {
140
+ [attachments addObject:attachment];
141
+ }
142
+ }
143
+ return attachments;
144
+ }
145
+
146
+ /**
147
+ * Returns an UNNotificationAttachment from a file path or local resource
148
+ *
149
+ * @return UNNotificationAttachment or null if the attachment fails to resolve
150
+ */
151
+ + (UNNotificationAttachment *)attachmentFromDictionary:(NSDictionary *)attachmentDict {
152
+ NSString *identifier = attachmentDict[@"id"];
153
+ NSString *urlString = attachmentDict[@"url"];
154
+
155
+ NSURL *url = [self getURLFromString:urlString];
156
+
157
+ if (url) {
158
+ NSError *error;
159
+ UNNotificationAttachment *attachment = [UNNotificationAttachment
160
+ attachmentWithIdentifier:identifier
161
+ URL:url
162
+ options:[self attachmentOptionsFromDictionary:attachmentDict]
163
+ error:&error];
164
+ if (error != nil) {
165
+ NSLog(@"NotifeeCore: An error occurred whilst trying to resolve an "
166
+ @"attachment %@: %@",
167
+ attachmentDict, error);
168
+ return nil;
169
+ } else if (attachment == nil) {
170
+ NSLog(@"NotifeeCore: Failed resolving an attachment %@: data at URL is "
171
+ @"not a supported type.",
172
+ attachmentDict);
173
+ }
174
+
175
+ return attachment;
176
+ }
177
+
178
+ NSLog(@"NotifeeCore: Unable to resolve url for attachment: %@", attachmentDict);
179
+ return nil;
180
+ }
181
+
182
+ /*
183
+ * get the URL from a string
184
+ *
185
+ * @param urlString NSString
186
+ * @return NSURL
187
+ */
188
+ + (NSURL *)getURLFromString:(NSString *)urlString {
189
+ NSURL *url;
190
+
191
+ if ([urlString hasPrefix:@"http://"] || [urlString hasPrefix:@"https://"]) {
192
+ // handle remote url by attempting to download attachement synchronously
193
+ url = [self downloadMediaSynchronously:urlString];
194
+ } else if ([urlString hasPrefix:@"/"]) {
195
+ // handle absolute file path
196
+ url = [NSURL fileURLWithPath:urlString];
197
+ } else {
198
+ // try to resolve local resource
199
+ url = [[NSBundle mainBundle] URLForResource:urlString withExtension:nil];
200
+ }
201
+
202
+ return url;
203
+ }
204
+
205
+ /*
206
+ * Downloads a media file, syncronously to the NSCachesDirectory
207
+ *
208
+ * @param urlString NSString
209
+ * @return NSURL or nil
210
+ */
211
+ + (NSURL *)downloadMediaSynchronously:(NSString *)urlString {
212
+ NSURL *url = [NSURL URLWithString:urlString];
213
+
214
+ NSString *newCachedFileName = [self generateCachedFileName:15];
215
+
216
+ NSArray *localDirectoryPaths =
217
+ NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES);
218
+ NSString *tempDestination =
219
+ [localDirectoryPaths[0] stringByAppendingPathComponent:newCachedFileName];
220
+
221
+ @try {
222
+ NSError *error;
223
+
224
+ // Apple gives us a suggested file name which can be used to infer the file
225
+ // extension
226
+ NSString *suggestedFilename = [NotifeeCoreNSURLSession downloadItemAtURL:url
227
+ toFile:tempDestination
228
+ error:&error];
229
+
230
+ if (error) {
231
+ NSLog(@"NotifeeCore: Failed to download attachement with URL %@: %@", urlString, error);
232
+ return nil;
233
+ }
234
+
235
+ // Rename the recently downloaded file to include its file extension
236
+ NSFileManager *fileManager = [NSFileManager defaultManager];
237
+
238
+ NSString *fileExtension = [NSString stringWithFormat:@".%@", [suggestedFilename pathExtension]];
239
+
240
+ if (!fileExtension || [fileExtension isEqualToString:@""]) {
241
+ NSLog(@"NotifeeCore: Failed to determine file extension for attachment "
242
+ @"with URL %@: %@",
243
+ urlString, error);
244
+ return nil;
245
+ }
246
+
247
+ NSString *localFilePath =
248
+ [localDirectoryPaths[0] stringByAppendingPathComponent:newCachedFileName];
249
+
250
+ localFilePath = [localFilePath stringByAppendingString:fileExtension];
251
+ NSURL *localURL = [NSURL fileURLWithPath:localFilePath];
252
+
253
+ [fileManager moveItemAtPath:tempDestination toPath:localFilePath error:&error];
254
+
255
+ // Returns the local cached path to attachment
256
+ return localURL;
257
+ } @catch (NSException *exception) {
258
+ NSLog(@"NotifeeCore: An exception occured while attempting to download "
259
+ @"attachment with URL %@: "
260
+ @"%@",
261
+ urlString, exception);
262
+ return nil;
263
+ }
264
+ }
265
+
266
+ /**
267
+ * Returns a NSDictionary representation of options related to the attached file
268
+ *
269
+ * @param optionsDict NSDictionary
270
+ */
271
+ + (NSDictionary *)attachmentOptionsFromDictionary:(NSDictionary *)optionsDict {
272
+ NSMutableDictionary *options = [NSMutableDictionary new];
273
+ if (optionsDict[@"typeHint"] != nil) {
274
+ options[UNNotificationAttachmentOptionsTypeHintKey] = optionsDict[@"typeHint"];
275
+ }
276
+
277
+ if (optionsDict[@"thumbnailHidden"] != nil) {
278
+ options[UNNotificationAttachmentOptionsThumbnailHiddenKey] = optionsDict[@"thumbnailHidden"];
279
+ }
280
+
281
+ if (optionsDict[@"thumbnailClippingRect"] != nil) {
282
+ NSDictionary *area = optionsDict[@"thumbnailClippingRect"];
283
+ NSNumber *x = area[@"x"];
284
+ NSNumber *y = area[@"y"];
285
+ NSNumber *width = area[@"width"];
286
+ NSNumber *height = area[@"height"];
287
+ CGRect areaRect =
288
+ CGRectMake([x doubleValue], [y doubleValue], [width doubleValue], [height doubleValue]);
289
+ options[UNNotificationAttachmentOptionsThumbnailClippingRectKey] =
290
+ (__bridge id _Nullable)(CGRectCreateDictionaryRepresentation(areaRect));
291
+ }
292
+
293
+ if (optionsDict[@"thumbnailTime"] != nil) {
294
+ options[UNNotificationAttachmentOptionsThumbnailTimeKey] = optionsDict[@"thumbnailTime"];
295
+ }
296
+
297
+ return options;
298
+ }
299
+
300
+ /**
301
+ * Returns an UNNotificationTrigger from NSDictionary representing a trigger
302
+ *
303
+ * @param triggerDict NSDictionary
304
+ * @return UNNotificationTrigger or null if trigger type is not recognised
305
+ */
306
+ + (UNNotificationTrigger *)triggerFromDictionary:(NSDictionary *)triggerDict {
307
+ UNNotificationTrigger *trigger;
308
+ NSInteger triggerType = [triggerDict[@"type"] integerValue];
309
+
310
+ if (triggerType == NotifeeCoreTriggerTypeTimestamp) {
311
+ trigger = [self timestampTriggerFromDictionary:triggerDict];
312
+ } else if (triggerType == NotifeeCoreTriggerTypeInterval) {
313
+ trigger = [self intervalTriggerFromDictionary:triggerDict];
314
+ } else {
315
+ NSLog(@"NotifeeCore: Failed to parse trigger with unknown trigger type: %ld",
316
+ (long)triggerType);
317
+ }
318
+
319
+ return trigger;
320
+ }
321
+
322
+ /**
323
+ * Returns an UNNotificationTrigger from NSDictionary representing a
324
+ * TimestampTrigger
325
+ *
326
+ * @param triggerDict NSDictionary
327
+ */
328
+ + (UNNotificationTrigger *)timestampTriggerFromDictionary:(NSDictionary *)triggerDict {
329
+ UNNotificationTrigger *trigger;
330
+ Boolean repeats = false;
331
+ NSCalendarUnit calendarUnit;
332
+
333
+ NSInteger repeatFrequency = [triggerDict[@"repeatFrequency"] integerValue];
334
+ NSNumber *timestampMillis = triggerDict[@"timestamp"];
335
+
336
+ // convert timestamp to a NSDate
337
+ NSInteger timestamp = [timestampMillis doubleValue] / 1000;
338
+ NSDate *date = [NSDate dateWithTimeIntervalSince1970:timestamp];
339
+
340
+ if (repeatFrequency != -1) {
341
+ repeats = true;
342
+
343
+ if (repeatFrequency == NotifeeCoreRepeatFrequencyHourly) {
344
+ // match by minute and second
345
+ calendarUnit = NSCalendarUnitMinute | NSCalendarUnitSecond;
346
+ } else if (repeatFrequency == NotifeeCoreRepeatFrequencyDaily) {
347
+ // match by hour, minute and second
348
+ calendarUnit = NSCalendarUnitHour | NSCalendarUnitMinute | NSCalendarUnitSecond;
349
+ } else if (repeatFrequency == NotifeeCoreRepeatFrequencyWeekly) {
350
+ // match by day, hour, minute, and second
351
+ calendarUnit =
352
+ NSCalendarUnitWeekday | NSCalendarUnitHour | NSCalendarUnitMinute | NSCalendarUnitSecond;
353
+ } else {
354
+ NSLog(@"NotifeeCore: Failed to parse TimestampTrigger with unknown "
355
+ @"repeatFrequency: %ld",
356
+ (long)repeatFrequency);
357
+
358
+ return nil;
359
+ }
360
+ } else {
361
+ // Needs to match exactly to the second
362
+ calendarUnit = NSCalendarUnitYear | NSCalendarUnitMonth | NSCalendarUnitDay |
363
+ NSCalendarUnitHour | NSCalendarUnitMinute | NSCalendarUnitSecond;
364
+ }
365
+
366
+ NSDateComponents *components = [[NSCalendar currentCalendar] components:calendarUnit
367
+ fromDate:date];
368
+ trigger = [UNCalendarNotificationTrigger triggerWithDateMatchingComponents:components
369
+ repeats:repeats];
370
+
371
+ return trigger;
372
+ }
373
+ /**
374
+ * Returns an UNNotificationTrigger from NSDictionary representing an
375
+ * IntervalTrigger
376
+ *
377
+ * @param triggerDict NSDictionary
378
+ */
379
+ + (UNNotificationTrigger *)intervalTriggerFromDictionary:(NSDictionary *)triggerDict {
380
+ double intervalNumber = [triggerDict[@"interval"] doubleValue];
381
+ NSString *timeUnit = triggerDict[@"timeUnit"];
382
+
383
+ NSTimeInterval intervalInSeconds = 0;
384
+
385
+ if ([timeUnit isEqualToString:kNotifeeCoreTimeUnitSeconds]) {
386
+ intervalInSeconds = intervalNumber;
387
+ } else if ([timeUnit isEqualToString:kNotifeeCoreTimeUnitMinutes]) {
388
+ // multiply by the number of seconds in 1 minute
389
+ intervalInSeconds = intervalNumber * 60;
390
+ } else if ([timeUnit isEqualToString:kNotifeeCoreTimeUnitHours]) {
391
+ // multiply by the number of seconds in 1 hour
392
+ intervalInSeconds = intervalNumber * 3600;
393
+ } else if ([timeUnit isEqualToString:kNotifeeCoreTimeUnitDays]) {
394
+ // multiply by the number of seconds in 1 day
395
+ intervalInSeconds = intervalNumber * 86400;
396
+ } else {
397
+ NSLog(@"NotifeeCore: Failed to parse IntervalTrigger with unknown "
398
+ @"timeUnit: %@",
399
+ timeUnit);
400
+ return nil;
401
+ }
402
+
403
+ return [UNTimeIntervalNotificationTrigger triggerWithTimeInterval:intervalInSeconds repeats:true];
404
+ }
405
+
406
+ + (NSMutableArray<NSNumber *> *)intentIdentifiersFromStringArray:
407
+ (NSArray<NSString *> *)identifiers {
408
+ NSMutableArray<NSNumber *> *intentIdentifiers = [[NSMutableArray alloc] init];
409
+
410
+ for (NSString *identifier in identifiers) {
411
+ if ([identifier isEqualToString:INStartAudioCallIntentIdentifier]) {
412
+ // IOSIntentIdentifier.START_AUDIO_CALL
413
+ [intentIdentifiers addObject:@0];
414
+ } else if ([identifier isEqualToString:INStartVideoCallIntentIdentifier]) {
415
+ // IOSIntentIdentifier.START_VIDEO_CALL
416
+ [intentIdentifiers addObject:@1];
417
+ } else if ([identifier isEqualToString:INSearchCallHistoryIntentIdentifier]) {
418
+ // IOSIntentIdentifier.SEARCH_CALL_HISTORY
419
+ [intentIdentifiers addObject:@2];
420
+ } else if ([identifier isEqualToString:INSetAudioSourceInCarIntentIdentifier]) {
421
+ // IOSIntentIdentifier.SET_AUDIO_SOURCE_IN_CAR
422
+ [intentIdentifiers addObject:@3];
423
+ } else if ([identifier isEqualToString:INSetClimateSettingsInCarIntentIdentifier]) {
424
+ // IOSIntentIdentifier.SET_CLIMATE_SETTINGS_IN_CAR
425
+ [intentIdentifiers addObject:@4];
426
+ } else if ([identifier isEqualToString:INSetDefrosterSettingsInCarIntentIdentifier]) {
427
+ // IOSIntentIdentifier.SET_DEFROSTER_SETTINGS_IN_CAR
428
+ [intentIdentifiers addObject:@5];
429
+ } else if ([identifier isEqualToString:INSetSeatSettingsInCarIntentIdentifier]) {
430
+ // IOSIntentIdentifier.SET_SEAT_SETTINGS_IN_CAR
431
+ [intentIdentifiers addObject:@6];
432
+ } else if ([identifier isEqualToString:INSetProfileInCarIntentIdentifier]) {
433
+ // IOSIntentIdentifier.SET_PROFILE_IN_CAR
434
+ [intentIdentifiers addObject:@7];
435
+ } else if ([identifier isEqualToString:INSaveProfileInCarIntentIdentifier]) {
436
+ // IOSIntentIdentifier.SAVE_PROFILE_IN_CAR
437
+ [intentIdentifiers addObject:@8];
438
+ } else if ([identifier isEqualToString:INStartWorkoutIntentIdentifier]) {
439
+ // IOSIntentIdentifier.START_WORKOUT
440
+ [intentIdentifiers addObject:@9];
441
+ } else if ([identifier isEqualToString:INPauseWorkoutIntentIdentifier]) {
442
+ // IOSIntentIdentifier.PAUSE_WORKOUT
443
+ [intentIdentifiers addObject:@10];
444
+ } else if ([identifier isEqualToString:INEndWorkoutIntentIdentifier]) {
445
+ // IOSIntentIdentifier.END_WORKOUT
446
+ [intentIdentifiers addObject:@11];
447
+ } else if ([identifier isEqualToString:INCancelWorkoutIntentIdentifier]) {
448
+ // IOSIntentIdentifier.CANCEL_WORKOUT
449
+ [intentIdentifiers addObject:@12];
450
+ } else if ([identifier isEqualToString:INResumeWorkoutIntentIdentifier]) {
451
+ // IOSIntentIdentifier.RESUME_WORKOUT
452
+ [intentIdentifiers addObject:@13];
453
+ } else if ([identifier isEqualToString:INSetRadioStationIntentIdentifier]) {
454
+ // IOSIntentIdentifier.SET_RADIO_STATION
455
+ [intentIdentifiers addObject:@14];
456
+ } else if ([identifier isEqualToString:INSendMessageIntentIdentifier]) {
457
+ // IOSIntentIdentifier.SEND_MESSAGE
458
+ [intentIdentifiers addObject:@15];
459
+ } else if ([identifier isEqualToString:INSearchForMessagesIntentIdentifier]) {
460
+ // IOSIntentIdentifier.SEARCH_FOR_MESSAGES
461
+ [intentIdentifiers addObject:@16];
462
+ } else if ([identifier isEqualToString:INSetMessageAttributeIntentIdentifier]) {
463
+ // IOSIntentIdentifier.SET_MESSAGE_ATTRIBUTE
464
+ [intentIdentifiers addObject:@17];
465
+ } else if ([identifier isEqualToString:INSendPaymentIntentIdentifier]) {
466
+ // IOSIntentIdentifier.SEND_PAYMENT
467
+ [intentIdentifiers addObject:@18];
468
+ } else if ([identifier isEqualToString:INRequestPaymentIntentIdentifier]) {
469
+ // IOSIntentIdentifier.REQUEST_PAYMENT
470
+ [intentIdentifiers addObject:@19];
471
+ } else if ([identifier isEqualToString:INSearchForPhotosIntentIdentifier]) {
472
+ // IOSIntentIdentifier.SEARCH_FOR_PHOTOS
473
+ [intentIdentifiers addObject:@20];
474
+ } else if ([identifier isEqualToString:INStartPhotoPlaybackIntentIdentifier]) {
475
+ // IOSIntentIdentifier.START_PHOTO_PLAYBACK
476
+ [intentIdentifiers addObject:@21];
477
+ } else if ([identifier isEqualToString:INListRideOptionsIntentIdentifier]) {
478
+ // IOSIntentIdentifier.LIST_RIDE_OPTIONS
479
+ [intentIdentifiers addObject:@22];
480
+ } else if ([identifier isEqualToString:INRequestRideIntentIdentifier]) {
481
+ // IOSIntentIdentifier.REQUEST_RIDE
482
+ [intentIdentifiers addObject:@23];
483
+ } else if ([identifier isEqualToString:INGetRideStatusIntentIdentifier]) {
484
+ // IOSIntentIdentifier.GET_RIDE_STATUS
485
+ [intentIdentifiers addObject:@24];
486
+ }
487
+ }
488
+
489
+ return intentIdentifiers;
490
+ }
491
+
492
+ + (NSMutableArray<NSString *> *)intentIdentifiersFromNumberArray:
493
+ (NSArray<NSNumber *> *)identifiers {
494
+ NSMutableArray<NSString *> *intentIdentifiers = [[NSMutableArray alloc] init];
495
+
496
+ for (NSNumber *identifier in identifiers) {
497
+ if ([identifier isEqualToNumber:@0]) {
498
+ // IOSIntentIdentifier.START_AUDIO_CALL
499
+ [intentIdentifiers addObject:INStartAudioCallIntentIdentifier];
500
+ } else if ([identifier isEqualToNumber:@1]) {
501
+ // IOSIntentIdentifier.START_VIDEO_CALL
502
+ [intentIdentifiers addObject:INStartVideoCallIntentIdentifier];
503
+ } else if ([identifier isEqualToNumber:@2]) {
504
+ // IOSIntentIdentifier.SEARCH_CALL_HISTORY
505
+ [intentIdentifiers addObject:INSearchCallHistoryIntentIdentifier];
506
+ } else if ([identifier isEqualToNumber:@3]) {
507
+ // IOSIntentIdentifier.SET_AUDIO_SOURCE_IN_CAR
508
+ [intentIdentifiers addObject:INSetAudioSourceInCarIntentIdentifier];
509
+ } else if ([identifier isEqualToNumber:@4]) {
510
+ // IOSIntentIdentifier.SET_CLIMATE_SETTINGS_IN_CAR
511
+ [intentIdentifiers addObject:INSetClimateSettingsInCarIntentIdentifier];
512
+ } else if ([identifier isEqualToNumber:@5]) {
513
+ // IOSIntentIdentifier.SET_DEFROSTER_SETTINGS_IN_CAR
514
+ [intentIdentifiers addObject:INSetDefrosterSettingsInCarIntentIdentifier];
515
+ } else if ([identifier isEqualToNumber:@6]) {
516
+ // IOSIntentIdentifier.SET_SEAT_SETTINGS_IN_CAR
517
+ [intentIdentifiers addObject:INSetSeatSettingsInCarIntentIdentifier];
518
+ } else if ([identifier isEqualToNumber:@7]) {
519
+ // IOSIntentIdentifier.SET_PROFILE_IN_CAR
520
+ [intentIdentifiers addObject:INSetProfileInCarIntentIdentifier];
521
+ } else if ([identifier isEqualToNumber:@8]) {
522
+ // IOSIntentIdentifier.SAVE_PROFILE_IN_CAR
523
+ [intentIdentifiers addObject:INSaveProfileInCarIntentIdentifier];
524
+ } else if ([identifier isEqualToNumber:@9]) {
525
+ // IOSIntentIdentifier.START_WORKOUT
526
+ [intentIdentifiers addObject:INStartWorkoutIntentIdentifier];
527
+ } else if ([identifier isEqualToNumber:@10]) {
528
+ // IOSIntentIdentifier.PAUSE_WORKOUT
529
+ [intentIdentifiers addObject:INPauseWorkoutIntentIdentifier];
530
+ } else if ([identifier isEqualToNumber:@11]) {
531
+ // IntentIdentifier.END_WORKOUT
532
+ [intentIdentifiers addObject:INEndWorkoutIntentIdentifier];
533
+ } else if ([identifier isEqualToNumber:@12]) {
534
+ // IOSIntentIdentifier.CANCEL_WORKOUT
535
+ [intentIdentifiers addObject:INCancelWorkoutIntentIdentifier];
536
+ } else if ([identifier isEqualToNumber:@13]) {
537
+ // IOSIntentIdentifier.RESUME_WORKOUT
538
+ [intentIdentifiers addObject:INResumeWorkoutIntentIdentifier];
539
+ } else if ([identifier isEqualToNumber:@14]) {
540
+ // IOSIntentIdentifier.SET_RADIO_STATION
541
+ [intentIdentifiers addObject:INSetRadioStationIntentIdentifier];
542
+ } else if ([identifier isEqualToNumber:@15]) {
543
+ // IOSIntentIdentifier.SEND_MESSAGE
544
+ [intentIdentifiers addObject:INSendMessageIntentIdentifier];
545
+ } else if ([identifier isEqualToNumber:@16]) {
546
+ // IOSIntentIdentifier.SEARCH_FOR_MESSAGES
547
+ [intentIdentifiers addObject:INSearchForMessagesIntentIdentifier];
548
+ } else if ([identifier isEqualToNumber:@17]) {
549
+ // IOSIntentIdentifier.SET_MESSAGE_ATTRIBUTE
550
+ [intentIdentifiers addObject:INSetMessageAttributeIntentIdentifier];
551
+ } else if ([identifier isEqualToNumber:@18]) {
552
+ // IOSIntentIdentifier.SEND_PAYMENT
553
+ [intentIdentifiers addObject:INSendPaymentIntentIdentifier];
554
+ } else if ([identifier isEqualToNumber:@19]) {
555
+ // IOSIntentIdentifier.REQUEST_PAYMENT
556
+ [intentIdentifiers addObject:INRequestPaymentIntentIdentifier];
557
+ } else if ([identifier isEqualToNumber:@20]) {
558
+ // IOSIntentIdentifier.SEARCH_FOR_PHOTOS
559
+ [intentIdentifiers addObject:INSearchForPhotosIntentIdentifier];
560
+ } else if ([identifier isEqualToNumber:@21]) {
561
+ // IOSIntentIdentifier.START_PHOTO_PLAYBACK
562
+ [intentIdentifiers addObject:INStartPhotoPlaybackIntentIdentifier];
563
+ } else if ([identifier isEqualToNumber:@22]) {
564
+ // IOSIntentIdentifier.LIST_RIDE_OPTIONS
565
+ [intentIdentifiers addObject:INListRideOptionsIntentIdentifier];
566
+ } else if ([identifier isEqualToNumber:@23]) {
567
+ // IOSIntentIdentifier.REQUEST_RIDE
568
+ [intentIdentifiers addObject:INRequestRideIntentIdentifier];
569
+ } else if ([identifier isEqualToNumber:@24]) {
570
+ // IOSIntentIdentifier.GET_RIDE_STATUS
571
+ [intentIdentifiers addObject:INGetRideStatusIntentIdentifier];
572
+ }
573
+ }
574
+
575
+ return intentIdentifiers;
576
+ }
577
+
578
+ /**
579
+ * Returns timestamp in millisecons
580
+ *
581
+ * @param date NSDate
582
+ */
583
+ + (NSNumber *)convertToTimestamp:(NSDate *)date {
584
+ return [NSNumber numberWithDouble:([date timeIntervalSince1970] * 1000)];
585
+ }
586
+
587
+ /**
588
+ * Parse UNNotificationRequest to NSDictionary
589
+ *
590
+ * @param request UNNotificationRequest
591
+ */
592
+ + (NSMutableDictionary *)parseUNNotificationRequest:(UNNotificationRequest *)request {
593
+ NSMutableDictionary *dictionary = [NSMutableDictionary dictionary];
594
+
595
+ dictionary = [self parseUNNotificationContent:request.content];
596
+ dictionary[@"id"] = request.identifier;
597
+
598
+ NSDictionary *userInfo = request.content.userInfo;
599
+
600
+ // Check for remote details
601
+ if ([request.trigger isKindOfClass:[UNPushNotificationTrigger class]]) {
602
+ NSMutableDictionary *remote = [NSMutableDictionary dictionary];
603
+
604
+ remote[@"messageId"] = userInfo[@"gcm.message_id"];
605
+ remote[@"senderId"] = userInfo[@"google.c.sender.id"];
606
+
607
+ if (userInfo[@"aps"] != nil) {
608
+ remote[@"mutableContent"] = userInfo[@"aps"][@"mutable-content"];
609
+ remote[@"contentAvailable"] = userInfo[@"aps"][@"content-available"];
610
+ }
611
+
612
+ dictionary[@"remote"] = remote;
613
+ }
614
+
615
+ dictionary[@"data"] = [self parseDataFromUserInfo:userInfo];
616
+
617
+ return dictionary;
618
+ }
619
+
620
+ + (NSMutableDictionary *)parseDataFromUserInfo:(NSDictionary *)userInfo {
621
+ NSMutableDictionary *data = [[NSMutableDictionary alloc] init];
622
+ for (id key in userInfo) {
623
+ // build data dict from remaining keys but skip keys that shouldn't be included in data
624
+ if ([key isEqualToString:@"aps"] || [key hasPrefix:@"gcm."] || [key hasPrefix:@"google."] ||
625
+ // notifee or notifee_options
626
+ [key hasPrefix:@"notifee"] ||
627
+ // fcm_options
628
+ [key hasPrefix:@"fcm"]) {
629
+ continue;
630
+ }
631
+ data[key] = userInfo[key];
632
+ }
633
+
634
+ return data;
635
+ }
636
+
637
+ + (NSMutableDictionary *)parseUNNotificationContent:(UNNotificationContent *)content {
638
+ NSMutableDictionary *dictionary = [NSMutableDictionary dictionary];
639
+ NSMutableDictionary *iosDict = [NSMutableDictionary dictionary];
640
+
641
+ dictionary[@"subtitle"] = content.subtitle;
642
+ dictionary[@"body"] = content.body;
643
+ dictionary[@"data"] = [content.userInfo mutableCopy];
644
+
645
+ // title
646
+ if (content.title != nil) {
647
+ dictionary[@"title"] = content.title;
648
+ }
649
+
650
+ // subtitle
651
+ if (content.subtitle != nil) {
652
+ dictionary[@"subtitle"] = content.subtitle;
653
+ }
654
+
655
+ // body
656
+ if (content.body != nil) {
657
+ dictionary[@"body"] = content.body;
658
+ }
659
+
660
+ iosDict[@"badgeCount"] = content.badge;
661
+
662
+ // categoryId
663
+ if (content.categoryIdentifier != nil) {
664
+ iosDict[@"categoryId"] = content.categoryIdentifier;
665
+ }
666
+
667
+ // launchImageName
668
+ if (content.launchImageName != nil) {
669
+ iosDict[@"launchImageName"] = content.launchImageName;
670
+ }
671
+
672
+ // threadId
673
+ if (content.threadIdentifier != nil) {
674
+ iosDict[@"threadId"] = content.threadIdentifier;
675
+ }
676
+
677
+ // targetContentId
678
+ if (@available(iOS 13.0, macOS 10.15, macCatalyst 13.0, tvOS 13.0, watchOS 6.0, *)) {
679
+ if (content.targetContentIdentifier != nil) {
680
+ iosDict[@"targetContentId"] = content.targetContentIdentifier;
681
+ }
682
+ }
683
+
684
+ if (content.attachments != nil) {
685
+ // TODO: parse attachments
686
+ }
687
+
688
+ // sound
689
+ if (content.sound != nil) {
690
+ if ([content.sound isKindOfClass:[NSString class]]) {
691
+ iosDict[@"sound"] = content.sound;
692
+ } else if ([content.sound isKindOfClass:[NSDictionary class]]) {
693
+ NSDictionary *soundDict = (NSDictionary *)content.sound;
694
+ NSMutableDictionary *notificationIOSSound = [[NSMutableDictionary alloc] init];
695
+
696
+ // ios.sound.name String
697
+ if (soundDict[@"name"] != nil) {
698
+ notificationIOSSound[@"name"] = soundDict[@"name"];
699
+ }
700
+
701
+ // sound.critical Boolean
702
+ if (soundDict[@"critical"] != nil) {
703
+ notificationIOSSound[@"critical"] = soundDict[@"critical"];
704
+ }
705
+
706
+ // ios.sound.volume Number
707
+ if (soundDict[@"volume"] != nil) {
708
+ notificationIOSSound[@"volume"] = soundDict[@"volume"];
709
+ }
710
+
711
+ // ios.sound
712
+ iosDict[@"sound"] = notificationIOSSound;
713
+ }
714
+ }
715
+
716
+ dictionary[@"ios"] = iosDict;
717
+ return dictionary;
718
+ }
719
+
720
+ + (INSendMessageIntent *)generateSenderIntentForCommunicationNotification:
721
+ (NSDictionary *)communicationInfo {
722
+ if (@available(iOS 15.0, *)) {
723
+ NSDictionary *sender = communicationInfo[@"sender"];
724
+ INPersonHandle *senderPersonHandle =
725
+ [[INPersonHandle alloc] initWithValue:sender[@"id"] type:INPersonHandleTypeUnknown];
726
+
727
+ // Parse sender's avatar
728
+ INImage *avatar = nil;
729
+ if (sender[@"avatar"] != nil) {
730
+ NSURL *url = [self getURLFromString:sender[@"avatar"]];
731
+ avatar = [INImage imageWithURL:url];
732
+ }
733
+
734
+ INPerson *senderPerson = [[INPerson alloc] initWithPersonHandle:senderPersonHandle
735
+ nameComponents:nil
736
+ displayName:sender[@"displayName"]
737
+ image:avatar
738
+ contactIdentifier:nil
739
+ customIdentifier:nil];
740
+
741
+ NSMutableArray *recipients = nil;
742
+
743
+ INSpeakableString *speakableGroupName = nil;
744
+ if (communicationInfo[@"groupName"] != nil) {
745
+ speakableGroupName =
746
+ [[INSpeakableString alloc] initWithSpokenPhrase:communicationInfo[@"groupName"]];
747
+
748
+ // For the `groupName` to work we need to have more than one recipient, otherwise, it won't be
749
+ // recognized as a group communication. For this reason, we are adding a placeholder person to
750
+ // the recipients which is not going to do any harm, the recipients are used as a fallback for
751
+ // when you don't have a `groupName` it concatenates the recipients name and then use that as
752
+ // a group name.
753
+ INPersonHandle *placeholderPersonHandle =
754
+ [[INPersonHandle alloc] initWithValue:@"placeholderId" type:INPersonHandleTypeUnknown];
755
+ INPerson *placeholderPerson = [[INPerson alloc] initWithPersonHandle:placeholderPersonHandle
756
+ nameComponents:nil
757
+ displayName:sender[@"displayName"]
758
+ image:avatar
759
+ contactIdentifier:nil
760
+ customIdentifier:nil];
761
+ recipients = [NSMutableArray array];
762
+ [recipients addObject:senderPerson];
763
+ [recipients addObject:placeholderPerson];
764
+ }
765
+
766
+ INSendMessageIntent *intent =
767
+ [[INSendMessageIntent alloc] initWithRecipients:recipients
768
+ outgoingMessageType:INOutgoingMessageTypeOutgoingMessageText
769
+ content:communicationInfo[@"body"]
770
+ speakableGroupName:speakableGroupName
771
+ conversationIdentifier:communicationInfo[@"conversationId"]
772
+ serviceName:nil
773
+ sender:senderPerson
774
+ attachments:nil];
775
+
776
+ if (communicationInfo[@"groupAvatar"] != nil) {
777
+ NSURL *groupAvatarURL = [[NSURL alloc] initWithString:communicationInfo[@"groupAvatar"]];
778
+ INImage *groupAvatarImage = [INImage imageWithURL:groupAvatarURL];
779
+
780
+ [intent setImage:groupAvatarImage forParameterNamed:@"speakableGroupName"];
781
+ }
782
+
783
+ return intent;
784
+ }
785
+
786
+ return nil;
787
+ }
788
+ /**
789
+ * Returns a random string using UUID
790
+ *
791
+ * @param length int
792
+ */
793
+ + (NSString *)generateCachedFileName:(int)length {
794
+ return [[NSUUID UUID] UUIDString];
795
+ }
796
+
797
+ /**
798
+ * Returns a shared instance of [UIApplication sharedApplication]
799
+ * Needed to prevent compile errors for App extensions when calling [UIApplication
800
+ * sharedApplication]
801
+ *
802
+ * @return instancetype
803
+ */
804
+ + (nullable instancetype)notifeeUIApplication {
805
+ static dispatch_once_t once;
806
+ static NotifeeCoreUtil *sharedInstance;
807
+ dispatch_once(&once, ^{
808
+ static Class applicationClass = nil;
809
+ if (![self isAppExtension]) {
810
+ Class cls = NSClassFromString(@"UIApplication");
811
+ if (cls && [cls respondsToSelector:NSSelectorFromString(@"sharedApplication")]) {
812
+ applicationClass = cls;
813
+ }
814
+ }
815
+
816
+ sharedInstance = (NotifeeCoreUtil *)[applicationClass sharedApplication];
817
+ });
818
+
819
+ return sharedInstance;
820
+ }
821
+
822
+ /**
823
+ * Checks if the current application is an extension
824
+ */
825
+ + (BOOL)isAppExtension {
826
+ #if TARGET_OS_IOS || TARGET_OS_TV || TARGET_OS_WATCH
827
+ BOOL appExtension = [[[NSBundle mainBundle] bundlePath] hasSuffix:@".appex"];
828
+ return appExtension;
829
+ #elif TARGET_OS_OSX
830
+ return NO;
831
+ #endif
832
+ }
833
+
834
+ @end