I'm developing native platform Flutter plugin and I'm trying to get push notifications (through Firebase messaging) working with it but didReceiveRemoteNotification in the plugin never gets called.
I've added addApplicationDelegate to my AppDelegate and I'm getting didReceiveRemoteNotification triggered in AppDelegate but not in the plugin. However I do get didRegisterForRemoteNotificationsWithDeviceToken callbacks in both AppDelegate and plugin. I'm using Firebase messaging which might be messing up with this so I'm wondering if anybody tried this or might know what is causing callback not being called.
Plugin:
public class SomePlugin: NSObject, FlutterPlugin {
public static func register(with registrar: FlutterPluginRegistrar) {
...
// Add application delegate so that we can receive AppDelegate callbacks
registrar.addApplicationDelegate(instance)
}
}
extension SomePlugin {
public func application(_ application: UIApplication, didRegisterForRemoteNotificationsWithDeviceToken deviceToken: Data) {
NSLog("****/ didRegisterForRemoteNotificationsWithDeviceToken")
}
public func application(_ application: UIApplication, didReceive notification: UILocalNotification) {
NSLog("****/ didReceiveRemoteNotification")
}
public func application(_ application: UIApplication, didReceiveRemoteNotification userInfo: [AnyHashable : Any], fetchCompletionHandler completionHandler: #escaping (UIBackgroundFetchResult) -> Void) -> Bool {
NSLog("****/ didReceiveRemoteNotification")
return true
}
}
AppDelegate:
#UIApplicationMain
#objc class AppDelegate: FlutterAppDelegate {
override func application(_ application: UIApplication,
didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
let center = UNUserNotificationCenter.current()
center.delegate = self as UNUserNotificationCenterDelegate
// set the type as sound or badge
center.requestAuthorization(options: [.sound,.alert,.badge, .providesAppNotificationSettings]) { (granted, error) in
// Enable or disable features based on authorization
}
application.registerForRemoteNotifications()
GeneratedPluginRegistrant.register(with: self)
return super.application(application, didFinishLaunchingWithOptions: launchOptions)
}
extension AppDelegate {
override func application(_ application: UIApplication, didRegisterForRemoteNotificationsWithDeviceToken deviceToken: Data) {
NSLog("**** didRegisterForRemoteNotificationsWithDeviceToken")
super.application(application, didRegisterForRemoteNotificationsWithDeviceToken: deviceToken)
}
override func application(_ application: UIApplication,
didReceiveRemoteNotification userInfo: [AnyHashable: Any],
fetchCompletionHandler completionHandler: #escaping (UIBackgroundFetchResult) -> Void) {
completionHandler(UIBackgroundFetchResult.newData)
super.application(application, didReceiveRemoteNotification: userInfo, fetchCompletionHandler: completionHandler)
}
}
extension AppDelegate {
// when user opens the notification
override func userNotificationCenter(_ center: UNUserNotificationCenter,
didReceive response: UNNotificationResponse,
withCompletionHandler completionHandler: #escaping () -> Void) {
NSLog("**** userNotificationCenter: did receive!!!!")
completionHandler()
super.userNotificationCenter(center, didReceive: response, withCompletionHandler: completionHandler)
}
// app is in foreground
override func userNotificationCenter(_ center: UNUserNotificationCenter,
willPresent notification: UNNotification,
withCompletionHandler completionHandler: #escaping (UNNotificationPresentationOptions) -> Void) {
NSLog("**** userNotificationCenter: will present")
completionHandler(UNNotificationPresentationOptions.sound)
super.userNotificationCenter(center, willPresent: notification, withCompletionHandler: completionHandler)
}
}
Related
I m receive Fcm Push Notification in the foreground only.app not receive a notification while the app in the background and terminate.
Swift Code
AppDelegate.swift
import UIKit
import Flutter
import GoogleMaps
import Firebase
import FirebaseMessaging
import FirebaseInstanceID
#UIApplicationMain
#objc class AppDelegate: FlutterAppDelegate {
/// didFinishLaunchingWithOptions
override func application(
_ application: UIApplication,
didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?
) -> Bool {
FirebaseApp.configure()
if #available(iOS 10.0, *) {
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)
}
let remoteNotif = (launchOptions?[UIApplication.LaunchOptionsKey.remoteNotification] as? [NSObject : AnyObject])
if remoteNotif != nil {
self.application(application, didReceiveRemoteNotification: remoteNotif!)
UIApplication.shared.applicationIconBadgeNumber = 0
self.window?.makeKeyAndVisible()
return true
}
GMSServices.provideAPIKey("")
GeneratedPluginRegistrant.register(with: self) //ragister plugin
application.registerForRemoteNotifications() //register remoteNotifications
return super.application(application, didFinishLaunchingWithOptions: launchOptions)
}
/// didRegisterForRemoteNotificationsWithDeviceToken
override func application(_ application: UIApplication, didRegisterForRemoteNotificationsWithDeviceToken deviceToken: Data) {
Auth.auth().setAPNSToken(deviceToken, type: .unknown)
Messaging.messaging().apnsToken = deviceToken as Data
}
/// didReceiveRemoteNotification
override func application(_ application: UIApplication,
didReceiveRemoteNotification notification: [AnyHashable : Any],
fetchCompletionHandler completionHandler: #escaping (UIBackgroundFetchResult) -> Void) {
if Auth.auth().canHandleNotification(notification) {
completionHandler(.noData)
return
}
print(notification)
}
override func application(_ application: UIApplication, didFailToRegisterForRemoteNotificationsWithError error: Error) {
print("Unable to register for remote notifications: \(error.localizedDescription)")
}
override func application(_ application: UIApplication, didReceiveRemoteNotification userInfo: [AnyHashable: Any]){
print("Murtuza")
}
override func application(_ application: UIApplication, open url: URL, options: [UIApplication.OpenURLOptionsKey : Any]) -> Bool {
if Auth.auth().canHandle(url) {
return true
}
return false;
}
// MARK: - UNUserNotificationCenterDelegate Method
override func userNotificationCenter(_ center: UNUserNotificationCenter, willPresent notification: UNNotification, withCompletionHandler completionHandler: #escaping (UNNotificationPresentationOptions) -> Void) {
print(notification)
completionHandler([.alert])
}
override func userNotificationCenter(_ center: UNUserNotificationCenter, didReceive response: UNNotificationResponse, withCompletionHandler completionHandler: #escaping () -> Void) {
print(response)
completionHandler()
}
}
Flutter Code
_initFirebaseMessaging() {
_firebaseMessaging.configure(
onMessage: (Map<String, dynamic> message) {
print('AppPushs onMessage : $message');
_showNotification(message);
return;
},
onBackgroundMessage: Platform.isIOS ? myBackgroundMessageHandler : myBackgroundMessageHandler,
onResume: (Map<String, dynamic> message) {
print('AppPushs onResume : $message');
if (Platform.isIOS) {
_showNotification(message);
}
return;
},
onLaunch: (Map<String, dynamic> message) {
print('AppPushs onLaunch : $message');
return;
},
);
_firebaseMessaging.requestNotificationPermissions(
const IosNotificationSettings(sound: true, badge: true, alert: true));
_firebaseMessaging.onIosSettingsRegistered.listen((IosNotificationSettings settings) {
print("Settings registered: $settings");
});
}
How can I solve this?
Question.
https://github.com/FirebaseExtended/flutterfire/issues/2854#issuecomment-704922039
Very good news, FCM rework is coming soon https://twitter.com/mikediarmid/status/1319298281290203139?s=20
I am trying to put iOS push notifications onto my app. But keep getting the error
The object <FancyDeliveryManager.ViewController: 0x101910170> does not respond to -messaging:didReceiveRegistrationToken:. Please implement -messaging:didReceiveRegistrationToken: to be provided with an FCM token.
My app delegate looks like:
class AppDelegate: UIResponder, UIApplicationDelegate, UNUserNotificationCenterDelegate, MessagingDelegate {
var window : UIWindow?
//private let pushNotificationHandler: PushNotificationHandler = PushNotificationHandler()
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
FirebaseApp.configure()
//notification delegates
Messaging.messaging().delegate = self //enables recievieving of tokens
UNUserNotificationCenter.current().delegate = self
///
//set up nav controller
let root: UIViewController = ViewController()
let navigationController = UINavigationController(rootViewController: root)
window = UIWindow(frame: UIScreen.main.bounds)
window?.rootViewController = navigationController
window?.makeKeyAndVisible()
return true
}
//MARK: Push Handling
func registerForPushNotifications() {
UNUserNotificationCenter.current()
.requestAuthorization(options: [.alert, .sound, .badge]) {
[weak self] granted, error in
guard granted else { return }
self?.getNotificationSettings()
}
}
//check the notification settings
func getNotificationSettings() {
UNUserNotificationCenter.current().getNotificationSettings { settings in
guard settings.authorizationStatus == .authorized else { return }
DispatchQueue.main.async {
UIApplication.shared.registerForRemoteNotifications()
}
}
}
// This method will be called when app received push notifications in foreground
func userNotificationCenter(_ center: UNUserNotificationCenter, willPresent notification: UNNotification, withCompletionHandler completionHandler: #escaping (UNNotificationPresentationOptions) -> Void)
{
completionHandler([.alert, .badge, .sound])
}
//called when user interacts with push notification
func userNotificationCenter(_ center: UNUserNotificationCenter, didReceive response: UNNotificationResponse, withCompletionHandler completionHandler: #escaping () -> Void) {
print("Launched from push notification")
completionHandler()
}
// This callback is fired at each app startup and whenever a new token is created.
func messaging(_ messaging: Messaging, didReceiveRegistrationToken fcmToken: String) {
print("Firebase registration token: \(fcmToken)")
saveFcmToken(token: fcmToken)
}
//save token in database
func saveFcmToken(token: String){
var ref: DatabaseReference!
ref = Database.database().reference()
guard let userID = Auth.currentUser else { return }
ref.child("WarehouseManagers/\(userID)/fcmToken").setValue(token)
}
func application(_ application: UIApplication, didReceiveRemoteNotification userInfo: [AnyHashable: Any]) {
//default to delegate method below
}
func application(_ application: UIApplication, didReceiveRemoteNotification userInfo: [AnyHashable: Any],
fetchCompletionHandler completionHandler: #escaping (UIBackgroundFetchResult) -> Void) {
completionHandler(UIBackgroundFetchResult.newData)
}
// [END receive_message]
func application(_ application: UIApplication, didFailToRegisterForRemoteNotificationsWithError error: Error) {
print("Unable to register for remote notifications: \(error.localizedDescription)")
}
func application(_ application: UIApplication, didRegisterForRemoteNotificationsWithDeviceToken deviceToken: Data) {
print("APNs token retrieved: \(deviceToken)")
// With swizzling disabled you must set the APNs token here.
// Messaging.messaging().apnsToken = deviceToken
}
func applicationDidBecomeActive(_ application: UIApplication) {
UIApplication.shared.applicationIconBadgeNumber = 0
}
Is there something in the main ViewController that needs to be added here?
Any help would be greatly appreciated.
This is a part of my code. I want to use UNUserNotificationCenter and UNUserNotificationCenterDelegate to handle notification events.
This code catches the notification event when the app is in a foreground state. But "didReceive" is not fired for a background state.
func application(_ application: UIApplication,
didFinishLaunchingWithOptions launchOptions:
[UIApplication.LaunchOptionsKey : Any]? = nil) -> Bool {
UNUserNotificationCenter.current().delegate = self
application.registerForRemoteNotifications()
return true
}
func userNotificationCenter(_ center: UNUserNotificationCenter,
willPresent notification: UNNotification,
withCompletionHandler completionHandler: #escaping (UNNotificationPresentationOptions) -> Void)
{
print("willPresent") /// This works
completionHandler([.alert, .badge, .sound])
}
func userNotificationCenter(_ center: UNUserNotificationCenter,
didReceive response: UNNotificationResponse,
withCompletionHandler completionHandler: #escaping () -> Void) {
print("didReceive") /// This doesn't work
completionHandler()
}
But if I don't use UNUserNotificationCenter.current().delegate = self the delegate method is correctly fired in the background.
func application(_ application: UIApplication,
didReceiveRemoteNotification userInfo: [AnyHashable: Any],
fetchCompletionHandler completionHandler: #escaping FetchCompletionHandler) {
print("didReceiveRemoteNotification... \(userInfo)")
}
How can I use "didReceive"? I want to handle the notification in the background.
application(_:didReceiveRemoteNotification:fetchCompletionHandler:) is called when the application receives a silent push notification. Silent push notifications can be delivered in when the application is in the background state, iOS wakes the application to perform background processing invisible to the user.
A silent push notification has the content-available flag set to 1.
Silent push notifications should not include an alert, badge, or sound. Silent push is not meant to be visible to the user, it is only a hint to the application that new remote content is available.
Removing the content-available flag from your push notification payload will cause iOS to handle it as a regular notification. The user notification center delegate methods will be called instead of application(_:didReceiveRemoteNotification:fetchCompletionHandler:) but your application will be unable to do background processing triggered by the notification.
You can validate the content of your push notification payload for problems like this using this tool
This is how I handle push notification in my app. I think you need to implement all of these methods to handle all iOS versions in the foreground and background mode.
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool {
if #available(iOS 10.0, *) {
let center = UNUserNotificationCenter.current()
center.delegate = self
center.requestAuthorization(options: [.sound,.alert,.badge], completionHandler: { (granted, error) in
if error == nil {
DispatchQueue.main.async {
application.registerForRemoteNotifications()
}
}
})
}
else {
let notificationTypes: UIUserNotificationType = [UIUserNotificationType.alert, UIUserNotificationType.badge, UIUserNotificationType.sound]
let pushNotificationSettings = UIUserNotificationSettings(types: notificationTypes, categories: nil)
application.registerUserNotificationSettings(pushNotificationSettings)
application.registerForRemoteNotifications()
}
}
func application(_ application: UIApplication, didRegisterForRemoteNotificationsWithDeviceToken deviceToken: Data) {
let deviceTokenString = deviceToken.reduce("", {$0 + String(format: "%02X", $1)})
print(deviceTokenString)
}
func application(_ application: UIApplication, didFailToRegisterForRemoteNotificationsWithError error: Error) {
print("Failed to get token; error: \(error)")
}
#available(iOS 10.0, *)
func userNotificationCenter(_ center: UNUserNotificationCenter, willPresent notification: UNNotification, withCompletionHandler completionHandler: #escaping (UNNotificationPresentationOptions) -> Void) {
completionHandler([.alert,.sound])
//do sth
}
#available(iOS 10.0, *)
func userNotificationCenter(_ center: UNUserNotificationCenter, didReceive response: UNNotificationResponse, withCompletionHandler completionHandler: #escaping () -> Void) {
//do sth
}
// for iOS < 10
func application(_ application: UIApplication, didReceive notification: UILocalNotification) {
}
func application(_ application: UIApplication, didReceiveRemoteNotification userInfo: [AnyHashable : Any], fetchCompletionHandler completionHandler: #escaping (UIBackgroundFetchResult) -> Void) {
}
There are methods of UNUserNotificationCenterDelegate:
willPresent: This method gets called when you receive a notification when your app is in the foreground. If the app is in the background then this method will not calls.
didRecieve: This method gets called when a user clicked on the notification.
In case of background state, only 'didRecieve' will be called when user will click on a notification.
I've used apple push notification service in my app and I received certificates and it works well But now my problem is that when I use Firebase rest API for sending message as notification I won’t receive any notification in my iPhone until I run the app But when I use Firebase it will be working, well here is my codes:
import UIKit
import Firebase
import FirebaseMessaging
import FirebaseInstanceID
import UserNotifications
#UIApplicationMain
class AppDelegate: UIResponder, UIApplicationDelegate {
var window: UIWindow?
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool {
NotificationCenter.default.addObserver(self, selector: #selector(self.refreshToken(notification:)), name: NSNotification.Name.InstanceIDTokenRefresh, object: nil)
Messaging.messaging().isAutoInitEnabled = true
FirebaseApp.configure()
if #available(iOS 10.0, *) {
UNUserNotificationCenter.current().requestAuthorization(options: [.alert , .badge , .sound]) { (success, error) in
}
} else {
UIApplication.shared.registerUserNotificationSettings(UIUserNotificationSettings(types: [.badge, .sound, .alert], categories: nil))
UIApplication.shared.registerForRemoteNotifications()
// Fallback on earlier versions
}
application.registerForRemoteNotifications()
return true
}
func application(_ application: UIApplication, didRegisterForRemoteNotificationsWithDeviceToken deviceToken: Data) {
#if PROD_BUILD
FIRInstanceID.instanceID().setAPNSToken(deviceToken, type: .prod)
#else
InstanceID.instanceID().setAPNSToken(deviceToken, type: .sandbox)
#endif
Messaging.messaging().subscribe(toTopic: "global")
}
func application(_ application: UIApplication, didReceiveRemoteNotification userInfo: [AnyHashable: Any]) {
Messaging.messaging().subscribe(toTopic: "global")
print(userInfo)
}
func application(_ application: UIApplication, didReceiveRemoteNotification userInfo: [AnyHashable: Any], fetchCompletionHandler completionHandler: #escaping (UIBackgroundFetchResult) -> Void) {
Messaging.messaging().appDidReceiveMessage(userInfo)
if Messaging.messaging().fcmToken != nil {
Messaging.messaging().subscribe(toTopic: "global")
}
// Print full message.
print(userInfo)
completionHandler(UIBackgroundFetchResult.newData)
}
func applicationDidEnterBackground(_ application: UIApplication) {
Messaging.messaging().shouldEstablishDirectChannel = false
}
func applicationDidBecomeActive(_ application: UIApplication) {
FBHandler()
}
#objc func refreshToken(notification : NSNotification) {
let refreshToken = InstanceID.instanceID().token()!
print("***\(refreshToken)")
FBHandler()
}
func FBHandler() {
Messaging.messaging().shouldEstablishDirectChannel = true
}
}
For APNS to work in background you need to enable background modes in capabilities section and tick remote notification.
Once you done add/check this delegate method in AppDelegate class
func application(_ application: UIApplication, didReceiveRemoteNotification userInfo: [AnyHashable : Any]) {
print("APNS USER INFO: \(userInfo)")
}
This method fires when app is in background and APNS comes.
More Info: How to handle push notification in background in ios 10?
UPDATE:
Add this one also and let see if it works
func userNotificationCenter(_ center: UNUserNotificationCenter,
didReceive response: UNNotificationResponse,
withCompletionHandler completionHandler: #escaping () -> Void) {
print("APNS: \(response.notification.request.content.userInfo)")
}
import UIKit
import UserNotifications
class SLPushNotificationManager: NSObject, UIApplicationDelegate {
class var currentManager : SLPushNotificationManager {
struct Static {
static let instance : SLPushNotificationManager = SLPushNotificationManager()
}
return Static.instance
}
override init() {
super.init()
NSNotificationCenter.defaultCenter().addObserverForName(UserLoggedInNotification, object: nil, queue: NSOperationQueue.mainQueue()) {[unowned self] (notification) -> Void in
self.registerForRemoteNotifications()
}
}
func registerForRemoteNotifications(){
if #available(iOS 10, *) {
let center = UNUserNotificationCenter.currentNotificationCenter()
center.requestAuthorizationWithOptions([.Alert, .Badge, .Sound], completionHandler: { (granted, error) in
if error == nil {
UIApplication.sharedApplication().registerForRemoteNotifications()
}
})
}else {
let notificationType: UIUserNotificationType = [UIUserNotificationType.Alert, UIUserNotificationType.Badge, UIUserNotificationType.Sound]
let settings = UIUserNotificationSettings(forTypes: notificationType, categories: nil)
UIApplication.sharedApplication().registerUserNotificationSettings(settings)
}// end else
}// end func
In the app delegate:
func application(application: UIApplication, didFinishLaunchingWithOptions launchOptions: [NSObject: AnyObject]?) -> Bool {
SLPushNotificationManager.currentManager //init push notification manager
}
func application(application: UIApplication, didRegisterUserNotificationSettings notificationSettings: UIUserNotificationSettings) {
application.registerForRemoteNotifications()
}
func application(application: UIApplication, didReceiveRemoteNotification userInfo: [NSObject : AnyObject]) {
SLPushNotificationManager.currentManager.handleNotification(userInfo)
}
func application(application: UIApplication, didRegisterForRemoteNotificationsWithDeviceToken deviceToken: NSData) {
SLPushNotificationManager.currentManager.registerDeviceToken(deviceToken)
}
func application(application: UIApplication, didFailToRegisterForRemoteNotificationsWithError error: NSError) {
print("Error: \(error.localizedDescription)", terminator: "")
}
#available(iOS 10.0, *)
func userNotificationCenter(center: UNUserNotificationCenter, willPresentNotification notification: UNNotification, withCompletionHandler completionHandler: (UNNotificationPresentationOptions) -> Void){
completionHandler(.Alert)
}
#available(iOS 10.0, *)
func userNotificationCenter(center: UNUserNotificationCenter, didReceiveNotificationResponse response: UNNotificationResponse, withCompletionHandler completionHandler: () -> Void){
print(response.notification.request.content.userInfo)
}
Now for the first time after downloading the app the user can register for APN! and the methode: func application(application: UIApplication, didRegisterForRemoteNotificationsWithDeviceToken deviceToken: NSData)
is called but after I unregistere for APN later then I try to register again the method: func application(application: UIApplication, didRegisterForRemoteNotificationsWithDeviceToken deviceToken: NSData)
is not called again... Any Ideas y this happens??
Why are you deregistering? Have you read what the documentation says about this:
You should call this method in rare circumstances only, such as when a
new version of the app removes support for all types of remote
notifications