1

I have a an iOS app using Firebase for notifications. Notifications are set up and working, and I now need to receive/handle the notifications to present view controllers accordingly. I use Objective C code to call my C++ code, and therefore I have a bridging header in my project, which is mainly written in Swift.

I have used this example from the firebase documentation (as well as other examples). In short terms: conform to the protocol UNUserNotificationCenterDelegate and implement its functions. I also do import UserNotifications in my AppDelegate class.

Now, when compiling with these few changes I get these two errors

Cannot find protocol declaration for 'UNUserNotificationCenterDelegate'

Unknown type name 'UNNotificationPresentationOptions'

for the generated code:

SWIFT_AVAILABILITY(ios,introduced=10)
@interface AppDelegate (SWIFT_EXTENSION(myapp)) <UNUserNotificationCenterDelegate>
- (void)userNotificationCenter:(UNUserNotificationCenter * _Nonnull)center willPresentNotification:(UNNotification * _Nonnull)notification withCompletionHandler:(void (^ _Nonnull)(UNNotificationPresentationOptions))completionHandler;
- (void)userNotificationCenter:(UNUserNotificationCenter * _Nonnull)center didReceiveNotificationResponse:(UNNotificationResponse * _Nonnull)response withCompletionHandler:(void (^ _Nonnull)(void))completionHandler;
@end

--- Update

After some trial and error it seems that commenting out all calls from objC to Swift as well as all usage of Swift types declared as @objc makes my code compile, and the bridging header does not complain anymore. This also includes commenting out #import "myapp-Swift.h" in all my Objective C code (which is probably why the bridging header does not complain anymore). Unfortunately it is not feasible to stop using the Swift types in ObjC, as it would require quite some rewrite for a seemingly small change.

I guess this could somewhat indicate the origin of the issue, though I am still no sure why or how this affects the UNUserNotificationCenterDelegate protocol.

--- End Update

Other things to note:

  • The error originates from an Objective C++ file where I import the generated myapp-Swift.h.
  • I tried adding #import <UserNotifications/UNUserNotificationCenter.h> to my bridging header. Xcode does not complain about the include, so it does find it, but it does not help. Doing #import <UserNotifications/UserNotifications.h> does not work either.
  • I tried conforming to UNUserNotificationCenterDelegate both in the AppDelegate itself and as an extension. The errors are the same in both cases.
  • The bridging header is included in my .mm files and the errors originate from there.
  • UserNotifications.framework is in Frameworks, Libraries and Embedded Content.
  • UserNotifications.framework is in Link Binary With Libraries.
  • I tried changing the deployment target to newer versions without luck.
  • I looked at this question which is basically the same as this one, but nothing works. It points further to
  • this question, but that is not my use case.

Here is my git diff for reference:

diff --git a/ios/myapp.xcodeproj/project.pbxproj b/ios/myapp.xcodeproj/project.pbxproj
index 1ac676e..ca3a814 100644
--- a/ios/myapp.xcodeproj/project.pbxproj
+++ b/ios/myapp.xcodeproj/project.pbxproj
@@ -1550,7 +1550,7 @@
                GCC_WARN_UNUSED_FUNCTION = YES;
                GCC_WARN_UNUSED_VARIABLE = YES;
                HEADER_SEARCH_PATHS = "";
-               IPHONEOS_DEPLOYMENT_TARGET = 9.3;
+               IPHONEOS_DEPLOYMENT_TARGET = 10.0;
                MTL_ENABLE_DEBUG_INFO = YES;
                ONLY_ACTIVE_ARCH = YES;
                SDKROOT = iphoneos;
@@ -1601,7 +1601,7 @@
                GCC_WARN_UNUSED_FUNCTION = YES;
                GCC_WARN_UNUSED_VARIABLE = YES;
                HEADER_SEARCH_PATHS = "";
-               IPHONEOS_DEPLOYMENT_TARGET = 9.3;
+               IPHONEOS_DEPLOYMENT_TARGET = 10.0;
                MTL_ENABLE_DEBUG_INFO = NO;
                SDKROOT = iphoneos;
                VALIDATE_PRODUCT = YES;
diff --git a/ios/myapp/AppDelegate.swift b/ios/myapp/AppDelegate.swift
index a1c9543..1010f99 100644
--- a/ios/myapp/AppDelegate.swift
+++ b/ios/myapp/AppDelegate.swift
@@ -7,6 +7,7 @@
 //
 
 import UIKit
+import UserNotifications
 import Firebase
 
 @UIApplicationMain
@@ -21,6 +22,21 @@ class AppDelegate: UIResponder, UIApplicationDelegate
         FirebaseInterface.initialize()

         ShoppingListInterface.loadLastSavedShoppingList()
+
+        if #available(iOS 10.0, *) {
+            // For iOS 10 display notification (sent via APNS)
+            UNUserNotificationCenter.current().delegate = self
+
+            let authOptions: UNAuthorizationOptions = [.alert, .badge, .sound]
+            UNUserNotificationCenter.current().requestAuthorization(
+                options: authOptions,
+                completionHandler: {_, _ in })
+        } else {
+            let settings: UIUserNotificationSettings =
+            UIUserNotificationSettings(types: [.alert, .badge, .sound], categories: nil)
+            application.registerUserNotificationSettings(settings)
+        }
+
         return true
     }
 
@@ -73,3 +91,46 @@ class AppDelegate: UIResponder, UIApplicationDelegate
         // TODO: Save ShoppingList
     }
 }
+
+@available(iOS 10, *)
+extension AppDelegate : UNUserNotificationCenterDelegate
+{
+
+  // Receive displayed notifications for iOS 10 devices.
+  func userNotificationCenter(_ center: UNUserNotificationCenter,
+                              willPresent notification: UNNotification,
+    withCompletionHandler completionHandler: @escaping (UNNotificationPresentationOptions) -> Void) {
+    let userInfo = notification.request.content.userInfo
+
+    // With swizzling disabled you must let Messaging know about the message, for Analytics
+    // Messaging.messaging().appDidReceiveMessage(userInfo)
+    // Print message ID.
+//    if let messageID = userInfo[gcmMessageIDKey] {
+//      print("Message ID: \(messageID)")
+//    }
+
+    // Print full message.
+    print(userInfo)
+
+    // Change this to your preferred presentation option
+    completionHandler([[.alert, .sound]])
+  }
+
+  func userNotificationCenter(_ center: UNUserNotificationCenter,
+                              didReceive response: UNNotificationResponse,
+                              withCompletionHandler completionHandler: @escaping () -> Void) {
+    let userInfo = response.notification.request.content.userInfo
+    // Print message ID.
+//    if let messageID = userInfo[gcmMessageIDKey] {
+//      print("Message ID: \(messageID)")
+//    }
+
+    // With swizzling disabled you must let Messaging know about the message, for Analytics
+    // Messaging.messaging().appDidReceiveMessage(userInfo)
+    // Print full message.
+    print(userInfo)
+
+    completionHandler()
+  }
+}
+// [END ios_10_message_handling]
diff --git a/ios/myapp/myapp-Bridging-Header.h b/ios/myapp/myapp-Bridging-Header.h
index 1b2d4c1..4973a15 100644
--- a/ios/myapp/myapp-Bridging-Header.h
+++ b/ios/myapp/myapp-Bridging-Header.h
@@ -11,6 +11,7 @@
     myapp-Swift.h, remember to do #import <UIKit/UIKit.h> in the Objective C
     header file.
  */
+#import <UserNotifications/UNUserNotificationCenter.h>
 #import "ShoppingListWrapper.h"
 #import "DBWrapper.h"
 #import "FirebaseWrapper.h"
Krøllebølle
  • 2,878
  • 6
  • 54
  • 79
  • From what I understand, `AppDelegate.swift' is swift and conforming to `UNUserNotificationCenterDelegate`, so there's no need for `#import ` in `myapp-Bridging-Header.h` – Pushpak Narasimhan Sep 28 '20 at 18:45
  • @PushpakNarasimhan This is all true, the include in `myapp-Bridging-Header.h` was merely an attempt I made to see if it changed something. – Krøllebølle Sep 28 '20 at 18:50
  • You may want to try to select optional while linking framework – Pushpak Narasimhan Sep 28 '20 at 18:59
  • Maybe something to do with the poor C++ support for modules. Does it make a difference to move the offending code to Objective C? – Paul Beusterien Sep 29 '20 at 16:47
  • I did some more digging (see update in the question). Commenting out all calls from Objective C to Swift, as well as all usage of Swift types marked `@objc` in Objective C, makes the code compile successfully. This also includes commenting out `#import "myapp-Swift.h"` in all Objective C code. I am unfortunately not quite sure what this fact tells me. – Krøllebølle Sep 30 '20 at 18:51

1 Answers1

5

After digging for some time I found the source of the error. Simply doing

#import <UserNotifications/UserNotifications.h> // Added this
#import "myapp-Swift.h"

makes the code compile. The reason for this originates from confusion about what the bridging header does. I believed that the bridging header was both for calling from Objective C to Swift and vice versa. This is not the case.

  • myapp-Bridging-Header.h exposes Objective C code to Swift to be able to call from Swift to Objective C.
  • myapp-Swift.h is autogenerated and exposes any Swift code marked with @objc to Objective C, to be able to call from Objective C to Swift.

Since the UNUserNotificationCenterDelegate protocol is of type NSObjectProtocol, the protocol declaration is in Objective C. Thus, the generated myapp-Swift.h does not know about it unless #import <UserNotifications/UserNotifications.h> is done first. Doing import UserNotifications in Swift is not even needed in this case.

Krøllebølle
  • 2,878
  • 6
  • 54
  • 79
  • First off, +1 for explicitly specifying the right terms. Thank you. In many answers I've checked reg this, they always use the word bridging header for both cases. That's wrong and the terminology Apple follows is: Swift interface header for exposing swift class, methods to ObjC and bridging header for exposing ObjC class to swift. – NightFuryLxD Apr 18 '23 at 09:02
  • Second, why import UserNotifications.h in the .mm file (where I've imported my swift interface header)? That .mm file has nothing to do with UserNotifications code and I'm not comfortable including it (since it just causes confusion). Why doesn't the swift interface header internally include it since that's the exact place where the problem originates (there's a reference to UserNotifications in the swift interface header file). I think we're missing something here, which causes the swift interface header file to not to include UserNotifications.h. – NightFuryLxD Apr 18 '23 at 09:09
  • It makes no sense to explicitly include UserNotifications.h (before including swift interface header) in a .mm file which has nothing to do with UserNotifications. – NightFuryLxD Apr 18 '23 at 09:11