2017-03-09 15:26:28 +00:00
|
|
|
#import <Foundation/Foundation.h>
|
|
|
|
#import <NotificationCenter/NotificationCenter.h>
|
|
|
|
#if defined(__IPHONE_10_0) && __IPHONE_OS_VERSION_MAX_ALLOWED >= __IPHONE_10_0
|
|
|
|
#import <UserNotifications/UserNotifications.h>
|
|
|
|
#endif
|
|
|
|
#import "RNFirebaseMessaging.h"
|
|
|
|
#import "RNFirebaseEvents.h"
|
|
|
|
#import "RCTConvert.h"
|
|
|
|
|
|
|
|
// https://github.com/facebook/react-native/blob/master/Libraries/PushNotificationIOS/RCTPushNotificationManager.m
|
|
|
|
@implementation RCTConvert (UILocalNotification)
|
|
|
|
|
|
|
|
+ (UILocalNotification *)UILocalNotification:(id)json
|
|
|
|
{
|
2017-03-15 12:57:36 +00:00
|
|
|
NSDictionary<NSString *, id> *details = [self NSDictionary:json];
|
|
|
|
UILocalNotification *notification = [UILocalNotification new];
|
|
|
|
notification.fireDate = [RCTConvert NSDate:details[@"fireDate"]] ?: [NSDate date];
|
|
|
|
notification.alertBody = [RCTConvert NSString:details[@"alertBody"]];
|
|
|
|
notification.alertAction = [RCTConvert NSString:details[@"alertAction"]];
|
|
|
|
notification.soundName = [RCTConvert NSString:details[@"soundName"]] ?: UILocalNotificationDefaultSoundName;
|
|
|
|
notification.userInfo = [RCTConvert NSDictionary:details[@"userInfo"]];
|
|
|
|
notification.category = [RCTConvert NSString:details[@"category"]];
|
|
|
|
if (details[@"applicationIconBadgeNumber"]) {
|
|
|
|
notification.applicationIconBadgeNumber = [RCTConvert NSInteger:details[@"applicationIconBadgeNumber"]];
|
|
|
|
}
|
|
|
|
return notification;
|
2017-03-09 15:26:28 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
@end
|
|
|
|
|
|
|
|
@implementation RNFirebaseMessaging
|
|
|
|
|
|
|
|
// https://github.com/facebook/react-native/blob/master/Libraries/PushNotificationIOS/RCTPushNotificationManager.m
|
|
|
|
static NSDictionary *RCTFormatLocalNotification(UILocalNotification *notification)
|
|
|
|
{
|
2017-03-15 12:57:36 +00:00
|
|
|
NSMutableDictionary *formattedLocalNotification = [NSMutableDictionary dictionary];
|
|
|
|
if (notification.fireDate) {
|
|
|
|
NSDateFormatter *formatter = [NSDateFormatter new];
|
|
|
|
[formatter setDateFormat:@"yyyy-MM-dd'T'HH:mm:ss.SSSZZZZZ"];
|
|
|
|
NSString *fireDateString = [formatter stringFromDate:notification.fireDate];
|
|
|
|
formattedLocalNotification[@"fireDate"] = fireDateString;
|
|
|
|
}
|
|
|
|
formattedLocalNotification[@"alertAction"] = RCTNullIfNil(notification.alertAction);
|
|
|
|
formattedLocalNotification[@"alertBody"] = RCTNullIfNil(notification.alertBody);
|
|
|
|
formattedLocalNotification[@"applicationIconBadgeNumber"] = @(notification.applicationIconBadgeNumber);
|
|
|
|
formattedLocalNotification[@"category"] = RCTNullIfNil(notification.category);
|
|
|
|
formattedLocalNotification[@"soundName"] = RCTNullIfNil(notification.soundName);
|
|
|
|
formattedLocalNotification[@"userInfo"] = RCTNullIfNil(RCTJSONClean(notification.userInfo));
|
|
|
|
formattedLocalNotification[@"remote"] = @NO;
|
|
|
|
return formattedLocalNotification;
|
2017-03-09 15:26:28 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
- (void) dealloc
|
|
|
|
{
|
2017-03-15 12:57:36 +00:00
|
|
|
[[NSNotificationCenter defaultCenter] removeObserver: self];
|
2017-03-09 15:26:28 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
+ (void) setup:(UIApplication *) application
|
|
|
|
{
|
2017-03-15 12:57:36 +00:00
|
|
|
[[NSNotificationCenter defaultCenter] addObserver:self
|
|
|
|
selector:@selector(connectToFirebase)
|
|
|
|
name: UIApplicationDidEnterBackgroundNotification
|
|
|
|
object: nil];
|
|
|
|
|
|
|
|
[[NSNotificationCenter defaultCenter] addObserver:self
|
|
|
|
selector:@selector(disconnectFromFirebase)
|
|
|
|
name: UIApplicationDidBecomeActiveNotification
|
|
|
|
object: nil];
|
|
|
|
|
|
|
|
[[NSNotificationCenter defaultCenter] addObserver:self
|
|
|
|
selector:@selector(handleRemoteNotificationReceived:)
|
|
|
|
name:MESSAGING_MESSAGE_RECEIVED_REMOTE
|
|
|
|
object: nil];
|
|
|
|
|
|
|
|
[[NSNotificationCenter defaultCenter] addObserver:self
|
|
|
|
selector:@selector(handleLocalNotificationReceived:)
|
|
|
|
name:MESSAGING_MESSAGE_RECEIVED_LOCAL
|
|
|
|
object: nil];
|
|
|
|
|
|
|
|
[[NSNotificationCenter defaultCenter] addObserver:self
|
|
|
|
selector:@selector(handleTokenRefresh)
|
|
|
|
name:kFIRInstanceIDTokenRefreshNotification
|
|
|
|
object: nil];
|
|
|
|
|
|
|
|
if (SYSTEM_VERSION_LESS_THAN_OR_EQUAL_TO(@"9.0")) {
|
|
|
|
UIUserNotificationType allNotificationTypes =
|
|
|
|
(UIUserNotificationTypeSound | UIUserNotificationTypeAlert | UIUserNotificationTypeBadge);
|
|
|
|
UIUserNotificationSettings *settings =
|
|
|
|
[UIUserNotificationSettings settingsForTypes:allNotificationTypes categories:nil];
|
|
|
|
[[UIApplication sharedApplication] registerUserNotificationSettings:settings];
|
|
|
|
} else {
|
|
|
|
// iOS 10 or later
|
|
|
|
#if defined(__IPHONE_10_0) && __IPHONE_OS_VERSION_MAX_ALLOWED >= __IPHONE_10_0
|
|
|
|
UNAuthorizationOptions authOptions =
|
|
|
|
UNAuthorizationOptionAlert
|
|
|
|
| UNAuthorizationOptionSound
|
|
|
|
| UNAuthorizationOptionBadge;
|
|
|
|
[[UNUserNotificationCenter currentNotificationCenter]
|
|
|
|
requestAuthorizationWithOptions:authOptions
|
|
|
|
completionHandler:^(BOOL granted, NSError * _Nullable error) {
|
|
|
|
}
|
|
|
|
];
|
|
|
|
|
|
|
|
// For iOS 10 display notification (sent via APNS)
|
|
|
|
[[UNUserNotificationCenter currentNotificationCenter] setDelegate:self];
|
|
|
|
// For iOS 10 data message (sent via FCM)
|
|
|
|
[[FIRMessaging messaging] setRemoteMessageDelegate:self];
|
|
|
|
#endif
|
|
|
|
}
|
|
|
|
|
|
|
|
[[UIApplication sharedApplication] registerForRemoteNotifications];
|
2017-03-09 15:26:28 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
#pragma mark callbacks
|
|
|
|
- (void) connectToFirebase
|
|
|
|
{
|
2017-03-15 12:57:36 +00:00
|
|
|
[[FIRMessaging messaging] connectWithCompletion:^(NSError *error) {
|
|
|
|
NSDictionary *evt;
|
|
|
|
NSString *evtName;
|
|
|
|
if (error != nil) {
|
|
|
|
NSLog(@"Error connecting: %@", [error debugDescription]);
|
|
|
|
evtName = MESSAGING_SUBSYSTEM_ERROR;
|
|
|
|
evt = @{
|
|
|
|
@"eventName": MESSAGING_SUBSYSTEM_ERROR,
|
|
|
|
@"msg": [error debugDescription]
|
|
|
|
};
|
|
|
|
} else {
|
|
|
|
NSLog(@"Connected to Firebase messaging");
|
|
|
|
evtName = MESSAGING_SUBSYSTEM_EVENT;
|
|
|
|
evt = @{
|
|
|
|
@"result": @"connected"
|
|
|
|
};
|
|
|
|
[self
|
|
|
|
sendJSEvent:evtName
|
|
|
|
props: evt];
|
|
|
|
|
|
|
|
}
|
|
|
|
}];
|
2017-03-09 15:26:28 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
- (void) disconnectFromFirebase
|
|
|
|
{
|
2017-03-15 12:57:36 +00:00
|
|
|
[[FIRMessaging messaging] disconnect];
|
|
|
|
NSLog(@"Disconnect from Firebase");
|
|
|
|
[self
|
2017-03-09 15:26:28 +00:00
|
|
|
sendJSEvent:MESSAGING_SUBSYSTEM_EVENT
|
|
|
|
props: @{
|
2017-03-15 12:57:36 +00:00
|
|
|
@"status": @"disconnected"
|
|
|
|
}];
|
2017-03-09 15:26:28 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
- (void) handleRemoteNotificationReceived:(NSNotification *) n
|
|
|
|
{
|
2017-03-15 12:57:36 +00:00
|
|
|
NSMutableDictionary *props = [[NSMutableDictionary alloc] initWithDictionary: n.userInfo];
|
|
|
|
[self sendJSEvent:MESSAGING_MESSAGE_RECEIVED_REMOTE props: props];
|
2017-03-09 15:26:28 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
- (void) handleLocalNotificationReceived:(NSNotification *) n
|
|
|
|
{
|
2017-03-15 12:57:36 +00:00
|
|
|
NSMutableDictionary *props = [[NSMutableDictionary alloc] initWithDictionary: n.userInfo];
|
|
|
|
[self sendJSEvent:MESSAGING_MESSAGE_RECEIVED_LOCAL props: props];
|
2017-03-09 15:26:28 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
- (void) handleTokenRefresh
|
|
|
|
{
|
2017-03-15 12:57:36 +00:00
|
|
|
NSDictionary *props = @{
|
|
|
|
@"status": @"token_refreshed",
|
|
|
|
@"token": [[FIRInstanceID instanceID] token]
|
|
|
|
};
|
|
|
|
[self sendJSEvent:MESSAGING_TOKEN_REFRESH props: props];
|
2017-03-09 15:26:28 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
RCT_EXPORT_MODULE(RNFirebaseMessaging);
|
|
|
|
|
|
|
|
|
2017-03-15 12:57:36 +00:00
|
|
|
RCT_EXPORT_METHOD(getToken:(RCTPromiseResolveBlock) resolve rejecter:(RCTPromiseRejectBlock) reject) {
|
|
|
|
NSString *token = [[FIRInstanceID instanceID] token];
|
|
|
|
resolve(token);
|
2017-03-09 15:26:28 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
RCT_EXPORT_METHOD(sendLocal:(UILocalNotification *)notification
|
2017-03-15 12:57:36 +00:00
|
|
|
callback:(RCTResponseSenderBlock) callback)
|
2017-03-09 15:26:28 +00:00
|
|
|
{
|
2017-03-15 12:57:36 +00:00
|
|
|
NSLog(@"sendLocal called with notification: %@", notification);
|
|
|
|
[RCTSharedApplication() presentLocalNotificationNow:notification];
|
2017-03-09 15:26:28 +00:00
|
|
|
}
|
|
|
|
RCT_EXPORT_METHOD(scheduleLocal:(UILocalNotification *)notification
|
2017-03-15 12:57:36 +00:00
|
|
|
callback:(RCTResponseSenderBlock) callback)
|
2017-03-09 15:26:28 +00:00
|
|
|
{
|
2017-03-15 12:57:36 +00:00
|
|
|
[RCTSharedApplication() scheduleLocalNotification:notification];
|
2017-03-09 15:26:28 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
RCT_EXPORT_METHOD(cancelAllLocalNotifications)
|
|
|
|
{
|
2017-03-15 12:57:36 +00:00
|
|
|
[RCTSharedApplication() cancelAllLocalNotifications];
|
2017-03-09 15:26:28 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
RCT_EXPORT_METHOD(cancelLocalNotifications:(NSDictionary<NSString *, id> *)userInfo)
|
|
|
|
{
|
2017-03-15 12:57:36 +00:00
|
|
|
for (UILocalNotification *notification in [UIApplication sharedApplication].scheduledLocalNotifications) {
|
|
|
|
__block BOOL matchesAll = YES;
|
|
|
|
NSDictionary<NSString *, id> *notificationInfo = notification.userInfo;
|
|
|
|
// Note: we do this with a loop instead of just `isEqualToDictionary:`
|
|
|
|
// because we only require that all specified userInfo values match the
|
|
|
|
// notificationInfo values - notificationInfo may contain additional values
|
|
|
|
// which we don't care about.
|
|
|
|
[userInfo enumerateKeysAndObjectsUsingBlock:^(NSString *key, id obj, BOOL *stop) {
|
|
|
|
if (![notificationInfo[key] isEqual:obj]) {
|
|
|
|
matchesAll = NO;
|
|
|
|
*stop = YES;
|
|
|
|
}
|
|
|
|
}];
|
|
|
|
if (matchesAll) {
|
|
|
|
[[UIApplication sharedApplication] cancelLocalNotification:notification];
|
|
|
|
}
|
2017-03-09 15:26:28 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2017-03-15 14:00:28 +00:00
|
|
|
RCT_EXPORT_METHOD(send:(NSDictionary *) message resolver:(RCTPromiseResolveBlock) resolve rejecter:(RCTPromiseRejectBlock) reject){
|
|
|
|
// todo
|
|
|
|
resolve([NSNull null]);
|
2017-03-09 15:26:28 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
RCT_EXPORT_METHOD(listenForTokenRefresh:(RCTResponseSenderBlock)callback)
|
|
|
|
{}
|
|
|
|
|
|
|
|
RCT_EXPORT_METHOD(unlistenForTokenRefresh:(RCTResponseSenderBlock)callback)
|
|
|
|
{}
|
|
|
|
|
|
|
|
RCT_EXPORT_METHOD(subscribeToTopic:(NSString *) topic
|
2017-03-15 12:57:36 +00:00
|
|
|
callback:(RCTResponseSenderBlock)callback)
|
2017-03-09 15:26:28 +00:00
|
|
|
{
|
2017-03-15 12:57:36 +00:00
|
|
|
[[FIRMessaging messaging] subscribeToTopic:topic];
|
|
|
|
callback(@[[NSNull null], @{
|
|
|
|
@"result": @"success",
|
|
|
|
@"topic": topic
|
|
|
|
}]);
|
2017-03-09 15:26:28 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
RCT_EXPORT_METHOD(unsubscribeFromTopic:(NSString *) topic
|
2017-03-15 12:57:36 +00:00
|
|
|
callback: (RCTResponseSenderBlock)callback)
|
2017-03-09 15:26:28 +00:00
|
|
|
{
|
2017-03-15 12:57:36 +00:00
|
|
|
[[FIRMessaging messaging] unsubscribeFromTopic:topic];
|
|
|
|
callback(@[[NSNull null], @{
|
|
|
|
@"result": @"success",
|
|
|
|
@"topic": topic
|
|
|
|
}]);
|
2017-03-09 15:26:28 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
RCT_EXPORT_METHOD(setBadge:(NSInteger) number
|
2017-03-15 12:57:36 +00:00
|
|
|
callback:(RCTResponseSenderBlock) callback)
|
2017-03-09 15:26:28 +00:00
|
|
|
{
|
2017-03-15 12:57:36 +00:00
|
|
|
RCTSharedApplication().applicationIconBadgeNumber = number;
|
|
|
|
callback(@[[NSNull null], @{
|
|
|
|
@"result": @"success",
|
|
|
|
@"number": @(number)
|
|
|
|
}]);
|
2017-03-09 15:26:28 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
RCT_EXPORT_METHOD(getBadge:(RCTResponseSenderBlock) callback)
|
|
|
|
{
|
2017-03-15 12:57:36 +00:00
|
|
|
callback(@[[NSNull null], @{
|
|
|
|
@"result": @"success",
|
|
|
|
@"number": @(RCTSharedApplication().applicationIconBadgeNumber)
|
|
|
|
}]);
|
2017-03-09 15:26:28 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
RCT_EXPORT_METHOD(listenForReceiveNotification:(RCTResponseSenderBlock)callback)
|
|
|
|
{}
|
|
|
|
|
|
|
|
RCT_EXPORT_METHOD(unlistenForReceiveNotification:(RCTResponseSenderBlock)callback)
|
|
|
|
{}
|
|
|
|
|
|
|
|
RCT_EXPORT_METHOD(listenForReceiveUpstreamSend:(RCTResponseSenderBlock)callback)
|
|
|
|
{}
|
|
|
|
|
|
|
|
RCT_EXPORT_METHOD(unlistenForReceiveUpstreamSend:(RCTResponseSenderBlock)callback)
|
|
|
|
{}
|
|
|
|
|
|
|
|
// Not sure how to get away from this... yet
|
|
|
|
- (NSArray<NSString *> *)supportedEvents {
|
|
|
|
return @[
|
2017-03-15 12:57:36 +00:00
|
|
|
MESSAGING_SUBSYSTEM_EVENT,
|
|
|
|
MESSAGING_SUBSYSTEM_ERROR,
|
|
|
|
MESSAGING_TOKEN_REFRESH,
|
|
|
|
MESSAGING_MESSAGE_RECEIVED_LOCAL,
|
|
|
|
MESSAGING_MESSAGE_RECEIVED_REMOTE];
|
2017-03-09 15:26:28 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
- (void) sendJSEvent:(NSString *)title
|
|
|
|
props:(NSDictionary *)props
|
|
|
|
{
|
|
|
|
@try {
|
|
|
|
[self sendEventWithName:title
|
|
|
|
body:@{
|
2017-03-15 12:57:36 +00:00
|
|
|
@"eventName": title,
|
|
|
|
@"body": props
|
|
|
|
}];
|
2017-03-09 15:26:28 +00:00
|
|
|
}
|
|
|
|
@catch (NSException *err) {
|
|
|
|
NSLog(@"An error occurred in sendJSEvent: %@", [err debugDescription]);
|
|
|
|
NSLog(@"Tried to send: %@ with %@", title, props);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
@end
|