How to get the Push Notifications displayed in the Notification center - swift

All of these came from just one app I'm currently developing, let's call this app SampleApp
How do I get a list of these notifications from my Phone's Tray
Yes I do know that I may be able to get the notifications via this code
if #available(iOS 10.0, *) {
UNUserNotificationCenter.current().getPendingNotificationRequests { (requests) in
print("here are the iOS 10 notifs \(requests)")
}
} else {
let requests = (UIApplication.shared.scheduledLocalNotifications ?? [])
print("here are the NON iOS 10 notifs \(requests)")
}
But the problem with this code is that I can only get notifications which I created offline, and not those coming from the Apple Push Notification Server (APNS)
By created offline I mean, scheduled UILocalNotifications, and since Push Notifications aren't UILocalNotifications I have no idea what to do
Some Questions You May ask
Are these notifications from my app?
Yes
Am I using the didReceiveRemoteNotification on the AppDelegate?
Yes, but that's different.
Does my remote notifications work/come from APNS
Yes.
What does my code for registering remote notifications look like?
if #available(iOS 10.0, *) {
let authOptions: UNAuthorizationOptions = [.alert, .badge, .sound]
let center = UNUserNotificationCenter.current()
center.delegate = self
center.requestAuthorization(options: authOptions, completionHandler: { (_, _) in })
} else {
let settings = UIUserNotificationSettings(types: [.alert, .badge, .sound], categories: nil)
application.registerUserNotificationSettings(settings)
}
application.registerForRemoteNotifications()

Dávid Pásztor's comment partially solved the issue because now I can retrieve the all (push and non push) Notifications displayed in the Notification Center, but only for iOS 10 because the code below is only for iOS 10 devices and above, what about other versions?
UNUserNotificationCenter.current().getDeliveredNotifications { (notifications) in
print(notifications)
}

Related

Swift didReceiveRemoteNotification() Firebase not being called

After reading about this extensively, I cannot find the solution. I am sending cloud messages from Firebase to my ios app, but the method didReceiveRemoteNotification() is not being called at all, not when the app is in foreground and not in background. I tried sending from the firebase console, and also from postman using topics, and both don't work. I have integrated Firebase in the app, and uploaded certificate to Firebase, and I get no errors in the console. Just the notification isn't coming? I also added the capability of cloud messages.
This is what I do (from the Firebase tutorial) in appDelegate:
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
// Override point for customization after application launch.
FirebaseApp.configure()
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)
}
application.registerForRemoteNotifications()
Messaging.messaging().delegate = self
return true
}
func application(_ application: UIApplication, didReceiveRemoteNotification userInfo: [AnyHashable: Any]) {
// If you are receiving a notification message while your app is in the background,
// this callback will not be fired till the user taps on the notification launching the application.
// TODO: Handle data of notification
// 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)
}
func application(_ application: UIApplication, didReceiveRemoteNotification userInfo: [AnyHashable: Any],
fetchCompletionHandler completionHandler: #escaping (UIBackgroundFetchResult) -> Void) {
// If you are receiving a notification message while your app is in the background,
// this callback will not be fired till the user taps on the notification launching the application.
// TODO: Handle data of notification
// 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)
completionHandler(UIBackgroundFetchResult.newData)
}
Disable Method swizzling and everything should work fine.
According to the documentation https://firebase.google.com/docs/cloud-messaging/ios/client#token-swizzle-disabled
If you have disabled method swizzling, or you are building a SwiftUI app, you'll need to explicitly map your APNs token to the FCM registration token. Implement the application(_:didRegisterForRemoteNotificationsWithDeviceToken:) method to retrieve the APNs token, and then set Messaging's apnsToken property
It also mentions here https://firebase.google.com/docs/cloud-messaging/ios/receive#handle_messages_with_method_swizzling_disabled
By default, if you assign your app's app delegate class to the UNUserNotificationCenter and Messaging delegate properties, FCM will swizzle your app delegate class to automatically associate your FCM token with the device's APNs token and pass notification-received events to Analytics. If you explicitly disable method swizzling, if you are building a SwiftUI app, or if you use a separate class for either delegate, you will need to perform both of these tasks manually.
Mapping apnToken and deviceToken is to be done at various places thus it's advised to read the documentation completely and apply the necessary changes.

swift notification not triggering

I'm new to notifications in swift and have setup the following in my AppDelegate.swift file. It runs and I get print output that the notifications are setup and do not error. I also get the permission ask for notifications, which I click yes to. I believe the interval notification I have should trigger in 20 seconds after launch. It does not trigger, this doesn't change if I'm in the app or have minimized the app. Note I am running in Simulator mode from XCODE. Why is my notification not triggering?
func setupNotifications(){
let notificationCenter = UNUserNotificationCenter.current()
notificationCenter.requestAuthorization(options: [.alert, .badge, .sound]) { (granted, error) in
if granted {
print("Yay!")
} else {
print("D'oh")
}
}
notificationCenter.removeAllPendingNotificationRequests()
print("setting up notifications")
let content = UNMutableNotificationContent()
content.title = "Answer your daily questions"
content.body = "Answer your daily questions please"
content.categoryIdentifier = "alarm"
content.userInfo = ["customData": "fizzbuzz"]
content.sound = UNNotificationSound.default
//let trigger = UNTimeIntervalNotificationTrigger(timeInterval: (1*60), repeats: true)
let trigger = UNTimeIntervalNotificationTrigger(timeInterval: 20, repeats: false)
// Create the request
let request = UNNotificationRequest(identifier: "daily-notifier",
content: content, trigger: trigger)
// Schedule the request with the system.
notificationCenter.add(request) { (error) in
if error != nil {
print("there were errors to address")
// Handle any errors.
}
}
print("done setting up notifications")
}
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
let path = NSSearchPathForDirectoriesInDomains(.applicationSupportDirectory, .userDomainMask, true)
print("\(path)")
// Override point for customization after application launch.
setupNotifications()
return true
}
First note that you should probably wait to receive permission before adding a request for a local notification as per Apple's documentation of the requestAuthorization(options:completionHandler:)
Note: Always call this method before scheduling any local notifications and before registering with the Apple Push Notification service.
Also make sure that you've added Push Notifications as a capability for your app as this will be needed should you submit it.
After I ran the above code I did not receive a notification while the app was still active but I did receive one while the app was in the background. I believe that if a user receives a notification for an app that is in the active state it is given silently; i.e., without the banner and sound (at least this is what I have experienced with my notifications, although there may be more to it).

watchOS, not receiving remote notifications

I am developing app on WatchOS 6 but I cannot receive remote notification
Here is my registration code for push notifications in ExtensionDelegate I am getting valid device token.
extension ExtensionDelegate {
func setupRemoteNotifications() {
UNUserNotificationCenter.current().delegate = self
UNUserNotificationCenter.current().requestAuthorization(options: [.alert, .badge, .sound]) { (granted, error) in
print("[WATCH PUSH NOTIFICATIONS] Permission granted: \(granted)")
guard granted else {
DispatchQueue.main.async {
self.showNotificationsNotGrantedAlert()
return
}
return
}
self.getNotificationSettings()
}
}
private func getNotificationSettings() {
UNUserNotificationCenter.current().getNotificationSettings { settings in
print("[WATCH PUSH NOTIFICATIONS] Notification settings: \(settings)")
guard settings.authorizationStatus == .authorized else { return }
DispatchQueue.main.async {
WKExtension.shared().registerForRemoteNotifications()
self.onRemoteNotificationRegistration()
}
}
}
private func onRemoteNotificationRegistration() { }
func didRegisterForRemoteNotifications(withDeviceToken deviceToken: Data) {
// Convert token to string
let deviceTokenString = deviceToken.map { data in String(format: "%02.2hhx", data) }.joined()
print("[WATCH PUSH NOTIFICACTIONS] Device Token: \(deviceTokenString)")
UserSettings.shared.deviceToken = deviceTokenString
}
func didFailToRegisterForRemoteNotificationsWithError(_ error: Error) {
print("[WATCH PUSH NOTIFICATIONS] Failed to register device: \(error)")
UserSettings.shared.deviceToken = nil
}
func didReceiveRemoteNotification(_ userInfo: [AnyHashable : Any], fetchCompletionHandler completionHandler: #escaping (WKBackgroundFetchResult) -> Void) {
print("[WATCH PUSH NOTIFICATIONS] Push notification received: \(userInfo)")
let aps = userInfo["aps"] as! [String: AnyObject]
completionHandler(.noData)
}
}
extension ExtensionDelegate: UNUserNotificationCenterDelegate {
// show notification also when in foreground
func userNotificationCenter(_ center: UNUserNotificationCenter, willPresent notification: UNNotification, withCompletionHandler completionHandler: #escaping (UNNotificationPresentationOptions) -> Void) {
print("[WATCH PUSH NOTIFICATION] Will present notification...")
let categoryIdentifier = notification.request.content.categoryIdentifier
let category = NotificationCategory(rawValue: categoryIdentifier)
if category == NotificationCategory.NotificationCategory {
} else if category == NotificationCategory.ActivityCategory {
}
completionHandler([.alert, .badge, .sound])
}
// called when tapped onto notification banner
func userNotificationCenter(_ center: UNUserNotificationCenter, didReceive response: UNNotificationResponse, withCompletionHandler completionHandler: #escaping () -> Void) {
print("[WATCH PUSH NOTIFICATION] Did receive notification response")
let userInfo = response.notification.request.content.userInfo as! [String: AnyObject]
let aps = userInfo["aps"] as! [String: AnyObject]
let categoryIdentifier = response.notification.request.content.categoryIdentifier
let category = NotificationCategory(rawValue: categoryIdentifier)
if category == NotificationCategory.NotificationCategory {
} else if category == NotificationCategory.ActivityCategory {
}
handleNotificationAction(response.actionIdentifier)
openNotification(userInfo: userInfo)
completionHandler()
}
}
extension ExtensionDelegate {
private func handleNotificationAction(_ actionIdentifier: String) {
let action = NotificationAction(rawValue: actionIdentifier)
if action == NotificationAction.Call {
print("Action: Call handled!")
} else if action == NotificationAction.Email {
print("Action: Email handled!")
} else if action == NotificationAction.Message {
print("Action: Message handled!")
}
}
private func openNotification(userInfo: [String: AnyObject]) {
// let something = userInfo["something"] ...
}
}
extension ExtensionDelegate {
private func showNotificationsNotGrantedAlert() {
let settingsActionTitle = NSLocalizedString("Settings", comment: "")
let cancelActionTitle = NSLocalizedString("Cancel", comment: "")
let message = NSLocalizedString("You need to grant a permission from notification settings.", comment: "")
let title = NSLocalizedString("Push Notifications Off", comment: "")
let settingsAction = WKAlertAction(title: settingsActionTitle, style: .default) {
print("[WATCH PUSH NOTIFICATIONS] Go to Notification Settings")
}
let cancelAction = WKAlertAction(title: cancelActionTitle, style: .cancel) {
print("[WATCH PUSH NOTIFICATIONS] Cancel to go to Notification Settings")
}
WKExtension.shared().rootInterfaceController?.presentAlert(withTitle: title, message: message, preferredStyle: .alert, actions: [settingsAction, cancelAction])
}
}
I've added Push notification Entitlement in WatchExtensions
I am sending notification from Push Notification Tester app using valid TeamId, P8 cert, P8 key, bundle Id -> where I am getting success.
I am sending default test notification nothing special
{
"aps": {
"alert": {
"title": "Silver Salmon Creek",
"body": "You are within 5 miles of Silver Salmon Creek."
},
"category": "Notification"
}
}
I have similar code for iOS and there notification are delivered correctly on iPhone.
UPDATE
I've tested a little bit more
And I have application added to my iPhone app
but iPhone app is for iOS 13
and Watch app is for WatchOS 6
So as I understand this WatchOS app can be install as standalone application.
I have checked "Support running without iOS App installation"
So as I understand my server should send this notification to both
Watch device Token and iOS device token. And there APNS/iOS/watchOS take the decision if there is duplicated notification where to deliver this notification i.e. to iPhone / watchOS.
If there is only watchOS app I think it will be delivered to watch Device token
If there is both watch/iOS app then it will send to iOS and then device whether user is using iPhone/Watch in given moment and deliver it to currently used device.
So now if I send notification to iPhone device token with iOS bundle identifier, and have locked iPhone and watch on wrist I am getting notification on my WatchOS and even can look at long notifications with dynamic content.
But when I've uninstall iOS version of application, and send notification with iPhone or watch device token and appropriate bundle id iOS/watchOS then for standalone app I am not getting this notification delivered again.
I encountered a similar issue, be sure your p8 Bundle ID is using the explicit WatchkitApp ID and not the Extension's ID.
Once I dropped the .watchkitextension and uninstalled (the app), reinstalled, reregistered the device, it worked.
I found the issue. It was related with the way Push Notification is send from the server using node-apns. I also have old Push Notification Tester app. 3.0 version of node-apns is required. there is additonal Header field send to APNS server
apns-push-type

FCM Token dosent save in Database when user clicks "Allow" on notifications

I developed a app that is in the App Store right now. However before launch I tested the app with my iPhone and the FCM token got registered in the database. However now as users start to use the app, I can see that FCM token dosen't save in the database? I asked my friend to test it out, and he neither gets the FCM token stored in the database.
And as a result of this, notifications are not displayed (because the function dosen't have a phone to send the notification to).
Can anyone figure this out?
(I have tried to resolve this issue for the past days, cant seem to catch where its gone wrong)
Code in my first ViewController, that users arrive to after signing up.
func registerForRemoteNotifications() {
Messaging.messaging().delegate = self
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)
UIApplication.shared.registerUserNotificationSettings(settings)
}
UIApplication.shared.registerForRemoteNotifications()
}
func messaging(_ messaging: Messaging, didReceiveRegistrationToken fcmToken: String) {
if let userUid = Auth.auth().currentUser?.uid {
Database.database().reference().child("profile/" + userUid + "/fcmToken").setValue(fcmToken)
}
}
I noticed that 'FCM token' is only sent to the db when a user is logged in, So the issue is coming when user opens app for first time when he isnt logged in.
Solution is:
func messaging(_ messaging: Messaging, didReceiveRegistrationToken fcmToken: String) {
UserDefaults.standard.setValue("fcmToken", forKey: fcmToken)
}
later on fetch the token from userDefaults
let deviceToken = UserDefaults.standard.value(forKey:"fcmToken") as? String ?? "not found"
to send it to server just after user gets logged in.

Firebase FCM push notifications stopped working iOS 11.1.1

I am using Firebase FCM to send push notifications from an iOS device. The push notifications did work, until yesterday.
When I now send a push notification, everything shows successful, but nothing is received on the device.
If I send directly via a curl request, this is the response:
{"multicast_id":7815294000653973158,"success":1,"failure":0,"canonical_ids":0,"results":[{"message_id":"0:1510474219556035%ca9ff0f8ca9ff0f8"}]}
If I send from the Notification dashboard on the firebase console, it shows successfully completed.
I have done the following with no success:
Locked in pod for 'FirebaseInstanceID', "2.0.0", as per FCM Push notifications do not work on iOS 11
Generated a new APN key on developer console and replaced the existing key on FCM setup in Firebase
Downloaded a fresh GoogleService-Info.plist and replaced existing
Checked that bundle id's etc all match
Updated firebase pods to latest:
Using Firebase (4.5.0)
Using FirebaseAnalytics (4.0.4)
Using FirebaseAuth (4.3.1)
Using FirebaseCore (4.0.10)
Using FirebaseFirestore (0.9.1)
Using FirebaseInstanceID (2.0.5)
Using FirebaseMessaging (2.0.6)
Turned message swizzling on and off
Setting Messaging.messaging().shouldEstablishDirectChannel = true as per Firebase notifications not working in iOS 11
Deleted this iOS app from firebase console and redone notification setup from the start
Made sure remote notification in Capabilities are still turned on
Restarted my devices
My setup:
I call FirebaseApp.configure() in the AppDelegate file in the override init() method:
override init() {
super.init()
FirebaseApp.configure()
}
In the AppDelegate didFinishLaunchingWithOptions:
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool {
// get push notification token id for user
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)
}
application.registerForRemoteNotifications()
return true
}
And then I save my token in userdefaults and persist to database later:
func application(_ application: UIApplication, didRegisterForRemoteNotificationsWithDeviceToken deviceToken: Data) {
if let refreshedToken = InstanceID.instanceID().token() {
defaults.set(refreshedToken, forKey: Constant.UserDefaults.token)
print("Token generated: ", refreshedToken)
} else {
print("Could not save token becuase error with instance id token")
}
}
The token generated here I also test with curl as mentioned above and all shows successful.
My GoogleService-Info.plist:
My Info.plist:
Please let me know if any other info is required.
My Android application is still working as before. I am struggling to understand what could change for the iOS app in one day to cause this, or what I perhaps broke in one day:)
Any help would be greatly appreciated.
Ok, fixed it. I disable firebase's message swizzling by adding this to my Info.plist: FirebaseAppDelegateProxyEnabled: NO
Further I removed all firebase messaging delegate methods. And generated my own APN token in didRegisterForRemoteNotificationsWithDeviceToken and setting Messaging.messaging().apnsToken = deviceToken in the didRegisterForRemoteNotificationsWithDeviceToken method once token generated.
func application(_ application: UIApplication, didRegisterForRemoteNotificationsWithDeviceToken deviceToken: Data) {
if let refreshedToken = InstanceID.instanceID().token() {
defaults.set(refreshedToken, forKey: Constant.UserDefaults.token)
Messaging.messaging().apnsToken = deviceToken
print("Token generated: ", refreshedToken)
} else {
print("Could not save token becuase error with instance id token")
}
}
Previously I used the firebase swizzling, it appears as if this stopped working for some reason.
As this was a tough experience for me, I would like to post the steps that I would now reccommend to enable iOS client for FCM:
In the Apple Developer console generate your APN and then select APNs. Select Continue and download your APN certificate. Take note of your Key ID.
Then in the firebase console under settings, cloud messaging, upload your APN key, add the key ID and bundle id, do not upload any p12 certificates.
Disable firebase message swizzling by adding this to your Info.plist:
FirebaseAppDelegateProxyEnabled: NO
Then add the following in your AppDelegate.swift file in the didFinishLaunchingWithOptions method:
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool {
// [START set_messaging_delegate]
Messaging.messaging().delegate = self
// [END set_messaging_delegate]
// get push notification token id for user
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)
}
application.registerForRemoteNotifications()
return true
}
Since swizzling is disabled you need to take care of generating your APN token and assigning it to the firebase messaging apnsToken. You do this by having the following method in your AppDelegate.swift file:
func application(_ application: UIApplication, didRegisterForRemoteNotificationsWithDeviceToken deviceToken: Data) {
if let refreshedToken = InstanceID.instanceID().token() {
defaults.set(refreshedToken, forKey: Constant.UserDefaults.token)
Messaging.messaging().apnsToken = deviceToken
print("Token generated: ", refreshedToken)
} else {
print("Could not save token becuase error with instance id token")
}
}
I then save the token in the userdefaults to later persist to the database see above:
defaults.set(refreshedToken, forKey: Constant.UserDefaults.token)
You can test that your token is working by copying the token printed out in the console and using the FCM messaging console. Or by using a curl request in the terminal as below. Note you have to replace YOUR_LEGACY_SERVER_KEY with the legacy server key which you can find in the firebase console under settings and cloud messaging and replace YOUR_TOKEN with the token printed in the console (which was generated above):
curl -X "POST" "https://fcm.googleapis.com/fcm/send" \
-H "Authorization: key=YOUR_LEGACY_SERVER_KEY” \
-H "Content-Type: application/json" \
-d $'{
"notification": {
"body": "Testing with direct FCM API",
"title": "Test Message",
"badge": "0",
"sound": "default"
},
"registration_ids": [YOUR_TOKEN]
}'