-
Notifications
You must be signed in to change notification settings - Fork 1
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
ios: add default chained notification delegate support
- Loading branch information
1 parent
6228fc2
commit e4ca119
Showing
3 changed files
with
201 additions
and
1 deletion.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,43 @@ | ||
// | ||
// Copyright © Batch.com. All rights reserved. | ||
// | ||
|
||
#import <Foundation/Foundation.h> | ||
#import <UserNotifications/UserNotifications.h> | ||
|
||
NS_ASSUME_NONNULL_BEGIN | ||
|
||
// Batch's bridge UNUserNotificationCenterDelegate | ||
// Handles: | ||
// - Forwarding calls to another delegate (chaining, rather than swizzling) | ||
// - Giving notification callbacks to Batch | ||
// - Enabling/Disabling foreground notifications | ||
@interface BatchBridgeNotificationCenterDelegate : NSObject <UNUserNotificationCenterDelegate> | ||
|
||
/// Shared singleton BatchUNUserNotificationCenterDelegate. | ||
/// Using this allows you to set the instance as UNUserNotificationCenter's delegate without having to retain it yourself. | ||
/// The shared instance is lazily loaded. | ||
@property (class, retain, readonly, nonnull) BatchBridgeNotificationCenterDelegate* sharedInstance; | ||
|
||
/// Registers this class' sharedInstance as UNUserNotificationCenter's delegate, and stores the previous one as a property | ||
+ (void)registerAsDelegate; | ||
|
||
/// Should iOS display notifications even if the app is in foreground? | ||
/// Default: true | ||
@property (assign) BOOL showForegroundNotifications; | ||
|
||
/// Should Batch use the chained delegate's completionHandler responses or force its own, while still calling the chained delegate. | ||
/// This is useful if you want Batch to enforce its "showForegroundNotifications" setting while still informing the chained delegate. | ||
/// Default: true, but the plugin will automatically set that to false when calling "setShowForegroundNotification" from JavaScript. | ||
@property (assign) BOOL shouldUseChainedCompletionHandlerResponse; | ||
|
||
/// Previous delegate | ||
@property (weak, nullable) id<UNUserNotificationCenterDelegate> previousDelegate; | ||
|
||
/// Should this class automatically register itself as UNUserNotificationCenterDelegate when the app is launched? Default: true | ||
/// This value needs to be changed before `[RNBatch start]` be called. | ||
@property (class, assign) BOOL automaticallyRegister; | ||
|
||
@end | ||
|
||
NS_ASSUME_NONNULL_END |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,151 @@ | ||
// | ||
// Copyright © Batch.com. All rights reserved. | ||
// | ||
|
||
#import "BatchBridgeNotificationCenterDelegate.h" | ||
|
||
#import <Batch/BatchPush.h> | ||
|
||
@implementation BatchBridgeNotificationCenterDelegate | ||
{ | ||
__weak __nullable id<UNUserNotificationCenterDelegate> _previousDelegate; | ||
} | ||
|
||
static BOOL _batBridgeNotifDelegateShouldAutomaticallyRegister = true; | ||
|
||
|
||
+ (BatchBridgeNotificationCenterDelegate *)sharedInstance | ||
{ | ||
static BatchBridgeNotificationCenterDelegate *sharedInstance = nil; | ||
static dispatch_once_t onceToken; | ||
dispatch_once(&onceToken, ^{ | ||
sharedInstance = [[BatchBridgeNotificationCenterDelegate alloc] init]; | ||
}); | ||
|
||
return sharedInstance; | ||
} | ||
|
||
+ (void)registerAsDelegate | ||
{ | ||
UNUserNotificationCenter *notifCenter = [UNUserNotificationCenter currentNotificationCenter]; | ||
BatchBridgeNotificationCenterDelegate *instance = [self sharedInstance]; | ||
instance.previousDelegate = notifCenter.delegate; | ||
notifCenter.delegate = instance; | ||
} | ||
|
||
+ (BOOL)automaticallyRegister | ||
{ | ||
return _batBridgeNotifDelegateShouldAutomaticallyRegister; | ||
} | ||
|
||
+ (void)setAutomaticallyRegister:(BOOL)automaticallyRegister | ||
{ | ||
_batBridgeNotifDelegateShouldAutomaticallyRegister = automaticallyRegister; | ||
} | ||
|
||
- (nullable id<UNUserNotificationCenterDelegate>)previousDelegate | ||
{ | ||
return _previousDelegate; | ||
} | ||
|
||
- (void)setPreviousDelegate:(nullable id<UNUserNotificationCenterDelegate>)delegate | ||
{ | ||
// Do not register ourserlves as previous delegate to avoid | ||
// an infinite loop | ||
if (delegate == self || [delegate isKindOfClass:[self class]]) { | ||
_previousDelegate = nil; | ||
} else { | ||
_previousDelegate = delegate; | ||
} | ||
} | ||
|
||
- (instancetype)init | ||
{ | ||
self = [super init]; | ||
if (self) { | ||
_showForegroundNotifications = true; | ||
_shouldUseChainedCompletionHandlerResponse = true; | ||
} | ||
return self; | ||
} | ||
|
||
- (void)userNotificationCenter:(UNUserNotificationCenter *)center willPresentNotification:(UNNotification *)notification withCompletionHandler:(void (^)(UNNotificationPresentationOptions))completionHandler | ||
{ | ||
[BatchPush handleUserNotificationCenter:center willPresentNotification:notification willShowSystemForegroundAlert:self.showForegroundNotifications]; | ||
|
||
id<UNUserNotificationCenterDelegate> chainDelegate = self.previousDelegate; | ||
// It's the chain delegate's responsibility to call the completionHandler | ||
if ([chainDelegate respondsToSelector:@selector(userNotificationCenter:willPresentNotification:withCompletionHandler:)]) { | ||
//returnType (^blockName)(parameterTypes) = ^returnType(parameters) {...}; | ||
void (^chainCompletionHandler)(UNNotificationPresentationOptions); | ||
|
||
if (self.shouldUseChainedCompletionHandlerResponse) { | ||
// Set iOS' completion handler as the one we give to the method, as we don't want to override the result | ||
chainCompletionHandler = completionHandler; | ||
} else { | ||
// Set ourselves as the chained completion handler so we can wait for the implementation but rewrite the response | ||
chainCompletionHandler = ^(UNNotificationPresentationOptions ignored) { | ||
[self performPresentCompletionHandler:completionHandler]; | ||
}; | ||
} | ||
|
||
[chainDelegate userNotificationCenter:center | ||
willPresentNotification:notification | ||
withCompletionHandler:chainCompletionHandler]; | ||
} else { | ||
[self performPresentCompletionHandler:completionHandler]; | ||
} | ||
} | ||
|
||
- (void)userNotificationCenter:(UNUserNotificationCenter *)center didReceiveNotificationResponse:(UNNotificationResponse *)response withCompletionHandler:(void (^)(void))completionHandler | ||
{ | ||
[BatchPush handleUserNotificationCenter:center didReceiveNotificationResponse:response]; | ||
|
||
id<UNUserNotificationCenterDelegate> chainDelegate = self.previousDelegate; | ||
// It's the chain delegate's responsibility to call the completionHandler | ||
if ([chainDelegate respondsToSelector:@selector(userNotificationCenter:didReceiveNotificationResponse:withCompletionHandler:)]) { | ||
[chainDelegate userNotificationCenter:center | ||
didReceiveNotificationResponse:response | ||
withCompletionHandler:completionHandler]; | ||
} else { | ||
if (completionHandler) { | ||
completionHandler(); | ||
} | ||
} | ||
|
||
} | ||
|
||
- (void)userNotificationCenter:(UNUserNotificationCenter *)center openSettingsForNotification:(UNNotification *)notification | ||
{ | ||
if (@available(iOS 12.0, *)) { | ||
id<UNUserNotificationCenterDelegate> chainDelegate = self.previousDelegate; | ||
if ([chainDelegate respondsToSelector:@selector(userNotificationCenter:openSettingsForNotification:)]) { | ||
[self.previousDelegate userNotificationCenter:center | ||
openSettingsForNotification:notification]; | ||
} | ||
} | ||
} | ||
|
||
/// Call iOS back on the "present" completion handler with Batch controlled presentation options | ||
- (void)performPresentCompletionHandler:(void (^)(UNNotificationPresentationOptions))completionHandler { | ||
UNNotificationPresentationOptions options = UNNotificationPresentationOptionNone; | ||
if (self.showForegroundNotifications) { | ||
options = UNNotificationPresentationOptionBadge | UNNotificationPresentationOptionSound; | ||
|
||
#ifdef __IPHONE_14_0 | ||
if (@available(iOS 14.0, *)) { | ||
options = options | UNNotificationPresentationOptionList | UNNotificationPresentationOptionBanner; | ||
} else { | ||
options = options | UNNotificationPresentationOptionAlert; | ||
} | ||
#else | ||
options = options | UNNotificationPresentationOptionAlert; | ||
#endif | ||
} | ||
|
||
if (completionHandler) { | ||
completionHandler(options); | ||
}; | ||
} | ||
|
||
@end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters