ios/AliyunReactNativePush.mm (462 lines of code) (raw):
#import "AliyunReactNativePush.h"
#import <React/RCTBridge.h>
#import <React/RCTConvert.h>
#import <React/RCTEventDispatcher.h>
#import <CloudPushSDK/CloudPushSDK.h>
static NSString* const KEY_CODE = @"code";
static NSString* const KEY_ERROR_MSG = @"errorMsg";
static NSString* const CODE_SUCCESS = @"10000";
static NSString* const CODE_PARAMS_ILLEGAL = @"10001";
static NSString* const CODE_FAILED = @"10002";
static NSString* const kRemoteDeviceTokenRegistration = @"RemoteDeviceTokenRegistration";
static NSString* const kRemoteDeviceTokenRegisterError = @"RemoteDeviceTokenRegisterError";
static NSString* const kReceiveRemoteNotification = @"ReceiveRemoteNotification";
static NSString* const kForegroundReceiveNotification = @"ForegroundReceiveNotification";
static NSString* const kNotificationAction = @"NotificationAction";
@interface AliyunPushLog : NSObject
+ (void)enableLog;
+ (BOOL)isLogEnabled;
+ (void)disableLog;
#define PushLogD(frmt, ...)\
if ([AliyunPushLog isLogEnabled]) {\
NSLog(@"[CloudPush Debug]: %@", [NSString stringWithFormat:(frmt), ##__VA_ARGS__]);\
}
#define PushLogE(frmt, ...)\
if ([AliyunPushLog isLogEnabled]) {\
NSLog(@"[CloudPush Error]: %@", [NSString stringWithFormat:(frmt), ##__VA_ARGS__]);\
}
@end
static BOOL logEnable = YES;
@implementation AliyunPushLog
+ (void)enableLog {
logEnable = YES;
}
+ (BOOL)isLogEnabled {
return logEnable;
}
+ (void)disableLog {
logEnable = NO;
}
@end
@implementation AliyunPush {
BOOL _showNoticeWhenForeground;
}
RCT_EXPORT_MODULE()
+ (BOOL)requiresMainQueueSetup
{
return YES;
}
- (id) init {
self = [super init];
NSNotificationCenter *center = [NSNotificationCenter defaultCenter];
[center addObserver:self selector:@selector(handleDeviceTokenRegistered:) name:kRemoteDeviceTokenRegistration object:nil];
[center addObserver:self selector:@selector(handleDeviceTokenRegisterError:) name:kRemoteDeviceTokenRegisterError object:nil];
[center addObserver:self selector:@selector(handleReceiveRemoteNotification:) name:kReceiveRemoteNotification object:nil];
[center addObserver:self selector:@selector(handleForegroundReceiveNotification:) name:kForegroundReceiveNotification object:nil];
[center addObserver:self selector:@selector(handleNotificationAction:) name:kNotificationAction object:nil];
return self;
}
- (NSArray<NSString *> *)supportedEvents
{
return @[@"AliyunPush_onRegisterDeviceTokenSuccess",
@"AliyunPush_onRegisterDeviceTokenFailed",
@"AliyunPush_onNotification",
@"AliyunPush_onNotificationOpened",
@"AliyunPush_onNotificationRemoved",
@"AliyunPush_onChannelOpened",
@"AliyunPush_onMessage"
];
}
+ (void) didRegisterForRemoteNotificationsWithDeviceToken:(NSData *)deviceToken {
[[NSNotificationCenter defaultCenter] postNotificationName:kRemoteDeviceTokenRegistration object:self userInfo:@{@"deviceToken": deviceToken}];
}
+ (void) didFailToRegisterForRemoteNotificationsWithError:(NSError *)error {
[[NSNotificationCenter defaultCenter] postNotificationName:kRemoteDeviceTokenRegisterError object:self userInfo:@{@"error": error}];
}
+ (void) didReceiveRemoteNotification:(NSDictionary *)userInfo {
[[NSNotificationCenter defaultCenter] postNotificationName:kReceiveRemoteNotification object:self userInfo:@{@"notification": userInfo}];
}
+ (void) didReceiveRemoteNotifiaction:(NSDictionary *)userInfo fetchCompletionHandler:(AliyunPushRemoteNotificationCallback)completionHandler {
[[NSNotificationCenter defaultCenter] postNotificationName:kReceiveRemoteNotification object:self userInfo:@{@"notification": userInfo, @"completionHandler": completionHandler}];
}
+ (void) userNotificationCenter:(UNUserNotificationCenter *)center willPresentNotification:(UNNotification *)notification withCompletionHandler:(void (^)(UNNotificationPresentationOptions))completionHandler{
[[NSNotificationCenter defaultCenter] postNotificationName:kForegroundReceiveNotification object:self userInfo:@{@"notification": notification, @"completionHandler": completionHandler}];
}
+ (void) userNotificationCenter:(UNUserNotificationCenter *)center didReceiveNotificationResponse:(UNNotificationResponse *)response withCompletionHandler:(void (^)(void))completionHandler {
[[NSNotificationCenter defaultCenter] postNotificationName:kNotificationAction object:self userInfo:@{@"response": response, @"completionHandler": completionHandler}];
}
- (void) handleDeviceTokenRegistered: (NSNotification *)notification {
NSData* deviceToken = notification.userInfo[@"deviceToken"];
[CloudPushSDK registerDevice:deviceToken withCallback:^(CloudPushCallbackResult *res) {
if (res.success) {
PushLogD(@"Register deviceToken successfully, deviceToken: %@",[CloudPushSDK getApnsDeviceToken]);
NSMutableDictionary *dic = [NSMutableDictionary dictionary];
[dic setValue:[CloudPushSDK getApnsDeviceToken] forKey:@"apnsDeviceToken"];
[self sendEventWithName:@"AliyunPush_onRegisterDeviceTokenSuccess" body:dic];
} else {
PushLogD(@"Register deviceToken failed, error: %@", res.error);
NSMutableDictionary *dic = [NSMutableDictionary dictionary];
[dic setValue:res.error forKey:@"error"];
[self sendEventWithName:@"AliyunPush_onRegisterDeviceTokenFailed" body:dic];
}
}];
PushLogD(@"####### ===> APNs register success");
}
- (void) handleDeviceTokenRegisterError: (NSNotification *)notification {
NSError* error = notification.userInfo[@"error"];
NSMutableDictionary *dic = [NSMutableDictionary dictionary];
[dic setValue:error.userInfo.description forKey:@"error"];
[self sendEventWithName:@"AliyunPush_onRegisterDeviceTokenFailed" body:dic];
}
- (void) handleReceiveRemoteNotification: (NSNotification *) notification {
NSDictionary *userInfo = notification.userInfo[@"notification"];
AliyunPushRemoteNotificationCallback completionHandler = notification.userInfo[@"completionHandler"];
//服务端中extras字段,key是自己定义的
PushLogD(@"onNotification userInfo =%@", userInfo);
[CloudPushSDK sendNotificationAck:userInfo];
[self sendEventWithName:@"AliyunPush_onNotification" body:userInfo];
if (completionHandler != nil) {
completionHandler(UIBackgroundFetchResultNewData);
}
}
- (void) handleForegroundReceiveNotification: (NSNotification *) notification {
UNNotification *unnotification = notification.userInfo[@"notification"];
AliyunPushForeReceiveNoticeCallback completionHandler = notification.userInfo[@"completionHandler"];
if(_showNoticeWhenForeground) {
// 通知弹出,且带有声音、内容和角标
if (completionHandler != nil) {
completionHandler(UNNotificationPresentationOptionSound | UNNotificationPresentationOptionAlert | UNNotificationPresentationOptionBadge);
}
} else {
// 处理iOS 10通知,并上报通知打开回执
[self handleiOS10Notification:unnotification];
// 通知不弹出
if (completionHandler != nil) {
completionHandler(UNNotificationPresentationOptionNone);
}
}
}
- (void) handleNotificationAction: (NSNotification *) notification {
UNNotificationResponse *response = notification.userInfo[@"response"];
NSString *userAction = response.actionIdentifier;
// 点击通知打开
if ([userAction isEqualToString:UNNotificationDefaultActionIdentifier]) {
[CloudPushSDK sendNotificationAck:response.notification.request.content.userInfo];
NSLog(@"###### AliyunPush_onNotificationOpened");
[self sendEventWithName:@"AliyunPush_onNotificationOpened" body:response.notification.request.content.userInfo];
}
// 通知dismiss,category创建时传入UNNotificationCategoryOptionCustomDismissAction才可以触发
if ([userAction isEqualToString:UNNotificationDismissActionIdentifier]) {
//通知删除回执上报
[CloudPushSDK sendDeleteNotificationAck:response.notification.request.content.userInfo];
[self sendEventWithName:@"AliyunPush_onNotificationRemoved" body:response.notification.request.content.userInfo];
}
AliyunPushNotificationActionCallback completionHandler = notification.userInfo[@"completionHandler"];
if (completionHandler != nil) {
completionHandler();
}
}
- (void)handleiOS10Notification:(UNNotification *)notification {
UNNotificationRequest *request = notification.request;
UNNotificationContent *content = request.content;
NSDictionary *userInfo = content.userInfo;
// 通知角标数清0
[UIApplication sharedApplication].applicationIconBadgeNumber = 0;
// 同步角标数到服务端
[CloudPushSDK syncBadgeNum:0 withCallback:^(CloudPushCallbackResult *res) {
if (res.success) {
PushLogD(@"Sync badge num: 0 success.");
} else {
PushLogD(@"Sync badge num: 0 failed, error: %@", res.error);
}
}];
// 通知打开回执上报
[CloudPushSDK sendNotificationAck:userInfo];
PushLogD(@"onNotification userInfo = %@", userInfo);
[self sendEventWithName:@"AliyunPush_onNotification" body:userInfo];
}
RCT_REMAP_METHOD(initPush,
initPushWithAppKey:(NSString*)appKey
initPushWithAppSecret:(NSString*)appSecret
withResolver:(RCTPromiseResolveBlock)resolve
withRejecter:(RCTPromiseRejectBlock)reject) {
if (!appKey || !appKey.length) {
resolve(@{KEY_CODE: CODE_PARAMS_ILLEGAL, @"errorMsg": @"appKey config error"});
return;
}
if (!appSecret|| !appSecret.length) {
resolve(@{KEY_CODE: CODE_PARAMS_ILLEGAL, @"errorMsg": @"appSecret config error"});
return;
}
[self registerAPNS];
//初始化
[CloudPushSDK asyncInit:appKey appSecret:appSecret callback:^(CloudPushCallbackResult *res) {
if (res.success) {
PushLogD(@"Push SDK init success, deviceId: %@.", [CloudPushSDK getDeviceId]);
resolve(@{KEY_CODE:CODE_SUCCESS});
} else {
PushLogD(@"Push SDK init failed, error: %@", res.error);
resolve(@{KEY_CODE:CODE_FAILED, @"errorMsg": [NSString stringWithFormat:@"errorCode: %ld", res.error.code]});
}
}];
[self listenerOnChannelOpened];
[self registerMessageReceive];
}
-(void)registerAPNS {
float systemVersionNum = [[[UIDevice currentDevice] systemVersion] floatValue];
if (systemVersionNum >= 10.0) {
// iOS 10 notifications
UNUserNotificationCenter *_notificationCenter = [UNUserNotificationCenter currentNotificationCenter];
// 请求推送权限
[_notificationCenter requestAuthorizationWithOptions:UNAuthorizationOptionAlert | UNAuthorizationOptionBadge | UNAuthorizationOptionSound completionHandler:^(BOOL granted, NSError * _Nullable error) {
if (granted) {
// granted
PushLogD(@"####### ===> User authored notification.");
// 向APNs注册,获取deviceToken
dispatch_async(dispatch_get_main_queue(), ^{
[RCTSharedApplication() registerForRemoteNotifications];
});
} else {
// not granted
PushLogD(@"####### ===> User denied notification.");
}
}];
} else if (systemVersionNum >= 8.0) {
#pragma clang diagnostic push
#pragma clang diagnostic ignored"-Wdeprecated-declarations"
[RCTSharedApplication() registerUserNotificationSettings:
[UIUserNotificationSettings settingsForTypes:
(UIUserNotificationTypeSound | UIUserNotificationTypeAlert | UIUserNotificationTypeBadge)
categories:nil]];
[RCTSharedApplication() registerForRemoteNotifications];
#pragma clang diagnostic pop
} else {
#pragma clang diagnostic push
#pragma clang diagnostic ignored"-Wdeprecated-declarations"
[RCTSharedApplication()registerForRemoteNotificationTypes:
(UIRemoteNotificationTypeAlert | UIRemoteNotificationTypeBadge | UIRemoteNotificationTypeSound)];
#pragma clang diagnostic pop
}
}
#pragma mark Channel Opened
/**
* 注册推送通道打开监听
*/
- (void)listenerOnChannelOpened {
[[NSNotificationCenter defaultCenter] addObserver:self
selector:@selector(onChannelOpened:)
name:@"CCPDidChannelConnectedSuccess"
object:nil];
}
/**
* 推送通道打开回调
*
*/
- (void)onChannelOpened:(NSNotification *)notification {
[self sendEventWithName:@"AliyunPush_onChannelOpened" body:nil];
}
#pragma mark Receive Message
/**
* @brief 注册推送消息到来监听
*/
- (void)registerMessageReceive {
[[NSNotificationCenter defaultCenter] addObserver:self
selector:@selector(onMessageReceived:)
name:@"CCPDidReceiveMessageNotification"
object:nil];
}
/**
* 处理到来推送消息
*
*/
- (void)onMessageReceived:(NSNotification *)notification {
CCPSysMessage *message = [notification object];
NSMutableDictionary *dic = [NSMutableDictionary dictionary];
NSString *title = [[NSString alloc] initWithData:message.title encoding:NSUTF8StringEncoding];
NSString *body = [[NSString alloc] initWithData:message.body encoding:NSUTF8StringEncoding];
[dic setValue:title forKey:@"title"];
[dic setValue:body forKey:@"body"];
PushLogD(@"Push SDK onMessageReceived: title: %@, body: %@", title, body);
[self sendEventWithName:@"AliyunPush_onMessage" body:dic];
}
RCT_REMAP_METHOD(closeCCPChannel,
closeCCPWithResolver:(RCTPromiseResolveBlock)resolve
closeCCPWithRejecter:(RCTPromiseRejectBlock)reject){
[CloudPushSDK closeCCPChannel];
resolve(@{KEY_CODE: CODE_SUCCESS});
}
RCT_REMAP_METHOD(getDeviceId,
getDeviceIdWithResolver:(RCTPromiseResolveBlock)resolve
getDeviceIdWithRejecter:(RCTPromiseRejectBlock)reject)
{
resolve([CloudPushSDK getDeviceId]);
}
RCT_REMAP_METHOD(turnOnDebug,
turnOnDebugWithResolver:(RCTPromiseResolveBlock)resolve
turnOnDebugWithRejecter:(RCTPromiseRejectBlock)reject) {
[CloudPushSDK turnOnDebug];
resolve(@{KEY_CODE: CODE_SUCCESS});
}
RCT_REMAP_METHOD(bindAccount,
bindAccountWithAccount:(NSString *)account
bindAccountWithResolver:(RCTPromiseResolveBlock)resolve
bindAccountWithRejecter:(RCTPromiseRejectBlock)reject) {
if (account) {
[CloudPushSDK bindAccount:account withCallback:^(CloudPushCallbackResult *res) {
if (res.success) {
resolve(@{KEY_CODE:CODE_SUCCESS});
} else {
resolve(@{KEY_CODE:CODE_FAILED, KEY_ERROR_MSG: [NSString stringWithFormat:@"errorCode: %ld", res.error.code]});
}
}];
} else {
resolve(@{KEY_CODE: CODE_PARAMS_ILLEGAL, KEY_ERROR_MSG: @"account can not be empty"});
}
}
RCT_REMAP_METHOD(unbindAccount,
unbindAccountWithResolver:(RCTPromiseResolveBlock)resolve
unbindAccountWithRejecter:(RCTPromiseRejectBlock)reject) {
[CloudPushSDK unbindAccount:^(CloudPushCallbackResult *res) {
if (res.success) {
resolve(@{KEY_CODE:CODE_SUCCESS});
} else {
resolve(@{KEY_CODE:CODE_FAILED, KEY_ERROR_MSG: [NSString stringWithFormat:@"errorCode: %ld", res.error.code]});
}
}];
}
RCT_REMAP_METHOD(addAlias, addAliasWithAlias:(NSString *)alias
addAliasWithResolver:(RCTPromiseResolveBlock)resolve
addAliasWithRejecter:(RCTPromiseRejectBlock)reject) {
if (alias) {
[CloudPushSDK addAlias:alias withCallback:^(CloudPushCallbackResult *res) {
if (res.success) {
resolve(@{KEY_CODE:CODE_SUCCESS});
} else {
resolve(@{KEY_CODE:CODE_FAILED, KEY_ERROR_MSG: [NSString stringWithFormat:@"errorCode: %ld", res.error.code]});
}
}];
} else {
resolve(@{KEY_CODE: CODE_PARAMS_ILLEGAL, KEY_ERROR_MSG: @"alias can not be empty"});
}
}
RCT_REMAP_METHOD(removeAlias, removeAliasWithAlias:(NSString *)alias
removeAliasWithResolver:(RCTPromiseResolveBlock)resolve
removeAliasWithRejecter:(RCTPromiseRejectBlock)reject) {
if (alias) {
[CloudPushSDK removeAlias:alias withCallback:^(CloudPushCallbackResult *res) {
if (res.success) {
resolve(@{KEY_CODE:CODE_SUCCESS});
} else {
resolve(@{KEY_CODE:CODE_FAILED, KEY_ERROR_MSG: [NSString stringWithFormat:@"errorCode: %ld", res.error.code]});
}
}];
} else {
resolve(@{KEY_CODE: CODE_PARAMS_ILLEGAL, KEY_ERROR_MSG: @"alias can not be empty"});
}
}
RCT_REMAP_METHOD(listAlias,
listAliasWithResolver:(RCTPromiseResolveBlock)resolve
listAliasWithRejecter:(RCTPromiseRejectBlock)reject) {
[CloudPushSDK listAliases:^(CloudPushCallbackResult *res) {
if (res.success) {
resolve(@{KEY_CODE:CODE_SUCCESS, @"aliasList": res.data});
} else {
resolve(@{KEY_CODE:CODE_FAILED, KEY_ERROR_MSG: [NSString stringWithFormat:@"errorCode: %ld", res.error.code]});
}
}];
}
RCT_REMAP_METHOD(bindTag,
bindTagWithTags:(NSArray *)tags
bindTagWithTarget:(int)target
bindTagWithAlias:(NSString *)alias
bindTagWithResolver:(RCTPromiseResolveBlock)resolve
bindTagWithRejecter:(RCTPromiseRejectBlock)reject
) {
if (tags && tags.count != 0) {
if (target != 1 && target != 2 && target != 3) {
target = 1;
}
[CloudPushSDK bindTag:target withTags:tags withAlias:alias withCallback:^(CloudPushCallbackResult *res){
if (res.success) {
resolve(@{KEY_CODE:CODE_SUCCESS});
} else {
resolve(@{KEY_CODE:CODE_FAILED, KEY_ERROR_MSG: [NSString stringWithFormat:@"errorCode: %ld", res.error.code]});
}
}];
} else {
resolve(@{KEY_CODE: CODE_PARAMS_ILLEGAL, KEY_ERROR_MSG: @"tags can not be empty"});
}
}
RCT_REMAP_METHOD(unbindTag,
unbindTagWithTags:(NSArray *)tags
unbindTagWithTarget:(int)target
unbindTagWithAlias:(NSString *)alias
unbindTagWithResolver:(RCTPromiseResolveBlock)resolve
unbindTagWithRejecter:(RCTPromiseRejectBlock)reject
) {
if (tags && tags.count != 0) {
if (target != 1 && target != 2 && target != 3) {
target = 1;
}
[CloudPushSDK unbindTag:target withTags:tags withAlias:alias withCallback:^(CloudPushCallbackResult *res){
if (res.success) {
resolve(@{KEY_CODE:CODE_SUCCESS});
} else {
resolve(@{KEY_CODE:CODE_FAILED, KEY_ERROR_MSG: [NSString stringWithFormat:@"errorCode: %ld", res.error.code]});
}
}];
} else {
resolve(@{KEY_CODE: CODE_PARAMS_ILLEGAL, KEY_ERROR_MSG: @"tags can not be empty"});
}
}
RCT_REMAP_METHOD(listTags,
listTagsWithTarget:(int)target
listTagsWithResolver:(RCTPromiseResolveBlock)resolve
listTagsWithRejecter:(RCTPromiseRejectBlock)reject
) {
if (target != 1 && target != 2 && target != 3) {
target = 1;
}
[CloudPushSDK listTags:target withCallback:^(CloudPushCallbackResult *res){
if (res.success) {
resolve(@{KEY_CODE:CODE_SUCCESS, @"tagsList": res.data});
} else {
resolve(@{KEY_CODE:CODE_FAILED, KEY_ERROR_MSG: [NSString stringWithFormat:@"errorCode: %ld", res.error.code]});
}
}];
}
RCT_REMAP_METHOD(setBadgeNum,
setBadgeNumWithNum:(int)num
setBadgeNumWithResolver:(RCTPromiseResolveBlock)resolve
setBadgeNumWithRejecter:(RCTPromiseRejectBlock)reject) {
if (@available(iOS 16.0, *)) {
UNUserNotificationCenter *_notificationCenter = [UNUserNotificationCenter currentNotificationCenter];
[_notificationCenter setBadgeCount:num withCompletionHandler:^(NSError * _Nullable error) {
if (error == nil) {
resolve(@{KEY_CODE: CODE_SUCCESS});
} else {
resolve(@{KEY_CODE:CODE_FAILED, KEY_ERROR_MSG: error.localizedDescription});
}
}];
} else {
RCTExecuteOnMainQueue(^{
RCTSharedApplication().applicationIconBadgeNumber = num;
resolve(@{KEY_CODE: CODE_SUCCESS});
});
}
}
RCT_REMAP_METHOD(syncBadgeNum,
syncBadgeNumWithNum:(int)num
syncBadgeNumWithResolver:(RCTPromiseResolveBlock)resolve
syncBadgeNumWithRejecter:(RCTPromiseRejectBlock)reject) {
[CloudPushSDK syncBadgeNum:num withCallback:^(CloudPushCallbackResult *res) {
if (res.success) {
PushLogD(@"Sync badge num: [%lu] success.", (unsigned long)num);
resolve(@{KEY_CODE: CODE_SUCCESS});
} else {
PushLogD(@"Sync badge num: [%lu] failed, error: %@", (unsigned long)num, res.error);
resolve(@{KEY_CODE: CODE_FAILED, KEY_ERROR_MSG: [NSString stringWithFormat:@"errorCode: %ld", res.error.code]});
}
}];
}
RCT_REMAP_METHOD(showNoticeWhenForeground, showNoticeWhenForegroundWithEnabled: (BOOL)enable
showNoticeWhenForegroundWithResolver:(RCTPromiseResolveBlock)resolve
showNoticeWhenForegroundWithRejecter:(RCTPromiseRejectBlock)reject) {
_showNoticeWhenForeground = enable;
resolve(@{KEY_CODE:CODE_SUCCESS});
}
RCT_REMAP_METHOD(getApnsDeviceToken,
getApnsDeviceTokenWithResolver:(RCTPromiseResolveBlock)resolve
getApnsDeviceTokenWithRejecter:(RCTPromiseRejectBlock)reject) {
resolve([CloudPushSDK getApnsDeviceToken]);
}
RCT_REMAP_METHOD(setPluginLogEnabled, setPluginLogEnabledWithEnabled:(BOOL)enabled) {
if (enabled) {
[AliyunPushLog enableLog];
}else {
[AliyunPushLog disableLog];
}
}
RCT_REMAP_METHOD(isChannelOpened,
isChannelOpenedWithResolver:(RCTPromiseResolveBlock)resolve
isChannelOpenedWithRejecter:(RCTPromiseRejectBlock)reject) {
resolve(@([CloudPushSDK isChannelOpened]));
}
// Don't compile this code when we build for the old architecture.
#ifdef RCT_NEW_ARCH_ENABLED
- (std::shared_ptr<facebook::react::TurboModule>)getTurboModule:
(const facebook::react::ObjCTurboModule::InitParams &)params
{
return std::make_shared<facebook::react::NativeAliyunReactNativePushSpecJSI>(params);
}
#endif
@end