66// Copyright © 2016 Facebook. All rights reserved.
77//
88
9- #import " FirestackCloudMessaging.h "
9+ #import < Foundation/Foundation.h >
1010#import < NotificationCenter/NotificationCenter.h>
11+ #import " FirestackCloudMessaging.h"
12+ #import " FirestackEvents.h"
13+ #import " RCTConvert.h"
1114
1215@import FirebaseInstanceID;
1316@import FirebaseMessaging;
1417
18+ // https://github.com/facebook/react-native/blob/master/Libraries/PushNotificationIOS/RCTPushNotificationManager.m
19+ @implementation RCTConvert (UILocalNotification)
20+
21+ + (UILocalNotification *)UILocalNotification : (id )json
22+ {
23+ NSDictionary <NSString *, id > *details = [self NSDictionary: json];
24+ UILocalNotification *notification = [UILocalNotification new ];
25+ notification.fireDate = [RCTConvert NSDate: details[@" fireDate" ]] ?: [NSDate date ];
26+ notification.alertBody = [RCTConvert NSString: details[@" alertBody" ]];
27+ notification.alertAction = [RCTConvert NSString: details[@" alertAction" ]];
28+ notification.soundName = [RCTConvert NSString: details[@" soundName" ]] ?: UILocalNotificationDefaultSoundName;
29+ notification.userInfo = [RCTConvert NSDictionary: details[@" userInfo" ]];
30+ notification.category = [RCTConvert NSString: details[@" category" ]];
31+ if (details[@" applicationIconBadgeNumber" ]) {
32+ notification.applicationIconBadgeNumber = [RCTConvert NSInteger: details[@" applicationIconBadgeNumber" ]];
33+ }
34+ return notification;
35+ }
36+
37+ @end
38+
1539@implementation FirestackCloudMessaging
1640
17- + (void ) registerForNotification : (NSString *) typeStr andToken : (NSData *)deviceToken
41+ // https://github.com/facebook/react-native/blob/master/Libraries/PushNotificationIOS/RCTPushNotificationManager.m
42+ static NSDictionary *RCTFormatLocalNotification (UILocalNotification *notification)
43+ {
44+ NSMutableDictionary *formattedLocalNotification = [NSMutableDictionary dictionary ];
45+ if (notification.fireDate ) {
46+ NSDateFormatter *formatter = [NSDateFormatter new ];
47+ [formatter setDateFormat: @" yyyy-MM-dd'T'HH:mm:ss.SSSZZZZZ" ];
48+ NSString *fireDateString = [formatter stringFromDate: notification.fireDate];
49+ formattedLocalNotification[@" fireDate" ] = fireDateString;
50+ }
51+ formattedLocalNotification[@" alertAction" ] = RCTNullIfNil (notification.alertAction );
52+ formattedLocalNotification[@" alertBody" ] = RCTNullIfNil (notification.alertBody );
53+ formattedLocalNotification[@" applicationIconBadgeNumber" ] = @(notification.applicationIconBadgeNumber );
54+ formattedLocalNotification[@" category" ] = RCTNullIfNil (notification.category );
55+ formattedLocalNotification[@" soundName" ] = RCTNullIfNil (notification.soundName );
56+ formattedLocalNotification[@" userInfo" ] = RCTNullIfNil (RCTJSONClean (notification.userInfo ));
57+ formattedLocalNotification[@" remote" ] = @NO ;
58+ return formattedLocalNotification;
59+ }
60+
61+ - (void ) dealloc
1862{
63+ [[NSNotificationCenter defaultCenter ] removeObserver: self ];
64+ }
65+
66+ + (void ) setup : (UIApplication *) application
67+ {
68+ [[NSNotificationCenter defaultCenter ] addObserver: self
69+ selector: @selector (connectToFirebase )
70+ name: UIApplicationDidEnterBackgroundNotification
71+ object: nil ];
72+
73+ [[NSNotificationCenter defaultCenter ] addObserver: self
74+ selector: @selector (disconnectFromFirebase )
75+ name: UIApplicationDidBecomeActiveNotification
76+ object: nil ];
77+
78+ [[NSNotificationCenter defaultCenter ] addObserver: self
79+ selector: @selector (handleRemoteNotificationReceived: )
80+ name: MESSAGING_MESSAGE_RECEIVED_REMOTE
81+ object: nil ];
82+
83+ [[NSNotificationCenter defaultCenter ] addObserver: self
84+ selector: @selector (handleLocalNotificationReceived: )
85+ name: MESSAGING_MESSAGE_RECEIVED_LOCAL
86+ object: nil ];
87+
88+ [[NSNotificationCenter defaultCenter ] addObserver: self
89+ selector: @selector (handleTokenRefresh )
90+ name: kFIRInstanceIDTokenRefreshNotification
91+ object: nil ];
92+
93+ if (SYSTEM_VERSION_LESS_THAN_OR_EQUAL_TO (@" 9.0" )) {
1994 UIUserNotificationType allNotificationTypes =
2095 (UIUserNotificationTypeSound | UIUserNotificationTypeAlert | UIUserNotificationTypeBadge);
2196 UIUserNotificationSettings *settings =
2297 [UIUserNotificationSettings settingsForTypes: allNotificationTypes categories: nil ];
2398 [[UIApplication sharedApplication ] registerUserNotificationSettings: settings];
24- [[UIApplication sharedApplication ] registerForRemoteNotifications ];
99+ } else {
100+ // iOS 10 or later
101+ #if defined(__IPHONE_10_0) && __IPHONE_OS_VERSION_MAX_ALLOWED >= __IPHONE_10_0
102+ UNAuthorizationOptions authOptions =
103+ UNAuthorizationOptionAlert
104+ | UNAuthorizationOptionSound
105+ | UNAuthorizationOptionBadge ;
106+ [[UNUserNotificationCenter currentNotificationCenter ]
107+ requestAuthorizationWithOptions: authOptions
108+ completionHandler: ^(BOOL granted, NSError * _Nullable error) {
109+ }
110+ ];
111+
112+ // For iOS 10 display notification (sent via APNS)
113+ [[UNUserNotificationCenter currentNotificationCenter ] setDelegate: self ];
114+ // For iOS 10 data message (sent via FCM)
115+ [[FIRMessaging messaging ] setRemoteMessageDelegate: self ];
116+ #endif
117+ }
118+
119+ [[UIApplication sharedApplication ] registerForRemoteNotifications ];
120+ }
121+
122+ #pragma mark callbacks
123+ - (void ) connectToFirebase
124+ {
125+ [[FIRMessaging messaging ] connectWithCompletion: ^(NSError *error) {
126+ NSDictionary *evt;
127+ NSString *evtName;
128+ if (error != nil ) {
129+ NSLog (@" Error connecting: %@ " , [error debugDescription ]);
130+ evtName = MESSAGING_SUBSYSTEM_ERROR;
131+ evt = @{
132+ @" eventName" : MESSAGING_SUBSYSTEM_ERROR,
133+ @" msg" : [error debugDescription ]
134+ };
135+ } else {
136+ NSLog (@" Connected to Firebase messaging" );
137+ evtName = MESSAGING_SUBSYSTEM_EVENT;
138+ evt = @{
139+ @" result" : @" connected"
140+ };
141+ [self
142+ sendJSEvent: evtName
143+ props: evt];
25144
26- [[FIRMessaging messaging ] connectWithCompletion: ^(NSError * _Nullable error) {
27- NSLog (@" Connected: %@ " , [error debugDescription ]);
28- }];
145+ }
146+ }];
29147}
30148
31- RCT_EXPORT_MODULE (FirestackCloudMessaging);
149+ - (void ) disconnectFromFirebase
150+ {
151+ [[FIRMessaging messaging ] disconnect ];
152+ NSLog (@" Disconnect from Firebase" );
153+ [self
154+ sendJSEvent: MESSAGING_SUBSYSTEM_EVENT
155+ props: @{
156+ @" status" : @" disconnected"
157+ }];
158+ }
32159
33- + (void ) getToken : ( NSString *) typeStr andToken : ( NSData *) deviceToken
160+ - (void ) handleRemoteNotificationReceived : ( NSNotification *) n
34161{
162+ NSMutableDictionary *props = [[NSMutableDictionary alloc ] initWithDictionary: n.userInfo];
163+ [self sendJSEvent: MESSAGING_MESSAGE_RECEIVED_REMOTE props: props];
35164}
36165
166+ - (void ) handleLocalNotificationReceived : (NSNotification *) n
167+ {
168+ NSMutableDictionary *props = [[NSMutableDictionary alloc ] initWithDictionary: n.userInfo];
169+ [self sendJSEvent: MESSAGING_MESSAGE_RECEIVED_LOCAL props: props];
170+ }
171+
172+ - (void ) handleTokenRefresh
173+ {
174+ NSDictionary *props = @{
175+ @" status" : @" token_refreshed" ,
176+ @" token" : [[FIRInstanceID instanceID ] token ]
177+ };
178+ [self sendJSEvent: MESSAGING_TOKEN_REFRESH props: props];
179+ }
180+
181+ RCT_EXPORT_MODULE (FirestackCloudMessaging);
182+
183+
37184RCT_EXPORT_METHOD (getToken:(RCTResponseSenderBlock)callback)
38- {}
185+ {
186+ NSString *token = [[FIRInstanceID instanceID ] token ];
187+ callback (@[[NSNull null ], @{
188+ @" status" : @" success" ,
189+ @" token" : token
190+ }]);
191+ }
192+
193+ RCT_EXPORT_METHOD (sendLocal:(UILocalNotification *)notification
194+ callback:(RCTResponseSenderBlock) callback)
195+ {
196+ NSLog (@" sendLocal called with notification: %@ " , notification);
197+ [RCTSharedApplication () presentLocalNotificationNow: notification];
198+ }
199+ RCT_EXPORT_METHOD (scheduleLocal:(UILocalNotification *)notification
200+ callback:(RCTResponseSenderBlock) callback)
201+ {
202+ [RCTSharedApplication () scheduleLocalNotification: notification];
203+ }
204+
205+ RCT_EXPORT_METHOD (cancelAllLocalNotifications)
206+ {
207+ [RCTSharedApplication () cancelAllLocalNotifications ];
208+ }
209+
210+ RCT_EXPORT_METHOD (cancelLocalNotifications:(NSDictionary <NSString *, id > *)userInfo)
211+ {
212+ for (UILocalNotification *notification in [UIApplication sharedApplication ].scheduledLocalNotifications ) {
213+ __block BOOL matchesAll = YES ;
214+ NSDictionary <NSString *, id > *notificationInfo = notification.userInfo ;
215+ // Note: we do this with a loop instead of just `isEqualToDictionary:`
216+ // because we only require that all specified userInfo values match the
217+ // notificationInfo values - notificationInfo may contain additional values
218+ // which we don't care about.
219+ [userInfo enumerateKeysAndObjectsUsingBlock: ^(NSString *key, id obj, BOOL *stop) {
220+ if (![notificationInfo[key] isEqual: obj]) {
221+ matchesAll = NO ;
222+ *stop = YES ;
223+ }
224+ }];
225+ if (matchesAll) {
226+ [[UIApplication sharedApplication ] cancelLocalNotification: notification];
227+ }
228+ }
229+ }
230+
231+ RCT_EXPORT_METHOD (sendRemote:(UILocalNotification *)notification
232+ callback:(RCTResponseSenderBlock) callback)
233+ {
234+
235+ }
236+
39237
40238RCT_EXPORT_METHOD (send:(NSString *) senderId
41239 messageId:(NSString *) messageId
42240 messageType:(NSString *) messageType
43241 msg: (NSString *) msg
44242 callback:(RCTResponseSenderBlock)callback)
45- {}
243+ {
244+
245+ }
46246
47247RCT_EXPORT_METHOD (listenForTokenRefresh:(RCTResponseSenderBlock)callback)
48248{}
@@ -52,11 +252,41 @@ + (void) getToken:(NSString *) typeStr andToken:(NSData *)deviceToken
52252
53253RCT_EXPORT_METHOD (subscribeToTopic:(NSString *) topic
54254 callback:(RCTResponseSenderBlock)callback)
55- {}
255+ {
256+ [[FIRMessaging messaging ] subscribeToTopic: topic];
257+ callback (@[[NSNull null ], @{
258+ @" result" : @" success" ,
259+ @" topic" : topic
260+ }]);
261+ }
56262
57263RCT_EXPORT_METHOD (unsubscribeFromTopic:(NSString *) topic
58264 callback: (RCTResponseSenderBlock)callback)
59- {}
265+ {
266+ [[FIRMessaging messaging ] unsubscribeFromTopic: topic];
267+ callback (@[[NSNull null ], @{
268+ @" result" : @" success" ,
269+ @" topic" : topic
270+ }]);
271+ }
272+
273+ RCT_EXPORT_METHOD (setBadge:(NSInteger ) number
274+ callback:(RCTResponseSenderBlock) callback)
275+ {
276+ RCTSharedApplication ().applicationIconBadgeNumber = number;
277+ callback (@[[NSNull null ], @{
278+ @" result" : @" success" ,
279+ @" number" : @(number)
280+ }]);
281+ }
282+
283+ RCT_EXPORT_METHOD (getBadge:(RCTResponseSenderBlock) callback)
284+ {
285+ callback (@[[NSNull null ], @{
286+ @" result" : @" success" ,
287+ @" number" : @(RCTSharedApplication ().applicationIconBadgeNumber )
288+ }]);
289+ }
60290
61291RCT_EXPORT_METHOD (listenForReceiveNotification:(RCTResponseSenderBlock)callback)
62292{}
@@ -70,4 +300,30 @@ + (void) getToken:(NSString *) typeStr andToken:(NSData *)deviceToken
70300RCT_EXPORT_METHOD (unlistenForReceiveUpstreamSend:(RCTResponseSenderBlock)callback)
71301{}
72302
303+ // Not sure how to get away from this... yet
304+ - (NSArray <NSString *> *)supportedEvents {
305+ return @[
306+ MESSAGING_SUBSYSTEM_EVENT,
307+ MESSAGING_SUBSYSTEM_ERROR,
308+ MESSAGING_TOKEN_REFRESH,
309+ MESSAGING_MESSAGE_RECEIVED_LOCAL,
310+ MESSAGING_MESSAGE_RECEIVED_REMOTE ];
311+ }
312+
313+ - (void ) sendJSEvent : (NSString *)title
314+ props : (NSDictionary *)props
315+ {
316+ @try {
317+ [self sendEventWithName: title
318+ body: @{
319+ @" eventName" : title,
320+ @" body" : props
321+ }];
322+ }
323+ @catch (NSException *err) {
324+ NSLog (@" An error occurred in sendJSEvent: %@ " , [err debugDescription ]);
325+ NSLog (@" Tried to send: %@ with %@ " , title, props);
326+ }
327+ }
328+
73329@end
0 commit comments