add apns supports.
This commit is contained in:
1 parent
855d24bc64
commit
21c392db8f
6 files changed
+442
-31
No files matched your search
+266
-5
@@ -8,12 +8,18 @@
|
||||
#import <UIKit/UIKit.h>
|
||||
|
||||
static NSString * const BluememeDeviceKeyDefaultsKey = @"bluememe_utils_device_key";
|
||||
static NSString * const BluememeUtilsPluginVersion = @"0.1.11";
|
||||
static NSString * const BluememeUtilsPluginVersion = @"0.1.12";
|
||||
static NSString * const BluememeCordovaRemoteNotificationName = @"CDVRemoteNotification";
|
||||
static NSString * const BluememeCordovaRemoteNotificationErrorName = @"CDVRemoteNotificationError";
|
||||
static NSTimeInterval const BluememeAPNSTokenTimeoutSeconds = 10.0;
|
||||
|
||||
@interface CDVBluememeUtils ()
|
||||
@property (nonatomic, copy) NSString *notificationCallbackId;
|
||||
@property (nonatomic, strong) NSDictionary *initialNotification;
|
||||
@property (nonatomic, weak) id<UNUserNotificationCenterDelegate> previousNotificationDelegate;
|
||||
@property (nonatomic, strong) NSData *apnsDeviceToken;
|
||||
@property (nonatomic, strong) NSError *apnsRegistrationError;
|
||||
@property (nonatomic, strong) CDVInvokedUrlCommand *pendingFirebaseTokenCommand;
|
||||
@end
|
||||
|
||||
@implementation CDVBluememeUtils
|
||||
@@ -29,6 +35,20 @@ static NSString * const BluememeUtilsPluginVersion = @"0.1.11";
|
||||
self.previousNotificationDelegate = center.delegate;
|
||||
center.delegate = self;
|
||||
}
|
||||
|
||||
[[NSNotificationCenter defaultCenter] addObserver:self
|
||||
selector:@selector(remoteNotificationRegistered:)
|
||||
name:BluememeCordovaRemoteNotificationName
|
||||
object:nil];
|
||||
[[NSNotificationCenter defaultCenter] addObserver:self
|
||||
selector:@selector(remoteNotificationRegistrationFailed:)
|
||||
name:BluememeCordovaRemoteNotificationErrorName
|
||||
object:nil];
|
||||
}
|
||||
|
||||
- (void)dealloc
|
||||
{
|
||||
[[NSNotificationCenter defaultCenter] removeObserver:self];
|
||||
}
|
||||
|
||||
- (void)saveWifi:(CDVInvokedUrlCommand *)command
|
||||
@@ -159,14 +179,31 @@ static NSString * const BluememeUtilsPluginVersion = @"0.1.11";
|
||||
return;
|
||||
}
|
||||
[[UIApplication sharedApplication] registerForRemoteNotifications];
|
||||
[self sendSuccessForCommand:command dictionary:@{@"type": @"notification", @"granted": @(granted), @"missing": granted ? @[] : @[@"notification"]}];
|
||||
[[UNUserNotificationCenter currentNotificationCenter] getNotificationSettingsWithCompletionHandler:^(UNNotificationSettings *settings) {
|
||||
dispatch_async(dispatch_get_main_queue(), ^{
|
||||
NSMutableDictionary *result = [[self dictionaryFromNotificationSettings:settings] mutableCopy];
|
||||
result[@"granted"] = @(granted);
|
||||
result[@"registeredForRemoteNotifications"] = @([[UIApplication sharedApplication] isRegisteredForRemoteNotifications]);
|
||||
result[@"apnsTokenAvailable"] = @(self.apnsDeviceToken.length > 0);
|
||||
[self sendSuccessForCommand:command dictionary:result];
|
||||
});
|
||||
}];
|
||||
});
|
||||
}];
|
||||
} else {
|
||||
UIUserNotificationSettings *settings = [UIUserNotificationSettings settingsForTypes:(UIUserNotificationTypeAlert | UIUserNotificationTypeBadge | UIUserNotificationTypeSound) categories:nil];
|
||||
[[UIApplication sharedApplication] registerUserNotificationSettings:settings];
|
||||
[[UIApplication sharedApplication] registerForRemoteNotifications];
|
||||
[self sendSuccessForCommand:command dictionary:@{@"type": @"notification", @"granted": @YES, @"missing": @[]}];
|
||||
[self sendSuccessForCommand:command dictionary:@{
|
||||
@"type": @"notification",
|
||||
@"platform": @"iOS",
|
||||
@"enabled": @YES,
|
||||
@"granted": @YES,
|
||||
@"authorizationStatus": @"authorized",
|
||||
@"registeredForRemoteNotifications": @([[UIApplication sharedApplication] isRegisteredForRemoteNotifications]),
|
||||
@"apnsTokenAvailable": @(self.apnsDeviceToken.length > 0),
|
||||
@"missing": @[]
|
||||
}];
|
||||
}
|
||||
}
|
||||
|
||||
@@ -190,6 +227,8 @@ static NSString * const BluememeUtilsPluginVersion = @"0.1.11";
|
||||
@"alertEnabled": @((settings.types & UIUserNotificationTypeAlert) != 0),
|
||||
@"soundEnabled": @((settings.types & UIUserNotificationTypeSound) != 0),
|
||||
@"badgeEnabled": @((settings.types & UIUserNotificationTypeBadge) != 0),
|
||||
@"registeredForRemoteNotifications": @([[UIApplication sharedApplication] isRegisteredForRemoteNotifications]),
|
||||
@"apnsTokenAvailable": @(self.apnsDeviceToken.length > 0),
|
||||
@"missing": enabled ? @[] : @[@"notification"]
|
||||
}];
|
||||
}
|
||||
@@ -204,13 +243,102 @@ static NSString * const BluememeUtilsPluginVersion = @"0.1.11";
|
||||
}
|
||||
[FIRMessaging messaging].delegate = self;
|
||||
|
||||
if (@available(iOS 10.0, *)) {
|
||||
[[UNUserNotificationCenter currentNotificationCenter] getNotificationSettingsWithCompletionHandler:^(UNNotificationSettings *settings) {
|
||||
dispatch_async(dispatch_get_main_queue(), ^{
|
||||
if (![self notificationAuthorizationAllowsFirebaseToken:settings.authorizationStatus]) {
|
||||
NSMutableDictionary *details = [[self dictionaryFromNotificationSettings:settings] mutableCopy];
|
||||
details[@"apnsTokenAvailable"] = @(self.apnsDeviceToken.length > 0);
|
||||
details[@"registeredForRemoteNotifications"] = @([[UIApplication sharedApplication] isRegisteredForRemoteNotifications]);
|
||||
[self sendErrorForCommand:command
|
||||
code:@"PERMISSION_REQUIRED"
|
||||
message:@"Call requestPushPermission and wait for notification authorization before getFirebaseToken."
|
||||
details:details];
|
||||
return;
|
||||
}
|
||||
[self registerForRemoteNotificationsAndFetchFirebaseTokenForCommand:command];
|
||||
});
|
||||
}];
|
||||
return;
|
||||
}
|
||||
|
||||
[self registerForRemoteNotificationsAndFetchFirebaseTokenForCommand:command];
|
||||
}
|
||||
|
||||
- (void)registerForRemoteNotificationsAndFetchFirebaseTokenForCommand:(CDVInvokedUrlCommand *)command
|
||||
{
|
||||
if (self.apnsDeviceToken.length > 0) {
|
||||
[FIRMessaging messaging].APNSToken = self.apnsDeviceToken;
|
||||
[self fetchFirebaseTokenForCommand:command];
|
||||
return;
|
||||
}
|
||||
|
||||
if (self.pendingFirebaseTokenCommand != nil) {
|
||||
[self sendErrorForCommand:self.pendingFirebaseTokenCommand
|
||||
code:@"FCM_TOKEN_REQUEST_REPLACED"
|
||||
message:@"A newer getFirebaseToken request replaced this pending request."
|
||||
details:@{
|
||||
@"apnsTokenAvailable": @NO,
|
||||
@"registeredForRemoteNotifications": @([[UIApplication sharedApplication] isRegisteredForRemoteNotifications])
|
||||
}];
|
||||
}
|
||||
|
||||
self.pendingFirebaseTokenCommand = command;
|
||||
self.apnsRegistrationError = nil;
|
||||
[[UIApplication sharedApplication] registerForRemoteNotifications];
|
||||
|
||||
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(BluememeAPNSTokenTimeoutSeconds * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
|
||||
if (self.pendingFirebaseTokenCommand != command) {
|
||||
return;
|
||||
}
|
||||
|
||||
self.pendingFirebaseTokenCommand = nil;
|
||||
if (self.apnsRegistrationError != nil) {
|
||||
[self sendErrorForCommand:command
|
||||
code:@"APNS_TOKEN_FAILED"
|
||||
message:(self.apnsRegistrationError.localizedDescription ? self.apnsRegistrationError.localizedDescription : @"APNs registration failed.")
|
||||
details:[self detailsForNativeError:self.apnsRegistrationError includeAPNSState:YES]];
|
||||
return;
|
||||
}
|
||||
|
||||
[self sendErrorForCommand:command
|
||||
code:@"APNS_TOKEN_NOT_AVAILABLE"
|
||||
message:@"APNs device token was not received before fetching the FCM token. Check Push Notifications capability, aps-environment entitlement, provisioning profile, and Firebase APNs key configuration."
|
||||
details:@{
|
||||
@"apnsTokenAvailable": @NO,
|
||||
@"registeredForRemoteNotifications": @([[UIApplication sharedApplication] isRegisteredForRemoteNotifications]),
|
||||
@"timeoutSeconds": @(BluememeAPNSTokenTimeoutSeconds)
|
||||
}];
|
||||
});
|
||||
}
|
||||
|
||||
- (void)fetchFirebaseTokenForCommand:(CDVInvokedUrlCommand *)command
|
||||
{
|
||||
if (self.apnsDeviceToken.length > 0) {
|
||||
[FIRMessaging messaging].APNSToken = self.apnsDeviceToken;
|
||||
}
|
||||
|
||||
[[FIRMessaging messaging] tokenWithCompletion:^(NSString *token, NSError *error) {
|
||||
dispatch_async(dispatch_get_main_queue(), ^{
|
||||
if (error != nil || token.length == 0) {
|
||||
[self sendErrorForCommand:command code:@"FCM_TOKEN_FAILED" message:(error.localizedDescription ? error.localizedDescription : @"Failed to get Firebase token.")];
|
||||
NSMutableDictionary *details = [NSMutableDictionary dictionary];
|
||||
details[@"apnsTokenAvailable"] = @(self.apnsDeviceToken.length > 0);
|
||||
details[@"registeredForRemoteNotifications"] = @([[UIApplication sharedApplication] isRegisteredForRemoteNotifications]);
|
||||
if (error != nil) {
|
||||
[details addEntriesFromDictionary:[self detailsForNativeError:error includeAPNSState:NO]];
|
||||
}
|
||||
[self sendErrorForCommand:command
|
||||
code:@"FCM_TOKEN_FAILED"
|
||||
message:(error.localizedDescription ? error.localizedDescription : @"Failed to get Firebase token.")
|
||||
details:details];
|
||||
return;
|
||||
}
|
||||
[self sendSuccessForCommand:command dictionary:@{@"token": token}];
|
||||
[self sendSuccessForCommand:command dictionary:@{
|
||||
@"token": token,
|
||||
@"platform": @"iOS",
|
||||
@"apnsTokenAvailable": @(self.apnsDeviceToken.length > 0),
|
||||
@"registeredForRemoteNotifications": @([[UIApplication sharedApplication] isRegisteredForRemoteNotifications])
|
||||
}];
|
||||
[self sendEvent:@{@"type": @"token", @"token": token}];
|
||||
});
|
||||
}];
|
||||
@@ -716,6 +844,8 @@ static NSString * const BluememeUtilsPluginVersion = @"0.1.11";
|
||||
@"alertEnabled": @(alertEnabled),
|
||||
@"soundEnabled": @(soundEnabled),
|
||||
@"badgeEnabled": @(badgeEnabled),
|
||||
@"registeredForRemoteNotifications": @([[UIApplication sharedApplication] isRegisteredForRemoteNotifications]),
|
||||
@"apnsTokenAvailable": @(self.apnsDeviceToken.length > 0),
|
||||
@"missing": missing
|
||||
};
|
||||
}
|
||||
@@ -746,6 +876,137 @@ static NSString * const BluememeUtilsPluginVersion = @"0.1.11";
|
||||
return @"unknown";
|
||||
}
|
||||
|
||||
- (BOOL)notificationAuthorizationAllowsFirebaseToken:(UNAuthorizationStatus)status API_AVAILABLE(ios(10.0))
|
||||
{
|
||||
if (status == UNAuthorizationStatusAuthorized) {
|
||||
return YES;
|
||||
}
|
||||
if (@available(iOS 12.0, *)) {
|
||||
if (status == UNAuthorizationStatusProvisional) {
|
||||
return YES;
|
||||
}
|
||||
}
|
||||
if (@available(iOS 14.0, *)) {
|
||||
if (status == UNAuthorizationStatusEphemeral) {
|
||||
return YES;
|
||||
}
|
||||
}
|
||||
return NO;
|
||||
}
|
||||
|
||||
- (void)remoteNotificationRegistered:(NSNotification *)notification
|
||||
{
|
||||
NSData *tokenData = [self dataFromAPNSTokenObject:notification.object];
|
||||
if (tokenData.length == 0) {
|
||||
tokenData = [self dataFromAPNSTokenObject:notification.userInfo[@"deviceToken"]];
|
||||
}
|
||||
if (tokenData.length == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
self.apnsDeviceToken = tokenData;
|
||||
self.apnsRegistrationError = nil;
|
||||
if ([FIRApp defaultApp] != nil) {
|
||||
[FIRMessaging messaging].APNSToken = tokenData;
|
||||
}
|
||||
|
||||
if (self.pendingFirebaseTokenCommand != nil) {
|
||||
CDVInvokedUrlCommand *command = self.pendingFirebaseTokenCommand;
|
||||
self.pendingFirebaseTokenCommand = nil;
|
||||
[self fetchFirebaseTokenForCommand:command];
|
||||
}
|
||||
}
|
||||
|
||||
- (void)remoteNotificationRegistrationFailed:(NSNotification *)notification
|
||||
{
|
||||
NSError *error = nil;
|
||||
if ([notification.object isKindOfClass:[NSError class]]) {
|
||||
error = notification.object;
|
||||
} else if ([notification.userInfo[@"error"] isKindOfClass:[NSError class]]) {
|
||||
error = notification.userInfo[@"error"];
|
||||
}
|
||||
|
||||
self.apnsRegistrationError = error;
|
||||
|
||||
if (self.pendingFirebaseTokenCommand != nil) {
|
||||
CDVInvokedUrlCommand *command = self.pendingFirebaseTokenCommand;
|
||||
self.pendingFirebaseTokenCommand = nil;
|
||||
[self sendErrorForCommand:command
|
||||
code:@"APNS_TOKEN_FAILED"
|
||||
message:(error.localizedDescription ? error.localizedDescription : @"APNs registration failed.")
|
||||
details:[self detailsForNativeError:error includeAPNSState:YES]];
|
||||
}
|
||||
}
|
||||
|
||||
- (NSData *)dataFromAPNSTokenObject:(id)object
|
||||
{
|
||||
if ([object isKindOfClass:[NSData class]]) {
|
||||
return object;
|
||||
}
|
||||
if (![object isKindOfClass:[NSString class]]) {
|
||||
return nil;
|
||||
}
|
||||
|
||||
NSString *tokenString = (NSString *)object;
|
||||
NSRange angleStart = [tokenString rangeOfString:@"<"];
|
||||
NSRange angleEnd = [tokenString rangeOfString:@">" options:NSBackwardsSearch];
|
||||
if (angleStart.location != NSNotFound && angleEnd.location != NSNotFound && angleEnd.location > angleStart.location) {
|
||||
tokenString = [tokenString substringWithRange:NSMakeRange(angleStart.location + 1, angleEnd.location - angleStart.location - 1)];
|
||||
} else {
|
||||
NSRange bytesRange = [tokenString rangeOfString:@"bytes = 0x"];
|
||||
if (bytesRange.location != NSNotFound) {
|
||||
tokenString = [tokenString substringFromIndex:bytesRange.location + bytesRange.length];
|
||||
}
|
||||
}
|
||||
|
||||
NSMutableString *hex = [NSMutableString string];
|
||||
NSCharacterSet *hexCharacters = [NSCharacterSet characterSetWithCharactersInString:@"0123456789abcdefABCDEF"];
|
||||
for (NSUInteger index = 0; index < tokenString.length; index++) {
|
||||
unichar character = [tokenString characterAtIndex:index];
|
||||
if ([hexCharacters characterIsMember:character]) {
|
||||
[hex appendFormat:@"%C", character];
|
||||
} else if ([tokenString rangeOfString:@"bytes = 0x"].location != NSNotFound) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (hex.length == 0 || (hex.length % 2) != 0) {
|
||||
return nil;
|
||||
}
|
||||
|
||||
NSMutableData *data = [NSMutableData dataWithCapacity:(hex.length / 2)];
|
||||
for (NSUInteger index = 0; index < hex.length; index += 2) {
|
||||
NSString *byteString = [hex substringWithRange:NSMakeRange(index, 2)];
|
||||
unsigned int byte = 0;
|
||||
NSScanner *scanner = [NSScanner scannerWithString:byteString];
|
||||
if (![scanner scanHexInt:&byte]) {
|
||||
return nil;
|
||||
}
|
||||
unsigned char value = (unsigned char)byte;
|
||||
[data appendBytes:&value length:1];
|
||||
}
|
||||
return data;
|
||||
}
|
||||
|
||||
- (NSDictionary *)detailsForNativeError:(NSError *)error includeAPNSState:(BOOL)includeAPNSState
|
||||
{
|
||||
NSMutableDictionary *details = [NSMutableDictionary dictionary];
|
||||
if (includeAPNSState) {
|
||||
details[@"apnsTokenAvailable"] = @(self.apnsDeviceToken.length > 0);
|
||||
details[@"registeredForRemoteNotifications"] = @([[UIApplication sharedApplication] isRegisteredForRemoteNotifications]);
|
||||
}
|
||||
if (error.domain.length > 0) {
|
||||
details[@"nativeDomain"] = error.domain;
|
||||
}
|
||||
if (error != nil) {
|
||||
details[@"nativeCode"] = @(error.code);
|
||||
}
|
||||
if (error.localizedDescription.length > 0) {
|
||||
details[@"nativeError"] = error.localizedDescription;
|
||||
}
|
||||
return details;
|
||||
}
|
||||
|
||||
- (BOOL)ensureFirebaseConfigured:(NSError **)error
|
||||
{
|
||||
if ([FIRApp defaultApp] != nil) {
|
||||
|
||||
Reference in new issue
Block a user