Cannot find protocol declaration for 'UNUserNotificationCenterDelegate'

1.8k views Asked by At

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"
1

There are 1 answers

3
Krøllebølle On BEST ANSWER

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.