Compare commits

...
2 Commits
Author SHA1 Message Date
aria 0e90516856 Merge remote-tracking branch 'origin/main'
# Conflicts:
#	README.md
2026-05-25 11:27:59 +09:00
aria 8fcb440d8a •插件初始化时会给宿主 AppDelegate 安装 APNs 注册回调桥接。
•捕获:
◦application:didRegisterForRemoteNotificationsWithDeviceToken:
◦application:didFailToRegisterForRemoteNotificationsWithError:
•即使宿主没有转发 Cordova 的 CDVRemoteNotification,插件也能自己收到 APNs token。
•getFirebaseToken 等待 APNs token 时,会同时轮询 Firebase Messaging 已缓存的 APNSToken。
•返回结果增加诊断字段:
◦apnsTokenSource: plugin / firebase / none
◦cachedAPNSTokenAvailable
◦firebaseAPNSTokenAvailable
2026-05-25 11:26:31 +09:00
4 changed files with 228 additions and 61 deletions

No files matched your search

+16 -8
View File
@@ -113,13 +113,9 @@ WiFi options:
```js ```js
BluememeUtils.requestPushPermission(success, error); BluememeUtils.requestPushPermission(success, error);
BluememeUtils.getPushPermissionStatus(success, error); BluememeUtils.getPushPermissionStatus(success, error);
BluememeUtils.ensurePushPermission(success, error);
BluememeUtils.getFirebaseToken(function (result) { BluememeUtils.getFirebaseToken(function (result) {
console.log(result.token); console.log(result.token);
}, error); }, error);
BluememeUtils.getFirebaseTokenWithPermission(function (result) {
console.log(result.token);
}, error);
BluememeUtils.subscribeToTopic("news", success, error); BluememeUtils.subscribeToTopic("news", success, error);
BluememeUtils.unsubscribeFromTopic("news", success, error); BluememeUtils.unsubscribeFromTopic("news", success, error);
@@ -140,19 +136,31 @@ BluememeUtils.getInitialNotification(success, error);
BluememeUtils.stopNotificationListener(success, error); BluememeUtils.stopNotificationListener(success, error);
``` ```
`getPushPermissionStatus` 只读取当前系统通知状态,不弹授权框。可用于填充后端 `NotificationsEnabled`。`ensurePushPermission` 会先读取权限状态;如果未授权,再调用系统权限请求;授权成功后才继续。`getFirebaseTokenWithPermission` 会按同一套流程先确认通知权限,再调用 `getFirebaseToken`。 `getPushPermissionStatus` 只读取当前系统通知状态,不弹授权框。可用于填充后端 `NotificationsEnabled`。iOS 返回 `authorizationStatus: "notDetermined"` 时,说明还没有触发系统通知授权弹窗,需要先调用 `requestPushPermission`,用户授权后再调用 `getFirebaseToken`。
Android 和 iOS 获取 FCM token 都建议使用同一套顺序: iOS 获取 FCM token 的推荐顺序:
```js ```js
BluememeUtils.ensurePushPermission(function () { BluememeUtils.requestPushPermission(function (permission) {
if (permission.enabled !== true) {
return;
}
BluememeUtils.getFirebaseToken(function (result) { BluememeUtils.getFirebaseToken(function (result) {
console.log(result.token); console.log(result.token);
}, error); }, error);
}, error); }, error);
``` ```
完整示例流程见 `example/unified-push-flow.js`。iOS 上 FCM token 依赖 APNs device token。插件会在 `getFirebaseToken` 内等待 Cordova 收到 APNs token 后再向 Firebase 请求 FCM token;如果 APNs token 超时或注册失败,会返回 `APNS_TOKEN_NOT_AVAILABLE` 或 `APNS_TOKEN_FAILED`。 iOS 上 FCM token 依赖 APNs device token。必须等待 `requestPushPermission` 的 success callback 返回后再调用 `getFirebaseToken`,不要把 `requestPushPermission()` 当成同步调用。
插件会在 `getFirebaseToken` 内等待 APNs token 后再向 Firebase 请求 FCM token。iOS 侧会同时使用三种来源降低宿主 AppDelegate 未转发回调造成的问题:
- 直接桥接 `application:didRegisterForRemoteNotificationsWithDeviceToken:`
- 监听 Cordova 的 `CDVRemoteNotification`
- 轮询 Firebase Messaging 已缓存的 `APNSToken`
如果 APNs token 超时或注册失败,会返回 `APNS_TOKEN_NOT_AVAILABLE` 或 `APNS_TOKEN_FAILED`。返回结果会包含 `apnsTokenSource`、`cachedAPNSTokenAvailable`、`firebaseAPNSTokenAvailable` 等诊断字段。
返回示例: 返回示例:
+1 -1
View File
@@ -1,6 +1,6 @@
{ {
"name": "bluememe-utils", "name": "bluememe-utils",
"version": "0.1.12", "version": "0.1.13",
"description": "Cordova utilities for WiFi, Firebase Cloud Messaging, and biometric unlock for OutSystems 11 apps.", "description": "Cordova utilities for WiFi, Firebase Cloud Messaging, and biometric unlock for OutSystems 11 apps.",
"license": "MIT", "license": "MIT",
"files": [ "files": [
+1 -1
View File
@@ -2,7 +2,7 @@
<plugin xmlns="http://apache.org/cordova/ns/plugins/1.0" <plugin xmlns="http://apache.org/cordova/ns/plugins/1.0"
xmlns:android="http://schemas.android.com/apk/res/android" xmlns:android="http://schemas.android.com/apk/res/android"
id="bluememe-utils" id="bluememe-utils"
version="0.1.12"> version="0.1.13">
<name>bluememe-utils</name> <name>bluememe-utils</name>
<description>Cordova utilities for WiFi, Firebase Cloud Messaging, and biometric unlock.</description> <description>Cordova utilities for WiFi, Firebase Cloud Messaging, and biometric unlock.</description>
<license>MIT</license> <license>MIT</license>
+210 -51
View File
@@ -6,12 +6,61 @@
#import <SystemConfiguration/CaptiveNetwork.h> #import <SystemConfiguration/CaptiveNetwork.h>
#import <TargetConditionals.h> #import <TargetConditionals.h>
#import <UIKit/UIKit.h> #import <UIKit/UIKit.h>
#import <objc/runtime.h>
static NSString * const BluememeDeviceKeyDefaultsKey = @"bluememe_utils_device_key"; static NSString * const BluememeDeviceKeyDefaultsKey = @"bluememe_utils_device_key";
static NSString * const BluememeUtilsPluginVersion = @"0.1.12"; static NSString * const BluememeUtilsPluginVersion = @"0.1.13";
static NSString * const BluememeCordovaRemoteNotificationName = @"CDVRemoteNotification"; static NSString * const BluememeCordovaRemoteNotificationName = @"CDVRemoteNotification";
static NSString * const BluememeCordovaRemoteNotificationErrorName = @"CDVRemoteNotificationError"; static NSString * const BluememeCordovaRemoteNotificationErrorName = @"CDVRemoteNotificationError";
static NSTimeInterval const BluememeAPNSTokenTimeoutSeconds = 10.0; static NSTimeInterval const BluememeAPNSTokenTimeoutSeconds = 10.0;
static NSTimeInterval const BluememeAPNSTokenPollIntervalSeconds = 0.25;
static char BluememeRemoteNotificationBridgeInstalledKey;
static SEL BluememeRemoteNotificationBridgeSelectorForSelector(SEL selector)
{
NSString *selectorName = NSStringFromSelector(selector);
return NSSelectorFromString([@"bluememe_" stringByAppendingString:selectorName]);
}
static void BluememePostRemoteNotificationToken(NSData *deviceToken)
{
if (deviceToken.length == 0) {
return;
}
[[NSNotificationCenter defaultCenter] postNotificationName:BluememeCordovaRemoteNotificationName
object:deviceToken
userInfo:@{@"deviceToken": deviceToken}];
}
static void BluememePostRemoteNotificationError(NSError *error)
{
NSDictionary *userInfo = error == nil ? @{} : @{@"error": error};
[[NSNotificationCenter defaultCenter] postNotificationName:BluememeCordovaRemoteNotificationErrorName
object:error
userInfo:userInfo];
}
static void BluememeApplicationDidRegisterForRemoteNotifications(id self, SEL _cmd, UIApplication *application, NSData *deviceToken)
{
BluememePostRemoteNotificationToken(deviceToken);
SEL originalSelector = BluememeRemoteNotificationBridgeSelectorForSelector(_cmd);
if ([self respondsToSelector:originalSelector]) {
IMP originalImplementation = [self methodForSelector:originalSelector];
((void (*)(id, SEL, UIApplication *, NSData *))originalImplementation)(self, originalSelector, application, deviceToken);
}
}
static void BluememeApplicationDidFailToRegisterForRemoteNotifications(id self, SEL _cmd, UIApplication *application, NSError *error)
{
BluememePostRemoteNotificationError(error);
SEL originalSelector = BluememeRemoteNotificationBridgeSelectorForSelector(_cmd);
if ([self respondsToSelector:originalSelector]) {
IMP originalImplementation = [self methodForSelector:originalSelector];
((void (*)(id, SEL, UIApplication *, NSError *))originalImplementation)(self, originalSelector, application, error);
}
}
@interface CDVBluememeUtils () @interface CDVBluememeUtils ()
@property (nonatomic, copy) NSString *notificationCallbackId; @property (nonatomic, copy) NSString *notificationCallbackId;
@@ -29,6 +78,7 @@ static NSTimeInterval const BluememeAPNSTokenTimeoutSeconds = 10.0;
if ([self ensureFirebaseConfigured:nil]) { if ([self ensureFirebaseConfigured:nil]) {
[FIRMessaging messaging].delegate = self; [FIRMessaging messaging].delegate = self;
} }
[self installRemoteNotificationDelegateBridgeIfNeeded];
if (@available(iOS 10.0, *)) { if (@available(iOS 10.0, *)) {
UNUserNotificationCenter *center = [UNUserNotificationCenter currentNotificationCenter]; UNUserNotificationCenter *center = [UNUserNotificationCenter currentNotificationCenter];
@@ -183,8 +233,7 @@ static NSTimeInterval const BluememeAPNSTokenTimeoutSeconds = 10.0;
dispatch_async(dispatch_get_main_queue(), ^{ dispatch_async(dispatch_get_main_queue(), ^{
NSMutableDictionary *result = [[self dictionaryFromNotificationSettings:settings] mutableCopy]; NSMutableDictionary *result = [[self dictionaryFromNotificationSettings:settings] mutableCopy];
result[@"granted"] = @(granted); result[@"granted"] = @(granted);
result[@"registeredForRemoteNotifications"] = @([[UIApplication sharedApplication] isRegisteredForRemoteNotifications]); [result addEntriesFromDictionary:[self apnsStateDetails]];
result[@"apnsTokenAvailable"] = @(self.apnsDeviceToken.length > 0);
[self sendSuccessForCommand:command dictionary:result]; [self sendSuccessForCommand:command dictionary:result];
}); });
}]; }];
@@ -194,16 +243,16 @@ static NSTimeInterval const BluememeAPNSTokenTimeoutSeconds = 10.0;
UIUserNotificationSettings *settings = [UIUserNotificationSettings settingsForTypes:(UIUserNotificationTypeAlert | UIUserNotificationTypeBadge | UIUserNotificationTypeSound) categories:nil]; UIUserNotificationSettings *settings = [UIUserNotificationSettings settingsForTypes:(UIUserNotificationTypeAlert | UIUserNotificationTypeBadge | UIUserNotificationTypeSound) categories:nil];
[[UIApplication sharedApplication] registerUserNotificationSettings:settings]; [[UIApplication sharedApplication] registerUserNotificationSettings:settings];
[[UIApplication sharedApplication] registerForRemoteNotifications]; [[UIApplication sharedApplication] registerForRemoteNotifications];
[self sendSuccessForCommand:command dictionary:@{ NSMutableDictionary *result = [[self apnsStateDetails] mutableCopy];
[result addEntriesFromDictionary:@{
@"type": @"notification", @"type": @"notification",
@"platform": @"iOS", @"platform": @"iOS",
@"enabled": @YES, @"enabled": @YES,
@"granted": @YES, @"granted": @YES,
@"authorizationStatus": @"authorized", @"authorizationStatus": @"authorized",
@"registeredForRemoteNotifications": @([[UIApplication sharedApplication] isRegisteredForRemoteNotifications]),
@"apnsTokenAvailable": @(self.apnsDeviceToken.length > 0),
@"missing": @[] @"missing": @[]
}]; }];
[self sendSuccessForCommand:command dictionary:result];
} }
} }
@@ -218,7 +267,8 @@ static NSTimeInterval const BluememeAPNSTokenTimeoutSeconds = 10.0;
} else { } else {
UIUserNotificationSettings *settings = [[UIApplication sharedApplication] currentUserNotificationSettings]; UIUserNotificationSettings *settings = [[UIApplication sharedApplication] currentUserNotificationSettings];
BOOL enabled = settings.types != UIUserNotificationTypeNone; BOOL enabled = settings.types != UIUserNotificationTypeNone;
[self sendSuccessForCommand:command dictionary:@{ NSMutableDictionary *result = [[self apnsStateDetails] mutableCopy];
[result addEntriesFromDictionary:@{
@"type": @"notification", @"type": @"notification",
@"platform": @"iOS", @"platform": @"iOS",
@"enabled": @(enabled), @"enabled": @(enabled),
@@ -227,10 +277,9 @@ static NSTimeInterval const BluememeAPNSTokenTimeoutSeconds = 10.0;
@"alertEnabled": @((settings.types & UIUserNotificationTypeAlert) != 0), @"alertEnabled": @((settings.types & UIUserNotificationTypeAlert) != 0),
@"soundEnabled": @((settings.types & UIUserNotificationTypeSound) != 0), @"soundEnabled": @((settings.types & UIUserNotificationTypeSound) != 0),
@"badgeEnabled": @((settings.types & UIUserNotificationTypeBadge) != 0), @"badgeEnabled": @((settings.types & UIUserNotificationTypeBadge) != 0),
@"registeredForRemoteNotifications": @([[UIApplication sharedApplication] isRegisteredForRemoteNotifications]),
@"apnsTokenAvailable": @(self.apnsDeviceToken.length > 0),
@"missing": enabled ? @[] : @[@"notification"] @"missing": enabled ? @[] : @[@"notification"]
}]; }];
[self sendSuccessForCommand:command dictionary:result];
} }
} }
@@ -248,8 +297,7 @@ static NSTimeInterval const BluememeAPNSTokenTimeoutSeconds = 10.0;
dispatch_async(dispatch_get_main_queue(), ^{ dispatch_async(dispatch_get_main_queue(), ^{
if (![self notificationAuthorizationAllowsFirebaseToken:settings.authorizationStatus]) { if (![self notificationAuthorizationAllowsFirebaseToken:settings.authorizationStatus]) {
NSMutableDictionary *details = [[self dictionaryFromNotificationSettings:settings] mutableCopy]; NSMutableDictionary *details = [[self dictionaryFromNotificationSettings:settings] mutableCopy];
details[@"apnsTokenAvailable"] = @(self.apnsDeviceToken.length > 0); [details addEntriesFromDictionary:[self apnsStateDetails]];
details[@"registeredForRemoteNotifications"] = @([[UIApplication sharedApplication] isRegisteredForRemoteNotifications]);
[self sendErrorForCommand:command [self sendErrorForCommand:command
code:@"PERMISSION_REQUIRED" code:@"PERMISSION_REQUIRED"
message:@"Call requestPushPermission and wait for notification authorization before getFirebaseToken." message:@"Call requestPushPermission and wait for notification authorization before getFirebaseToken."
@@ -267,63 +315,80 @@ static NSTimeInterval const BluememeAPNSTokenTimeoutSeconds = 10.0;
- (void)registerForRemoteNotificationsAndFetchFirebaseTokenForCommand:(CDVInvokedUrlCommand *)command - (void)registerForRemoteNotificationsAndFetchFirebaseTokenForCommand:(CDVInvokedUrlCommand *)command
{ {
if (self.apnsDeviceToken.length > 0) { NSData *tokenData = [self availableAPNSToken];
[FIRMessaging messaging].APNSToken = self.apnsDeviceToken; if (tokenData.length > 0) {
[FIRMessaging messaging].APNSToken = tokenData;
[self fetchFirebaseTokenForCommand:command]; [self fetchFirebaseTokenForCommand:command];
return; return;
} }
if (self.pendingFirebaseTokenCommand != nil) { if (self.pendingFirebaseTokenCommand != nil) {
NSMutableDictionary *details = [[self apnsStateDetails] mutableCopy];
details[@"apnsTokenAvailable"] = @NO;
[self sendErrorForCommand:self.pendingFirebaseTokenCommand [self sendErrorForCommand:self.pendingFirebaseTokenCommand
code:@"FCM_TOKEN_REQUEST_REPLACED" code:@"FCM_TOKEN_REQUEST_REPLACED"
message:@"A newer getFirebaseToken request replaced this pending request." message:@"A newer getFirebaseToken request replaced this pending request."
details:@{ details:details];
@"apnsTokenAvailable": @NO,
@"registeredForRemoteNotifications": @([[UIApplication sharedApplication] isRegisteredForRemoteNotifications])
}];
} }
self.pendingFirebaseTokenCommand = command; self.pendingFirebaseTokenCommand = command;
self.apnsRegistrationError = nil; self.apnsRegistrationError = nil;
[[UIApplication sharedApplication] registerForRemoteNotifications]; [[UIApplication sharedApplication] registerForRemoteNotifications];
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(BluememeAPNSTokenTimeoutSeconds * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{ NSDate *deadline = [NSDate dateWithTimeIntervalSinceNow:BluememeAPNSTokenTimeoutSeconds];
if (self.pendingFirebaseTokenCommand != command) { [self waitForAPNSTokenAndFetchFirebaseTokenForCommand:command deadline:deadline];
return; }
}
- (void)waitForAPNSTokenAndFetchFirebaseTokenForCommand:(CDVInvokedUrlCommand *)command deadline:(NSDate *)deadline
{
if (self.pendingFirebaseTokenCommand != command) {
return;
}
NSData *tokenData = [self availableAPNSToken];
if (tokenData.length > 0) {
self.pendingFirebaseTokenCommand = nil; self.pendingFirebaseTokenCommand = nil;
if (self.apnsRegistrationError != nil) { [FIRMessaging messaging].APNSToken = tokenData;
[self sendErrorForCommand:command [self fetchFirebaseTokenForCommand:command];
code:@"APNS_TOKEN_FAILED" return;
message:(self.apnsRegistrationError.localizedDescription ? self.apnsRegistrationError.localizedDescription : @"APNs registration failed.") }
details:[self detailsForNativeError:self.apnsRegistrationError includeAPNSState:YES]];
return;
}
if (self.apnsRegistrationError != nil) {
self.pendingFirebaseTokenCommand = 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;
}
if ([deadline timeIntervalSinceNow] <= 0) {
self.pendingFirebaseTokenCommand = nil;
NSMutableDictionary *details = [[self apnsStateDetails] mutableCopy];
details[@"timeoutSeconds"] = @(BluememeAPNSTokenTimeoutSeconds);
[self sendErrorForCommand:command [self sendErrorForCommand:command
code:@"APNS_TOKEN_NOT_AVAILABLE" 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." message:@"APNs device token was not received before fetching the FCM token. Check Push Notifications capability, aps-environment entitlement, provisioning profile, Firebase APNs key configuration, or AppDelegate remote notification forwarding."
details:@{ details:details];
@"apnsTokenAvailable": @NO, return;
@"registeredForRemoteNotifications": @([[UIApplication sharedApplication] isRegisteredForRemoteNotifications]), }
@"timeoutSeconds": @(BluememeAPNSTokenTimeoutSeconds)
}]; dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(BluememeAPNSTokenPollIntervalSeconds * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
[self waitForAPNSTokenAndFetchFirebaseTokenForCommand:command deadline:deadline];
}); });
} }
- (void)fetchFirebaseTokenForCommand:(CDVInvokedUrlCommand *)command - (void)fetchFirebaseTokenForCommand:(CDVInvokedUrlCommand *)command
{ {
if (self.apnsDeviceToken.length > 0) { NSData *tokenData = [self availableAPNSToken];
[FIRMessaging messaging].APNSToken = self.apnsDeviceToken; if (tokenData.length > 0) {
[FIRMessaging messaging].APNSToken = tokenData;
} }
[[FIRMessaging messaging] tokenWithCompletion:^(NSString *token, NSError *error) { [[FIRMessaging messaging] tokenWithCompletion:^(NSString *token, NSError *error) {
dispatch_async(dispatch_get_main_queue(), ^{ dispatch_async(dispatch_get_main_queue(), ^{
if (error != nil || token.length == 0) { if (error != nil || token.length == 0) {
NSMutableDictionary *details = [NSMutableDictionary dictionary]; NSMutableDictionary *details = [[self apnsStateDetails] mutableCopy];
details[@"apnsTokenAvailable"] = @(self.apnsDeviceToken.length > 0);
details[@"registeredForRemoteNotifications"] = @([[UIApplication sharedApplication] isRegisteredForRemoteNotifications]);
if (error != nil) { if (error != nil) {
[details addEntriesFromDictionary:[self detailsForNativeError:error includeAPNSState:NO]]; [details addEntriesFromDictionary:[self detailsForNativeError:error includeAPNSState:NO]];
} }
@@ -333,12 +398,10 @@ static NSTimeInterval const BluememeAPNSTokenTimeoutSeconds = 10.0;
details:details]; details:details];
return; return;
} }
[self sendSuccessForCommand:command dictionary:@{ NSMutableDictionary *result = [[self apnsStateDetails] mutableCopy];
@"token": token, result[@"token"] = token;
@"platform": @"iOS", result[@"platform"] = @"iOS";
@"apnsTokenAvailable": @(self.apnsDeviceToken.length > 0), [self sendSuccessForCommand:command dictionary:result];
@"registeredForRemoteNotifications": @([[UIApplication sharedApplication] isRegisteredForRemoteNotifications])
}];
[self sendEvent:@{@"type": @"token", @"token": token}]; [self sendEvent:@{@"type": @"token", @"token": token}];
}); });
}]; }];
@@ -835,7 +898,8 @@ static NSTimeInterval const BluememeAPNSTokenTimeoutSeconds = 10.0;
BOOL badgeEnabled = settings.badgeSetting == UNNotificationSettingEnabled; BOOL badgeEnabled = settings.badgeSetting == UNNotificationSettingEnabled;
NSArray *missing = enabled ? @[] : @[@"notification"]; NSArray *missing = enabled ? @[] : @[@"notification"];
return @{ NSMutableDictionary *result = [[self apnsStateDetails] mutableCopy];
[result addEntriesFromDictionary:@{
@"type": @"notification", @"type": @"notification",
@"platform": @"iOS", @"platform": @"iOS",
@"enabled": @(enabled), @"enabled": @(enabled),
@@ -844,10 +908,9 @@ static NSTimeInterval const BluememeAPNSTokenTimeoutSeconds = 10.0;
@"alertEnabled": @(alertEnabled), @"alertEnabled": @(alertEnabled),
@"soundEnabled": @(soundEnabled), @"soundEnabled": @(soundEnabled),
@"badgeEnabled": @(badgeEnabled), @"badgeEnabled": @(badgeEnabled),
@"registeredForRemoteNotifications": @([[UIApplication sharedApplication] isRegisteredForRemoteNotifications]),
@"apnsTokenAvailable": @(self.apnsDeviceToken.length > 0),
@"missing": missing @"missing": missing
}; }];
return result;
} }
- (NSString *)authorizationStatusName:(UNAuthorizationStatus)status API_AVAILABLE(ios(10.0)) - (NSString *)authorizationStatusName:(UNAuthorizationStatus)status API_AVAILABLE(ios(10.0))
@@ -894,6 +957,103 @@ static NSTimeInterval const BluememeAPNSTokenTimeoutSeconds = 10.0;
return NO; return NO;
} }
- (NSData *)firebaseAPNSToken
{
if ([FIRApp defaultApp] == nil) {
return nil;
}
return [FIRMessaging messaging].APNSToken;
}
- (NSData *)availableAPNSToken
{
if (self.apnsDeviceToken.length > 0) {
return self.apnsDeviceToken;
}
NSData *firebaseToken = [self firebaseAPNSToken];
if (firebaseToken.length > 0) {
self.apnsDeviceToken = firebaseToken;
self.apnsRegistrationError = nil;
return firebaseToken;
}
return nil;
}
- (NSDictionary *)apnsStateDetails
{
NSData *cachedToken = self.apnsDeviceToken;
NSData *firebaseToken = [self firebaseAPNSToken];
BOOL cachedTokenAvailable = cachedToken.length > 0;
BOOL firebaseTokenAvailable = firebaseToken.length > 0;
NSString *source = @"none";
if (cachedTokenAvailable) {
source = @"plugin";
} else if (firebaseTokenAvailable) {
source = @"firebase";
}
return @{
@"apnsTokenAvailable": @(cachedTokenAvailable || firebaseTokenAvailable),
@"cachedAPNSTokenAvailable": @(cachedTokenAvailable),
@"firebaseAPNSTokenAvailable": @(firebaseTokenAvailable),
@"apnsTokenSource": source,
@"registeredForRemoteNotifications": @([[UIApplication sharedApplication] isRegisteredForRemoteNotifications])
};
}
- (void)installRemoteNotificationDelegateBridgeIfNeeded
{
id<UIApplicationDelegate> appDelegate = [UIApplication sharedApplication].delegate;
if (appDelegate == nil) {
return;
}
Class appDelegateClass = [appDelegate class];
if ([objc_getAssociatedObject(appDelegateClass, &BluememeRemoteNotificationBridgeInstalledKey) boolValue]) {
return;
}
[self installRemoteNotificationBridgeForClass:appDelegateClass
selector:@selector(application:didRegisterForRemoteNotificationsWithDeviceToken:)
implementation:(IMP)BluememeApplicationDidRegisterForRemoteNotifications
typeEncoding:"v@:@@"];
[self installRemoteNotificationBridgeForClass:appDelegateClass
selector:@selector(application:didFailToRegisterForRemoteNotificationsWithError:)
implementation:(IMP)BluememeApplicationDidFailToRegisterForRemoteNotifications
typeEncoding:"v@:@@"];
objc_setAssociatedObject(appDelegateClass, &BluememeRemoteNotificationBridgeInstalledKey, @YES, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
}
- (void)installRemoteNotificationBridgeForClass:(Class)delegateClass
selector:(SEL)selector
implementation:(IMP)implementation
typeEncoding:(const char *)fallbackTypeEncoding
{
Method originalMethod = class_getInstanceMethod(delegateClass, selector);
const char *originalTypeEncoding = originalMethod == NULL ? NULL : method_getTypeEncoding(originalMethod);
const char *typeEncoding = originalTypeEncoding == NULL ? fallbackTypeEncoding : originalTypeEncoding;
if (originalMethod == NULL) {
class_addMethod(delegateClass, selector, implementation, typeEncoding);
return;
}
SEL bridgeSelector = BluememeRemoteNotificationBridgeSelectorForSelector(selector);
if (class_addMethod(delegateClass, selector, implementation, typeEncoding)) {
class_addMethod(delegateClass, bridgeSelector, method_getImplementation(originalMethod), typeEncoding);
return;
}
if (!class_addMethod(delegateClass, bridgeSelector, implementation, typeEncoding)) {
return;
}
Method bridgeMethod = class_getInstanceMethod(delegateClass, bridgeSelector);
originalMethod = class_getInstanceMethod(delegateClass, selector);
method_exchangeImplementations(originalMethod, bridgeMethod);
}
- (void)remoteNotificationRegistered:(NSNotification *)notification - (void)remoteNotificationRegistered:(NSNotification *)notification
{ {
NSData *tokenData = [self dataFromAPNSTokenObject:notification.object]; NSData *tokenData = [self dataFromAPNSTokenObject:notification.object];
@@ -992,8 +1152,7 @@ static NSTimeInterval const BluememeAPNSTokenTimeoutSeconds = 10.0;
{ {
NSMutableDictionary *details = [NSMutableDictionary dictionary]; NSMutableDictionary *details = [NSMutableDictionary dictionary];
if (includeAPNSState) { if (includeAPNSState) {
details[@"apnsTokenAvailable"] = @(self.apnsDeviceToken.length > 0); [details addEntriesFromDictionary:[self apnsStateDetails]];
details[@"registeredForRemoteNotifications"] = @([[UIApplication sharedApplication] isRegisteredForRemoteNotifications]);
} }
if (error.domain.length > 0) { if (error.domain.length > 0) {
details[@"nativeDomain"] = error.domain; details[@"nativeDomain"] = error.domain;