Compare commits
2
Commits
61e04723f3
...
0e90516856
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
0e90516856 | ||
|
|
8fcb440d8a |
No files matched your search
@@ -113,13 +113,9 @@ WiFi options:
|
||||
```js
|
||||
BluememeUtils.requestPushPermission(success, error);
|
||||
BluememeUtils.getPushPermissionStatus(success, error);
|
||||
BluememeUtils.ensurePushPermission(success, error);
|
||||
BluememeUtils.getFirebaseToken(function (result) {
|
||||
console.log(result.token);
|
||||
}, error);
|
||||
BluememeUtils.getFirebaseTokenWithPermission(function (result) {
|
||||
console.log(result.token);
|
||||
}, error);
|
||||
|
||||
BluememeUtils.subscribeToTopic("news", success, error);
|
||||
BluememeUtils.unsubscribeFromTopic("news", success, error);
|
||||
@@ -140,19 +136,31 @@ BluememeUtils.getInitialNotification(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
|
||||
BluememeUtils.ensurePushPermission(function () {
|
||||
BluememeUtils.requestPushPermission(function (permission) {
|
||||
if (permission.enabled !== true) {
|
||||
return;
|
||||
}
|
||||
|
||||
BluememeUtils.getFirebaseToken(function (result) {
|
||||
console.log(result.token);
|
||||
}, 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
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"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.",
|
||||
"license": "MIT",
|
||||
"files": [
|
||||
|
||||
+1
-1
@@ -2,7 +2,7 @@
|
||||
<plugin xmlns="http://apache.org/cordova/ns/plugins/1.0"
|
||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
id="bluememe-utils"
|
||||
version="0.1.12">
|
||||
version="0.1.13">
|
||||
<name>bluememe-utils</name>
|
||||
<description>Cordova utilities for WiFi, Firebase Cloud Messaging, and biometric unlock.</description>
|
||||
<license>MIT</license>
|
||||
|
||||
+210
-51
@@ -6,12 +6,61 @@
|
||||
#import <SystemConfiguration/CaptiveNetwork.h>
|
||||
#import <TargetConditionals.h>
|
||||
#import <UIKit/UIKit.h>
|
||||
#import <objc/runtime.h>
|
||||
|
||||
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 BluememeCordovaRemoteNotificationErrorName = @"CDVRemoteNotificationError";
|
||||
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 ()
|
||||
@property (nonatomic, copy) NSString *notificationCallbackId;
|
||||
@@ -29,6 +78,7 @@ static NSTimeInterval const BluememeAPNSTokenTimeoutSeconds = 10.0;
|
||||
if ([self ensureFirebaseConfigured:nil]) {
|
||||
[FIRMessaging messaging].delegate = self;
|
||||
}
|
||||
[self installRemoteNotificationDelegateBridgeIfNeeded];
|
||||
|
||||
if (@available(iOS 10.0, *)) {
|
||||
UNUserNotificationCenter *center = [UNUserNotificationCenter currentNotificationCenter];
|
||||
@@ -183,8 +233,7 @@ static NSTimeInterval const BluememeAPNSTokenTimeoutSeconds = 10.0;
|
||||
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);
|
||||
[result addEntriesFromDictionary:[self apnsStateDetails]];
|
||||
[self sendSuccessForCommand:command dictionary:result];
|
||||
});
|
||||
}];
|
||||
@@ -194,16 +243,16 @@ static NSTimeInterval const BluememeAPNSTokenTimeoutSeconds = 10.0;
|
||||
UIUserNotificationSettings *settings = [UIUserNotificationSettings settingsForTypes:(UIUserNotificationTypeAlert | UIUserNotificationTypeBadge | UIUserNotificationTypeSound) categories:nil];
|
||||
[[UIApplication sharedApplication] registerUserNotificationSettings:settings];
|
||||
[[UIApplication sharedApplication] registerForRemoteNotifications];
|
||||
[self sendSuccessForCommand:command dictionary:@{
|
||||
NSMutableDictionary *result = [[self apnsStateDetails] mutableCopy];
|
||||
[result addEntriesFromDictionary:@{
|
||||
@"type": @"notification",
|
||||
@"platform": @"iOS",
|
||||
@"enabled": @YES,
|
||||
@"granted": @YES,
|
||||
@"authorizationStatus": @"authorized",
|
||||
@"registeredForRemoteNotifications": @([[UIApplication sharedApplication] isRegisteredForRemoteNotifications]),
|
||||
@"apnsTokenAvailable": @(self.apnsDeviceToken.length > 0),
|
||||
@"missing": @[]
|
||||
}];
|
||||
[self sendSuccessForCommand:command dictionary:result];
|
||||
}
|
||||
}
|
||||
|
||||
@@ -218,7 +267,8 @@ static NSTimeInterval const BluememeAPNSTokenTimeoutSeconds = 10.0;
|
||||
} else {
|
||||
UIUserNotificationSettings *settings = [[UIApplication sharedApplication] currentUserNotificationSettings];
|
||||
BOOL enabled = settings.types != UIUserNotificationTypeNone;
|
||||
[self sendSuccessForCommand:command dictionary:@{
|
||||
NSMutableDictionary *result = [[self apnsStateDetails] mutableCopy];
|
||||
[result addEntriesFromDictionary:@{
|
||||
@"type": @"notification",
|
||||
@"platform": @"iOS",
|
||||
@"enabled": @(enabled),
|
||||
@@ -227,10 +277,9 @@ static NSTimeInterval const BluememeAPNSTokenTimeoutSeconds = 10.0;
|
||||
@"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"]
|
||||
}];
|
||||
[self sendSuccessForCommand:command dictionary:result];
|
||||
}
|
||||
}
|
||||
|
||||
@@ -248,8 +297,7 @@ static NSTimeInterval const BluememeAPNSTokenTimeoutSeconds = 10.0;
|
||||
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]);
|
||||
[details addEntriesFromDictionary:[self apnsStateDetails]];
|
||||
[self sendErrorForCommand:command
|
||||
code:@"PERMISSION_REQUIRED"
|
||||
message:@"Call requestPushPermission and wait for notification authorization before getFirebaseToken."
|
||||
@@ -267,63 +315,80 @@ static NSTimeInterval const BluememeAPNSTokenTimeoutSeconds = 10.0;
|
||||
|
||||
- (void)registerForRemoteNotificationsAndFetchFirebaseTokenForCommand:(CDVInvokedUrlCommand *)command
|
||||
{
|
||||
if (self.apnsDeviceToken.length > 0) {
|
||||
[FIRMessaging messaging].APNSToken = self.apnsDeviceToken;
|
||||
NSData *tokenData = [self availableAPNSToken];
|
||||
if (tokenData.length > 0) {
|
||||
[FIRMessaging messaging].APNSToken = tokenData;
|
||||
[self fetchFirebaseTokenForCommand:command];
|
||||
return;
|
||||
}
|
||||
|
||||
if (self.pendingFirebaseTokenCommand != nil) {
|
||||
NSMutableDictionary *details = [[self apnsStateDetails] mutableCopy];
|
||||
details[@"apnsTokenAvailable"] = @NO;
|
||||
[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])
|
||||
}];
|
||||
details:details];
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
NSDate *deadline = [NSDate dateWithTimeIntervalSinceNow:BluememeAPNSTokenTimeoutSeconds];
|
||||
[self waitForAPNSTokenAndFetchFirebaseTokenForCommand:command deadline:deadline];
|
||||
}
|
||||
|
||||
- (void)waitForAPNSTokenAndFetchFirebaseTokenForCommand:(CDVInvokedUrlCommand *)command deadline:(NSDate *)deadline
|
||||
{
|
||||
if (self.pendingFirebaseTokenCommand != command) {
|
||||
return;
|
||||
}
|
||||
|
||||
NSData *tokenData = [self availableAPNSToken];
|
||||
if (tokenData.length > 0) {
|
||||
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;
|
||||
}
|
||||
[FIRMessaging messaging].APNSToken = tokenData;
|
||||
[self fetchFirebaseTokenForCommand:command];
|
||||
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
|
||||
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)
|
||||
}];
|
||||
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];
|
||||
return;
|
||||
}
|
||||
|
||||
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
|
||||
{
|
||||
if (self.apnsDeviceToken.length > 0) {
|
||||
[FIRMessaging messaging].APNSToken = self.apnsDeviceToken;
|
||||
NSData *tokenData = [self availableAPNSToken];
|
||||
if (tokenData.length > 0) {
|
||||
[FIRMessaging messaging].APNSToken = tokenData;
|
||||
}
|
||||
|
||||
[[FIRMessaging messaging] tokenWithCompletion:^(NSString *token, NSError *error) {
|
||||
dispatch_async(dispatch_get_main_queue(), ^{
|
||||
if (error != nil || token.length == 0) {
|
||||
NSMutableDictionary *details = [NSMutableDictionary dictionary];
|
||||
details[@"apnsTokenAvailable"] = @(self.apnsDeviceToken.length > 0);
|
||||
details[@"registeredForRemoteNotifications"] = @([[UIApplication sharedApplication] isRegisteredForRemoteNotifications]);
|
||||
NSMutableDictionary *details = [[self apnsStateDetails] mutableCopy];
|
||||
if (error != nil) {
|
||||
[details addEntriesFromDictionary:[self detailsForNativeError:error includeAPNSState:NO]];
|
||||
}
|
||||
@@ -333,12 +398,10 @@ static NSTimeInterval const BluememeAPNSTokenTimeoutSeconds = 10.0;
|
||||
details:details];
|
||||
return;
|
||||
}
|
||||
[self sendSuccessForCommand:command dictionary:@{
|
||||
@"token": token,
|
||||
@"platform": @"iOS",
|
||||
@"apnsTokenAvailable": @(self.apnsDeviceToken.length > 0),
|
||||
@"registeredForRemoteNotifications": @([[UIApplication sharedApplication] isRegisteredForRemoteNotifications])
|
||||
}];
|
||||
NSMutableDictionary *result = [[self apnsStateDetails] mutableCopy];
|
||||
result[@"token"] = token;
|
||||
result[@"platform"] = @"iOS";
|
||||
[self sendSuccessForCommand:command dictionary:result];
|
||||
[self sendEvent:@{@"type": @"token", @"token": token}];
|
||||
});
|
||||
}];
|
||||
@@ -835,7 +898,8 @@ static NSTimeInterval const BluememeAPNSTokenTimeoutSeconds = 10.0;
|
||||
BOOL badgeEnabled = settings.badgeSetting == UNNotificationSettingEnabled;
|
||||
NSArray *missing = enabled ? @[] : @[@"notification"];
|
||||
|
||||
return @{
|
||||
NSMutableDictionary *result = [[self apnsStateDetails] mutableCopy];
|
||||
[result addEntriesFromDictionary:@{
|
||||
@"type": @"notification",
|
||||
@"platform": @"iOS",
|
||||
@"enabled": @(enabled),
|
||||
@@ -844,10 +908,9 @@ static NSTimeInterval const BluememeAPNSTokenTimeoutSeconds = 10.0;
|
||||
@"alertEnabled": @(alertEnabled),
|
||||
@"soundEnabled": @(soundEnabled),
|
||||
@"badgeEnabled": @(badgeEnabled),
|
||||
@"registeredForRemoteNotifications": @([[UIApplication sharedApplication] isRegisteredForRemoteNotifications]),
|
||||
@"apnsTokenAvailable": @(self.apnsDeviceToken.length > 0),
|
||||
@"missing": missing
|
||||
};
|
||||
}];
|
||||
return result;
|
||||
}
|
||||
|
||||
- (NSString *)authorizationStatusName:(UNAuthorizationStatus)status API_AVAILABLE(ios(10.0))
|
||||
@@ -894,6 +957,103 @@ static NSTimeInterval const BluememeAPNSTokenTimeoutSeconds = 10.0;
|
||||
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
|
||||
{
|
||||
NSData *tokenData = [self dataFromAPNSTokenObject:notification.object];
|
||||
@@ -992,8 +1152,7 @@ static NSTimeInterval const BluememeAPNSTokenTimeoutSeconds = 10.0;
|
||||
{
|
||||
NSMutableDictionary *details = [NSMutableDictionary dictionary];
|
||||
if (includeAPNSState) {
|
||||
details[@"apnsTokenAvailable"] = @(self.apnsDeviceToken.length > 0);
|
||||
details[@"registeredForRemoteNotifications"] = @([[UIApplication sharedApplication] isRegisteredForRemoteNotifications]);
|
||||
[details addEntriesFromDictionary:[self apnsStateDetails]];
|
||||
}
|
||||
if (error.domain.length > 0) {
|
||||
details[@"nativeDomain"] = error.domain;
|
||||
|
||||
Reference in new issue
Block a user