I'm currently rewriting my app from Objective-C to Swift and working on notifications. For some reason Swift version of the app is not receiving any remote push notifications while Objective-C version does.
Here's the code I'm using to register for notifications in AppDelegate:
UNUserNotificationCenter.current().delegate = self
let options: UNAuthorizationOptions = [.badge, .alert, .sound]
UNUserNotificationCenter.current().requestAuthorization(options: options, completionHandler: { granted, error in
print("access granted: \(granted)")
})
application.registerForRemoteNotifications()
I assume that the app successfully registers because didRegisterForRemoteNotificationsWithDeviceToken method gets called. But when I try to send the test notification using the token I got from that method, I don't get actual notification on device.
Also none of these methods get called:
func pushRegistry(_ registry: PKPushRegistry, didReceiveIncomingPushWith payload: PKPushPayload, for type: PKPushType) {
}
func userNotificationCenter(_ center: UNUserNotificationCenter, openSettingsFor notification: UNNotification?) {
}
func userNotificationCenter(_ center: UNUserNotificationCenter, willPresent notification: UNNotification, withCompletionHandler completionHandler: #escaping (_ options: UNNotificationPresentationOptions) -> Void) {
}
func userNotificationCenter(_ center: UNUserNotificationCenter, didReceive response: UNNotificationResponse, withCompletionHandler completionHandler: #escaping () -> Void) {
}
func application(_ application: UIApplication, didReceiveRemoteNotification userInfo: [AnyHashable : Any], fetchCompletionHandler completionHandler: #escaping (UIBackgroundFetchResult) -> Void) {
}
func application(_ application: UIApplication, handleActionWithIdentifier identifier: String?, forRemoteNotification userInfo: [AnyHashable : Any], completionHandler: #escaping () -> Void) {
}
What am I doing wrong?
you say that you are not receiving so let's first make sure that those methods you mention before that are not being called are comming from here extension AppDelegate: UNUserNotificationCenterDelegate (By the way when you do implement the didReceive method make sure you call the completionHandler() at the end)
When you do this:
application.registerForRemoteNotifications()
Make sure to run it from the main thread, as it can be called if not specified from the background and that might fail. You can do so by doing
DispatchQueue.main.async {
UIApplication.shared.registerForRemoteNotifications()
}
I'll assume that you are getting the device token this way or something similar:
func application(_ application: UIApplication, didRegisterForRemoteNotificationsWithDeviceToken deviceToken: Data) {
// 1. Convert device token to string
let tokenParts = deviceToken.map { data -> String in
return String(format: "%02.2hhx", data)
}
let token = tokenParts.joined()
// 2. Print device token to use for PNs payloads
print("Device Token: \(token)")
}
Finally implement this method to see if there are any errors while registering the device
func application(_ application: UIApplication, didFailToRegisterForRemoteNotificationsWithError error: Error) {
// 1. Print out error if PNs registration not successful
print("Failed to register for remote notifications with error: \(error)")
}
Oh by the way (just in case you have missed it) make sure you enabled in your project's target the push notification.
Make sure you have enabled Push notification in your project setting capabilities.
How to enable:
go to target: -> select project -> go to capabilities -> go to Push Notification. enable it there.
Another thing is the certificate. While development you should not Production certificates.
Related
My project is developed in Xcode 11 which only consist App delegate(not scene delegate), currently i am using Xcode 13 now but when notification is coming and i am clicking on that the delegates are not getting called.
So many gaps in your question. Let's begin.
You may have done this already but you first need to setup your capabilities and entitlements properly, which I assume you have.
Then, in appDidFinishLaunching delegate method you need to register for push notifications:
UNUserNotificationCenter.current().delegate = self
UNUserNotificationCenter.current()
.requestAuthorization(options: [.alert, .sound, .badge]) { [weak self] granted, _ in
print("Permission granted: (granted)")
guard granted else { return }
UNUserNotificationCenter.current().getNotificationSettings { settings in
print("Notification settings: \(settings)")
guard settings.authorizationStatus == .authorized else { return }
DispatchQueue.main.async {
UIApplication.shared.registerForRemoteNotifications()
}
}
}
Then you will receive one of the delegate calls depending if it succeeded or failed:
func application(_ application: UIApplication, didRegisterForRemoteNotificationsWithDeviceToken deviceToken: Data) {
// Need to send your token to backend
}
func application(_ application: UIApplication, didFailToRegisterForRemoteNotificationsWithError error: Error) {
print(error.localizedDescription)
}
Then, you should look at these delegate methods when receiving a push notification:
For silent pushes:
func application(_ application: UIApplication, didReceiveRemoteNotification userInfo: [AnyHashable : Any], fetchCompletionHandler completionHandler: #escaping (UIBackgroundFetchResult) -> Void) {
}
For non-silent:
(if app is in foreground)
func userNotificationCenter(_ center: UNUserNotificationCenter, willPresent notification: UNNotification, withCompletionHandler completionHandler: (UNNotificationPresentationOptions) -> Void) {
completionHandler([.banner, .badge, .sound])
}
(if app is in background)
func userNotificationCenter(_ center: UNUserNotificationCenter,
didReceive response: UNNotificationResponse,
withCompletionHandler completionHandler: #escaping () -> Void) {
completionHandler()
}
If still not working, go to step 0.
I seem to have no control over how the Deep Link in Push Notifications is always sending me to the App Store, even after the App is opened when I tap on the Push Notification. I already have a handler for my Push Notification and it catches the url:
#available(iOS 10.0, *)
extension Coordinating: UNUserNotificationCenterDelegate {
func userNotificationCenter(_ center: UNUserNotificationCenter, didReceive response: UNNotificationResponse, withCompletionHandler completionHandler: #escaping () -> Void) {
if let apsDict = response.notification.request.content.userInfo["rpr_url"] as? String {
if apsDict.fullURLPathString().contains(string: "myurlPath") {
Coordinating.sharedInstance.customFunction()
}
}
completionHandler()
}
}
The delegate for this is set as well, I also have the didReceiveRemoteNotification handled:
func application(_ application: UIApplication, didReceiveRemoteNotification userInfo: [AnyHashable : Any], fetchCompletionHandler completionHandler: #escaping (UIBackgroundFetchResult) -> Void) {
customHandler.handle(application, userInfo: userInfo, fetchCompletionHandler: completionHandler)
}
func application(_ application: UIApplication, didReceiveRemoteNotification userInfo: [AnyHashable : Any]) {
customHandler.handle(application, userInfo: userInfo, fetchCompletionHandler: nil)
}
I am currently using Repro's Push Notification Service. I have also tried disabling/enabling and adding breakpoints to all URL handlers. Where does this redirect bit of code come from?
Edit: the app-site-association file is also handling Deep Links, so no problems when clicking on the url from E-mails and Browsers. This seems to only be happening for Push Notifications.
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 want to use FCM to send notifications and data messages to iOS Devices.
All works fine, but for some reason the delegate method messaging didReceiveRemoteMessage is never called. Therefore I cannot get data messages when the app is in the foreground... I tried it with and without a notification block beside the data. So the message I am sending looks like this:
{'message': {
'token': 'mytokenstandshere',
'notification': {
'body': 'message',
'title': 'title'},
'data': {
'datafield': 'datavalue',
'datafield2': 'datavalue2'}
}}
I tried all possibilities (without notification, without data, with both). Notification is working without problems, but the data block is not appearing in clean data messages.
I just want to have this running on iOS 11+. But I even tried it with the parts for iOS 9 and 10 from the docs from google.
This is my AppDelegate:
#
UIApplicationMain
class AppDelegate: UIResponder, UIApplicationDelegate, MessagingDelegate {
var window: UIWindow?
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool {
Hardware.start()
Preferences.initDefaults()
FirebaseApp.configure()
Messaging.messaging().delegate = self
Messaging.messaging().shouldEstablishDirectChannel = true
UNUserNotificationCenter.current().delegate = self
let authOptions: UNAuthorizationOptions = [.alert, .badge, .sound]
UNUserNotificationCenter.current().requestAuthorization(
options: authOptions,
completionHandler: {_, _ in })
application.registerForRemoteNotifications()
return true
}
func messaging(_ messaging: Messaging, didReceiveRegistrationToken fcmToken: String) {
print("new Token: \(fcmToken)")
Messaging.messaging().subscribe(toTopic: TOPIC_ALL_MESSAGES)
NotificationSettingsListener.start()
}
func messaging(_ messaging: Messaging, didReceive remoteMessage: MessagingRemoteMessage) {
print("Got a remote")
}
func application(_ application: UIApplication, didReceiveRemoteNotification userInfo: [AnyHashable : Any], fetchCompletionHandler completionHandler: #escaping (UIBackgroundFetchResult) -> Void) {
completionHandler(UIBackgroundFetchResult.newData)
}
}
#available(iOS 10, *)
extension AppDelegate : UNUserNotificationCenterDelegate {
func userNotificationCenter(_ center: UNUserNotificationCenter,
willPresent notification: UNNotification,
withCompletionHandler completionHandler: #escaping (UNNotificationPresentationOptions) -> Void) {
let userInfo = notification.request.content.userInfo
// Print full message.
print(userInfo)
// Change this to your preferred presentation option
completionHandler([.alert])
}
func userNotificationCenter(_ center: UNUserNotificationCenter,
didReceive response: UNNotificationResponse,
withCompletionHandler completionHandler: #escaping () -> Void) {
let userInfo = response.notification.request.content.userInfo
// Print full message.
print(userInfo)
completionHandler()
}
}
I'm not seeing:
application(_: didReceiveRemoteNotification: fetchCompletionHandler:)
I believe that's required for handling. Reference
Please make sure you have to assign Firebase messaging delegate in AppDelegate.m
[FIRMessaging messaging].delegate = self;
Running this on Xcode8.3 with swift 3.1
Below is my code in AppDelegate.swift
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool {
let center = UNUserNotificationCenter.current()
center.requestAuthorization(options: [.alert, .sound, .badge], completionHandler: { granted, error in
if granted {
print("OK!")
}
else {
print("No!")
}
})
application.registerForRemoteNotifications()
return true
}
func application(_ application: UIApplication, didReceiveRemoteNotification userInfo: [AnyHashable : Any], fetchCompletionHandler completionHandler: #escaping (UIBackgroundFetchResult) -> Void) {
print("==== didReceiveRemoteNotification ====")
print(userInfo)
}
I use node-apns to push notification to my app and I can have message from my Debug area in Xcode.
==== didReceiveRemoteNotification ====
[AnyHashable("id"): 123, AnyHashable("aps"): {
alert = "Hello World \U270c";
badge = 3;
}]
But, I did not receive any notification on my iPhone.
Did I missing something?
With iOS 10, you can do the following to see push notifications when the App is in Foreground. You have to implement the below function in AppDelegate. It's a delegate method of UNUserNotificationCenterDelegate.
func userNotificationCenter(_ center: UNUserNotificationCenter, willPresent notification: UNNotification, withCompletionHandler completionHandler: #escaping (UNNotificationPresentationOptions) -> Void)
Follow the below steps:
Import UserNotifications and extend UNUserNotificationCenterDelegate in AppDelegate.
Set up the UNUserNotificationCenter delegate in didFinishLaunchingWithOptions.
let center = UNUserNotificationCenter.current()center.delegate = self
Implement the will present method in AppDelegate and call the completion handler with the options.
func userNotificationCenter(_ center: UNUserNotificationCenter, willPresent notification: UNNotification, withCompletionHandler completionHandler: #escaping (UNNotificationPresentationOptions) -> Void) {
completionHandler([.alert,.badge,.sound])
}
Now if you get a push, you can see the notification alert even if your app is in foreground. More Info in Apple Doc.
if you want to handle notifications, when your app is active, you should do something like this, because the push view won't appear
func application(_ application: UIApplication, didReceiveRemoteNotification userInfo: [AnyHashable: Any]) {
if application.applicationState == .active {
//create custom view or something
} else {
//it was background/off
}
}
you might be also interested in creating new build schema, so your app will wait until it would be launched, so you can debug your app behaviour when receiving push.