diff --git a/ios/Capacitor/Capacitor.xcodeproj/project.pbxproj b/ios/Capacitor/Capacitor.xcodeproj/project.pbxproj index c2f49b5af9..3c97539366 100644 --- a/ios/Capacitor/Capacitor.xcodeproj/project.pbxproj +++ b/ios/Capacitor/Capacitor.xcodeproj/project.pbxproj @@ -7,6 +7,8 @@ objects = { /* Begin PBXBuildFile section */ + 373A69C1255C9360000A6F44 /* NotificationHandlerProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = 373A69C0255C9360000A6F44 /* NotificationHandlerProtocol.swift */; }; + 373A69F2255C95D0000A6F44 /* NotificationRouter.swift in Sources */ = {isa = PBXBuildFile; fileRef = 373A69F1255C95D0000A6F44 /* NotificationRouter.swift */; }; 501CBAA71FC0A723009B0D4D /* WebKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 501CBAA61FC0A723009B0D4D /* WebKit.framework */; }; 50503EE91FC08595003606DC /* Capacitor.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 50503EDF1FC08594003606DC /* Capacitor.framework */; }; 50503EEE1FC08595003606DC /* CapacitorTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 50503EED1FC08595003606DC /* CapacitorTests.swift */; }; @@ -122,6 +124,8 @@ /* End PBXCopyFilesBuildPhase section */ /* Begin PBXFileReference section */ + 373A69C0255C9360000A6F44 /* NotificationHandlerProtocol.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NotificationHandlerProtocol.swift; sourceTree = ""; }; + 373A69F1255C95D0000A6F44 /* NotificationRouter.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NotificationRouter.swift; sourceTree = ""; }; 501CBAA61FC0A723009B0D4D /* WebKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = WebKit.framework; path = System/Library/Frameworks/WebKit.framework; sourceTree = SDKROOT; }; 50503EDF1FC08594003606DC /* Capacitor.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Capacitor.framework; sourceTree = BUILT_PRODUCTS_DIR; }; 50503EE81FC08595003606DC /* CapacitorTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = CapacitorTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; @@ -319,6 +323,8 @@ 62959AFE2524DA7700A3D7F1 /* UIStatusBarManager+CAPHandleTapAction.m */, 62959B122524DA7700A3D7F1 /* Info.plist */, 62959B8225253A9500A3D7F1 /* Capacitor.modulemap */, + 373A69C0255C9360000A6F44 /* NotificationHandlerProtocol.swift */, + 373A69F1255C95D0000A6F44 /* NotificationRouter.swift */, ); path = Capacitor; sourceTree = ""; @@ -557,12 +563,14 @@ 62959B472524DA7800A3D7F1 /* CAPNotifications.swift in Sources */, 62959B312524DA7800A3D7F1 /* JS.swift in Sources */, 62959B292524DA7800A3D7F1 /* SplashScreen.swift in Sources */, + 373A69F2255C95D0000A6F44 /* NotificationRouter.swift in Sources */, 62959B1A2524DA7800A3D7F1 /* CAPPluginCall.swift in Sources */, 62959B302524DA7800A3D7F1 /* UIStatusBarManager+CAPHandleTapAction.m in Sources */, 62959B392524DA7800A3D7F1 /* CapacitorExtension.swift in Sources */, 62959B3D2524DA7800A3D7F1 /* CAPMessageHandlerWrapper.swift in Sources */, 62959B422524DA7800A3D7F1 /* DocLinks.swift in Sources */, 62959B172524DA7800A3D7F1 /* JSExport.swift in Sources */, + 373A69C1255C9360000A6F44 /* NotificationHandlerProtocol.swift in Sources */, 62959B3F2524DA7800A3D7F1 /* CAPAssetHandler.swift in Sources */, 62959B3C2524DA7800A3D7F1 /* CAPBridgeDelegate.swift in Sources */, 62959B2F2524DA7800A3D7F1 /* DefaultPlugins.m in Sources */, diff --git a/ios/Capacitor/Capacitor/CAPBridgeProtocol.swift b/ios/Capacitor/Capacitor/CAPBridgeProtocol.swift index 9bf0d773b4..37680bd566 100644 --- a/ios/Capacitor/Capacitor/CAPBridgeProtocol.swift +++ b/ios/Capacitor/Capacitor/CAPBridgeProtocol.swift @@ -6,6 +6,7 @@ import WebKit var viewController: UIViewController? { get } var config: InstanceConfiguration { get } var webView: WKWebView? { get } + var notificationRouter: NotificationRouter { get } var isSimEnvironment: Bool { get } var isDevEnvironment: Bool { get } @available(iOS 12.0, *) diff --git a/ios/Capacitor/Capacitor/CAPInstanceConfiguration.h b/ios/Capacitor/Capacitor/CAPInstanceConfiguration.h index 2f7d3cd033..e292ff09a7 100644 --- a/ios/Capacitor/Capacitor/CAPInstanceConfiguration.h +++ b/ios/Capacitor/Capacitor/CAPInstanceConfiguration.h @@ -17,6 +17,7 @@ NS_SWIFT_NAME(InstanceConfiguration) @property (nonatomic, readonly) BOOL enableLogging; @property (nonatomic, readonly) BOOL enableScrolling; @property (nonatomic, readonly) BOOL allowLinkPreviews; +@property (nonatomic, readonly) BOOL handleApplicationNotifications; @property (nonatomic, readonly) BOOL cordovaDeployDisabled; @property (nonatomic, readonly) UIScrollViewContentInsetAdjustmentBehavior contentInsetAdjustmentBehavior; @property (nonatomic, readonly, nonnull) NSURL *appLocation; diff --git a/ios/Capacitor/Capacitor/CAPInstanceConfiguration.m b/ios/Capacitor/Capacitor/CAPInstanceConfiguration.m index ad8c7b5a2b..0acdef46f5 100644 --- a/ios/Capacitor/Capacitor/CAPInstanceConfiguration.m +++ b/ios/Capacitor/Capacitor/CAPInstanceConfiguration.m @@ -15,6 +15,7 @@ - (instancetype)initWithDescriptor:(CAPInstanceDescriptor *)descriptor { _enableLogging = descriptor.enableLogging; _enableScrolling = descriptor.enableScrolling; _allowLinkPreviews = descriptor.allowLinkPreviews; + _handleApplicationNotifications = descriptor.handleApplicationNotifications; _contentInsetAdjustmentBehavior = descriptor.contentInsetAdjustmentBehavior; _appLocation = descriptor.appLocation; _pluginConfigurations = descriptor.pluginConfigurations; diff --git a/ios/Capacitor/Capacitor/CAPInstanceDescriptor.h b/ios/Capacitor/Capacitor/CAPInstanceDescriptor.h index 48433a533d..0f7469e397 100644 --- a/ios/Capacitor/Capacitor/CAPInstanceDescriptor.h +++ b/ios/Capacitor/Capacitor/CAPInstanceDescriptor.h @@ -34,6 +34,7 @@ NS_SWIFT_NAME(InstanceDescriptor) @property (nonatomic, assign) BOOL enableLogging; @property (nonatomic, assign) BOOL enableScrolling; @property (nonatomic, assign) BOOL allowLinkPreviews; +@property (nonatomic, assign) BOOL handleApplicationNotifications; @property (nonatomic, assign) UIScrollViewContentInsetAdjustmentBehavior contentInsetAdjustmentBehavior; @property (nonatomic, copy, nonnull) NSURL *appLocation; @property (nonatomic, copy, nonnull) CDVConfigParser *cordovaConfiguration; diff --git a/ios/Capacitor/Capacitor/CAPInstanceDescriptor.m b/ios/Capacitor/Capacitor/CAPInstanceDescriptor.m index c4c0c08473..66a7d93bed 100644 --- a/ios/Capacitor/Capacitor/CAPInstanceDescriptor.m +++ b/ios/Capacitor/Capacitor/CAPInstanceDescriptor.m @@ -38,6 +38,7 @@ - (void)_setDefaultsWithAppLocation:(NSURL*)location { _enableLogging = YES; _enableScrolling = YES; _allowLinkPreviews = YES; + _handleApplicationNotifications = YES; _contentInsetAdjustmentBehavior = UIScrollViewContentInsetAdjustmentNever; _appLocation = location; _cordovaConfiguration = [[CDVConfigParser alloc] init]; diff --git a/ios/Capacitor/Capacitor/CapacitorBridge.swift b/ios/Capacitor/Capacitor/CapacitorBridge.swift index af346bfb5f..b926913260 100644 --- a/ios/Capacitor/Capacitor/CapacitorBridge.swift +++ b/ios/Capacitor/Capacitor/CapacitorBridge.swift @@ -16,6 +16,8 @@ internal class CapacitorBridge: NSObject, CAPBridgeProtocol { return bridgeDelegate?.bridgedWebView } + public var notificationRouter: NotificationRouter + public var isSimEnvironment: Bool { #if targetEnvironment(simulator) return true @@ -172,6 +174,8 @@ internal class CapacitorBridge: NSObject, CAPBridgeProtocol { self.messageHandlerWrapper = messageHandlerWrapper self.config = configuration self.cordovaParser = cordovaConfiguration + self.notificationRouter = NotificationRouter() + self.notificationRouter.handleApplicationNotifications = configuration.handleApplicationNotifications super.init() diff --git a/ios/Capacitor/Capacitor/NotificationHandlerProtocol.swift b/ios/Capacitor/Capacitor/NotificationHandlerProtocol.swift new file mode 100644 index 0000000000..2b559bd811 --- /dev/null +++ b/ios/Capacitor/Capacitor/NotificationHandlerProtocol.swift @@ -0,0 +1,6 @@ +import Foundation + +@objc(CAPNotificationHandlerProtocol) public protocol NotificationHandlerProtocol { + func willPresent(notification: UNNotification) -> UNNotificationPresentationOptions + func didReceive(response: UNNotificationResponse) +} diff --git a/ios/Capacitor/Capacitor/NotificationRouter.swift b/ios/Capacitor/Capacitor/NotificationRouter.swift new file mode 100644 index 0000000000..99fcbb2550 --- /dev/null +++ b/ios/Capacitor/Capacitor/NotificationRouter.swift @@ -0,0 +1,60 @@ +import Foundation + +@objc(CAPNotificationRouter) public class NotificationRouter: NSObject, UNUserNotificationCenterDelegate { + var handleApplicationNotifications: Bool { + get { + return UNUserNotificationCenter.current().delegate === self + } + set { + let center = UNUserNotificationCenter.current() + + if newValue { + center.delegate = self + } else if center.delegate === self { + center.delegate = nil + } + } + } + + public weak var pushNotificationHandler: NotificationHandlerProtocol? { + didSet { + if pushNotificationHandler != nil, oldValue != nil { + CAPLog.print("Push notification handler overriding previous instance: \(String(describing: type(of: oldValue)))") + } + } + } + + public weak var localNotificationHandler: NotificationHandlerProtocol? { + didSet { + if localNotificationHandler != nil, oldValue != nil { + CAPLog.print("Local notification handler overriding previous instance: \(String(describing: type(of: oldValue)))") + } + } + } + + public func userNotificationCenter(_ center: UNUserNotificationCenter, + willPresent notification: UNNotification, + withCompletionHandler completionHandler: @escaping (UNNotificationPresentationOptions) -> Void) { + let presentationOptions: UNNotificationPresentationOptions? + + if notification.request.trigger?.isKind(of: UNPushNotificationTrigger.self) == true { + presentationOptions = pushNotificationHandler?.willPresent(notification: notification) + } else { + presentationOptions = localNotificationHandler?.willPresent(notification: notification) + } + + completionHandler(presentationOptions ?? []) + } + + public func userNotificationCenter(_ center: UNUserNotificationCenter, + didReceive response: UNNotificationResponse, + withCompletionHandler completionHandler: @escaping () -> Void) { + if response.notification.request.trigger?.isKind(of: UNPushNotificationTrigger.self) == true { + pushNotificationHandler?.didReceive(response: response) + } else { + localNotificationHandler?.didReceive(response: response) + } + + completionHandler() + } +}