add apns supports.

This commit is contained in:
aria committed 2026-04-23 15:38:20 +09:00
1 parent 855d24bc64
commit 21c392db8f
6 files changed
+442 -31

No files matched your search

+266 -5
View File
@@ -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) {