if more than a minute pass from the time a user notification arrived to notification center, there is a "clear" option to dismiss one or more notifications at once from notification center.
How the iOS OS notify that the user tapped on "clear" to dismiss several notifications together?
Van's anwser goes straight into the right direction, but we do not need to implement the custom action to get what the question giver wanted.
If you create the category and pass it to the UNUserNotificationCenter you get a callback on the delegates didReceive function even if the user tabbed on the builtin Clear Button or the "X" Button on the content extension. The ResponeIdentifier will then be response.actionIdentifier == UNNotificationDismissActionIdentifier.
The Category must be something like that:
//Create the category...
UNNotificationCategory(identifier: "YourCustomIdentifier",
actions: [], intentIdentifiers: [], options: .customDismissAction)
//... and pass it to the UNUserNotificationCenter
UNUserNotificationCenter.current().setNotificationCategories(notificationCategories)
The category triggers the magic in the iOS framework and suddenly you get callbacks in your delegate.
The delegate function should look like:
func userNotificationCenter(_ center: UNUserNotificationCenter,
didReceive response: UNNotificationResponse,
withCompletionHandler completionHandler: #escaping () -> Void) {
if response.actionIdentifier == UNNotificationDismissActionIdentifier {
// notification has been dismissed somehow
}
completionHandler()
}
its possible from iOS 10 and above with implementation of custom notification, you will need to work with UNNotificaitons
private func registerForRemoteNotificaiton(_ application: UIApplication) {
// show the dialog at a more appropriate time move this registration accordingly.
// [START register_for_notifications]
if #available(iOS 10.0, *) {
let authOptions: UNAuthorizationOptions = [.alert, .badge, .sound]
UNUserNotificationCenter.current().requestAuthorization(
options: authOptions,
completionHandler: {(granted, error) in
if granted {
DispatchQueue.main.async(execute: {
UIApplication.shared.registerForRemoteNotifications()
})
}
})
configureUserNotification()
// For iOS 10 display notification (sent via APNS)
UNUserNotificationCenter.current().delegate = self
// For iOS 10 data message (sent via FCM)
Messaging.messaging().delegate = self as MessagingDelegate
} else {
let settings: UIUserNotificationSettings =
UIUserNotificationSettings(types: [.alert, .badge, .sound], categories: nil)
application.registerUserNotificationSettings(settings)
}
// [END register_for_notifications]
}
private func configureUserNotification() {
if #available(iOS 10.0, *) {
let action = UNNotificationAction(identifier: UNNotificationDismissActionIdentifier, title: "Cancel", options: [])
//let action1 = UNNotificationAction(identifier: "dismiss", title: "OK", options: [])
let category = UNNotificationCategory(identifier: "myNotificationCategory", actions: [action], intentIdentifiers: [], options: .customDismissAction)
UNUserNotificationCenter.current().setNotificationCategories([category])
} else {
// Fallback on earlier versions
}
}
call registerForRemoteNotificaiton method from appdelegate's didFinishLaunching method.
Then you will need to implement UNUserNotificationCenterDelegate in appdelegate.
and then you will get the clear (here "Dismiss" as we added in action name)
func userNotificationCenter(_ center: UNUserNotificationCenter,
didReceive response: UNNotificationResponse,
withCompletionHandler completionHandler: #escaping () -> Void) {
if response.actionIdentifier == UNNotificationDismissActionIdentifier {
//user dismissed the notifcaiton:
}
completionHandler()
}
find more information here
First you should set the UNUserNotificationCenterDelegate:
import UserNotifications
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
UNUserNotificationCenter.current().delegate = self
return true
}
then user the delegate functinos
extension AppDelegate: UNUserNotificationCenterDelegate {
func userNotificationCenter(_ center: UNUserNotificationCenter, didReceive response: UNNotificationResponse, withCompletionHandler completionHandler: #escaping () -> Void) {
switch response.actionIdentifier {
case UNNotificationDismissActionIdentifier:
// Do whatever you want to do on dismiss action, when user clear the notification
break
case UNNotificationDefaultActionIdentifier:
// Do whatever you want to do on tap action, when user tap on notification and your application will open
break
default:
break
}
}
}
Related
I am creating an app in swift which will use UNNotificationAction buttons.
I have userNotificationCenter set up properly and I can call didReceive properly while the app is open... From here I show a modal window.
The issue is that when the app is not running in foreground or background (user hasn't opened app yet) I can't get didFinishLaunchingWithOptions to parse my code when I check on launchOptions?[UIApplication.LaunchOptionsKey.remoteNotification]
Is there a new technique for processing when a user fresh opens the app when tapping on a push notification using a UNNotificationAction?
In didFinishLaunchingWithOptions, when you discover that you are launching because of a notification, set yourself as the UNUserNotificationCenter's delegate and return false. Now your didReceive implementation will be called.
Kindly Check this code below , Hope this helps.
#UIApplicationMain
class AppDelegate: UIResponder, UIApplicationDelegate,CLLocationManagerDelegate {
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool {
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()
}
#available(iOS 10, *)
// 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(userInfo)
completionHandler([.alert, .badge, .sound])
// Change this to your preferred presentation option
// completionHandler([])
}
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)")
}
switch response.actionIdentifier {
case "action1":
print("Action First Tapped")
case "action2":
print("Action Second Tapped")
default:
break
}
// Print full message.
print(userInfo)
Messaging.messaging().appDidReceiveMessage(userInfo)
completionHandler()
}
Code :
FirebaseApp.configure()
application.registerForRemoteNotifications()
requestNotificationAuthorization(application: application)
NotificationCenter.default.addObserver(self, selector:
#selector(tokenRefreshNotification), name:
NSNotification.Name.InstanceIDTokenRefresh, object: nil)
//in iOS9 called didreceiveremote method when app is killed
let notification = launchOptions?[.remoteNotification]
if notification != nil {
self.application(application, didReceiveRemoteNotification: notification as! [AnyHashable : Any])
}
#objc func tokenRefreshNotification(_ notification: Notification) {
if let refreshedToken = InstanceID.instanceID().token() {
print("InstanceID token: \(refreshedToken)")
UserDefaults.standard.setPushToken(value:refreshedToken)
}
}
func requestNotificationAuthorization(application: UIApplication) {
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)
}
}
extension AppDelegate : UNUserNotificationCenterDelegate {
// iOS10+, called when presenting notification in foreground
func userNotificationCenter(_ center: UNUserNotificationCenter, willPresent notification: UNNotification, withCompletionHandler completionHandler: #escaping (UNNotificationPresentationOptions) -> Void) {
let userInfo = notification.request.content.userInfo
NSLog("[UserNotificationCenter] applicationState: \(applicationStateString) willPresentNotification: \(userInfo)")
//TODO: Handle foreground notification
completionHandler([.alert])
}
// iOS10+, called when received response (default open, dismiss or custom action) for a notification
func userNotificationCenter(_ center: UNUserNotificationCenter, didReceive response: UNNotificationResponse, withCompletionHandler completionHandler: #escaping () -> Void) {
let userInfo = response.notification.request.content.userInfo
NSLog("[UserNotificationCenter] applicationState: \(applicationStateString) didReceiveResponse: \(userInfo)")
//TODO: Handle background notification
if UIApplication.shared.applicationState == .active {
//TODO: Handle foreground notification
} else {
//TODO: Handle background notification
}
completionHandler()
}
func application(_ application: UIApplication, didReceiveRemoteNotification userInfo: [AnyHashable : Any]) {
NSLog("[RemoteNotification] applicationState: \(applicationStateString) didReceiveRemoteNotification for iOS9: \(userInfo)")
if UIApplication.shared.applicationState == .active {
//TODO: Handle foreground notification
} else {
//TODO: Handle background notification
}
}
}
I am developing an app which has target of iOS9 and firebase push notification feature. My requirement is the app removed from background and the user tap push notification then move to a screen(Ex. AccountVC). In iOS 9 I could able to handle push notification when app is removed from background. But in iOS11 I do not know how to handle when app is removed from background. Any help will be appreciated, thanks in advance
I am stuck for few days for trying to add UNNotificationAction buttons for my remote notifications that support iOS 9, 10 and 11. I followed all the steps from multiple tutorials, most of them are using local notification to show the UNNotificationAction which are working so far. However it doesn't work for my remote notification. I am able to send remote notification via Firebase. Please do let me know if some parts of my code are incorrect or missing parts to configure UNNotificationAction buttons.
Those code in my AppDelegate,
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool {
// Override point for customization after application launch.
FirebaseApp.configure()
requestNotificationAuthorization(application: application)
}
return true
}
func requestNotificationAuthorization(application: UIApplication) {
if #available(iOS 10.0, *) {
UNUserNotificationCenter.current().delegate = self
let authOptions: UNAuthorizationOptions = [.alert, .badge, .sound]
UNUserNotificationCenter.current().requestAuthorization(options: authOptions, completionHandler: {granted, error in
if (granted)
{
let viewAction = UNNotificationAction(identifier: "viewAction", title: "View", options: [])
let closeAction = UNNotificationAction(identifier: "closeAction", title: "Close", options: [])
// 2
let buttonCategory = UNNotificationCategory(identifier: "buttonCategory", actions: [viewAction, closeAction], intentIdentifiers: [], options: [])
// 3
UNUserNotificationCenter.current().setNotificationCategories([buttonCategory])
DispatchQueue.main.async {
application.registerForRemoteNotifications()
}
print("Granted")
}
})
} else {
let settings: UIUserNotificationSettings = UIUserNotificationSettings(types: [.alert, .badge, .sound], categories: notificationAction() as? Set<UIUserNotificationCategory>)
application.registerUserNotificationSettings(settings)
DispatchQueue.main.async {
application.registerForRemoteNotifications()
}
}
}
func notificationAction() -> NSSet {
let firstAction: UIMutableUserNotificationAction = UIMutableUserNotificationAction()
firstAction.identifier = "First_Action"
firstAction.title = "View"
firstAction.activationMode = .foreground
firstAction.isDestructive = false
firstAction.isAuthenticationRequired = false
let secondAction: UIMutableUserNotificationAction = UIMutableUserNotificationAction()
secondAction.identifier = "Second_Action"
secondAction.title = "Close"
secondAction.activationMode = .background
secondAction.isDestructive = false
secondAction.isAuthenticationRequired = false
//category
let firstCategory : UIMutableUserNotificationCategory = UIMutableUserNotificationCategory()
firstCategory.identifier = "buttonCategory"
let defaultAction = [firstAction,secondAction]
let mininalAction = [firstAction,secondAction]
firstCategory.setActions(defaultAction, for: UIUserNotificationActionContext.default)
firstCategory.setActions(mininalAction, for: UIUserNotificationActionContext.minimal)
//NSSet of category
let categories = NSSet(object: firstCategory)
return categories
}
#available(iOS 10, *)
extension AppDelegate : UNUserNotificationCenterDelegate {
// iOS10+, called when presenting notification in foreground
func userNotificationCenter(_ center: UNUserNotificationCenter, willPresent notification: UNNotification, withCompletionHandler completionHandler: #escaping (UNNotificationPresentationOptions) -> Void) {
completionHandler([.alert])
}
// iOS10+, called when received response (default open, dismiss or custom action) for a notification
func userNotificationCenter(_ center: UNUserNotificationCenter, didReceive response: UNNotificationResponse, withCompletionHandler completionHandler: #escaping () -> Void) {
if(response.actionIdentifier == "viewAction"){
print("viewing Action")
}
if(response.actionIdentifier == "closeAction"){
print("closing Action")
}
completionHandler()
}
}
There are several steps to making iOS remote notifications via Firebase work, and many of them are not code-related. First make sure that you are able to send/receive a simple text-only notification, without Actions.
Once that's working, then see if you are receiving the built-in actions, UNNotificationDefaultActionIdentifier and UNNotificationDismissActionIdentifier.
If that works, then you should only need the code from your delegate methods and requestNotificationAuthorization method - your notificationAction() method seems unnecessary.
I am trying to implement app launch (from inactive state) with an action from a local notification in iOS 10.
I have followed Launch a local notification at a specific time in iOS and the app launches fine in response to the local notification. But what I want from here is to perform an action in response to data in the notification.
In iOS 8 and 9 I had the setup in AppDelegate
func application(_ application: UIApplication, didReceive notification: UILocalNotification) {
if (application.applicationState == UIApplicationState.inactive || application.applicationState == UIApplicationState.background) {
NotificationCenter.default.post(name: Notification.Name(rawValue: "noteName", object: notification.alertBody)
and the observer catching it in ViewController
override func viewDidLoad() {
super.viewDidLoad()
NotificationCenter.default.addObserver(self, selector: #selector(ViewController.txtFromNotifier), name: NSNotification.Name(rawValue: "noteName", object: nil)
and in iOS 10 now in AppDelegate:
#available(iOS 10.0, *)
func userNotificationCenter(_ center: UNUserNotificationCenter, didReceive response: UNNotificationResponse, withCompletionHandler completionHandler: #escaping () -> Void) {
// Determine the user action
switch response.actionIdentifier {
case UNNotificationDismissActionIdentifier:
print("Dismiss Action")
case UNNotificationDefaultActionIdentifier:
print("Default")
// do something here
I haven't been able to find how to get from the UNNotification Default action ("Default" gets printed in the console after launch) to passing the parameters and executing the txtFromNotifier function in ViewController. Trying to use the NotificationCenter post / addObserver combination works when the app is in the background but doesn't get there when the app is inactive.
Any ideas?
I found a solution. I wrapped a delay around the notification broadcast. My AppDelegate now looks something like this. For selecting the notification centre in didFinishLaunchingWithOptions:
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool {
if #available(iOS 10.0, *) {
let center = UNUserNotificationCenter.current()
center.delegate = self
let options: UNAuthorizationOptions = [.alert, .badge, .sound]
center.requestAuthorization(options: options) {
(granted, error) in
if !granted {
print("Something went wrong")
}
}
} else {
application.registerUserNotificationSettings(UIUserNotificationSettings(types: [.alert , .badge , .sound], categories: nil))
}
return true
}
In didReceiveNotification I select out iOS 10...
func application(_ application: UIApplication, didReceive notification: UILocalNotification) {
if #available(iOS 10.0, *) {
// different notification centre for iOS 10, see #available below
} else {
if (application.applicationState == UIApplicationState.inactive || application.applicationState == UIApplicationState.background) {
runAfterDelay(2.0) { // 2 second delay, not sure if needed but doesn't seem to hurt - runAfterDelay function is below
NotificationCenter.default.post(name: Notification.Name(rawValue: NSLocalizedString("ticker_notification_name", comment: "")), object: notification.alertBody)
}
} /*else {
// handle the local notification when the app is open, if needed
} */
}
}
and use #available option to select for iOS 10:
#available(iOS 10.0, *)
func userNotificationCenter(_ center: UNUserNotificationCenter, didReceive response: UNNotificationResponse, withCompletionHandler completionHandler: #escaping () -> Void) {
// Determine the user action
switch response.actionIdentifier {
case UNNotificationDismissActionIdentifier:
print("Dismiss Action")
case UNNotificationDefaultActionIdentifier:
print("Default")
runAfterDelay(3.0) { // 3 second delay to give observer time to load. 3 seconds is arbitrary. runAfterDelay function is below
NotificationCenter.default.post(name: Notification.Name(rawValue: NSLocalizedString("ticker_notification_name", comment: "")), object: response) // .body
}
case "Snooze":
print("Snooze")
case "Delete":
print("Delete")
default:
print("Unknown action")
}
completionHandler()
}
and the runAfterDelay function that gives the observer time to put its socks on:
func runAfterDelay(_ delay: Double, closure:#escaping ()->()) {
let when = DispatchTime.now() + delay
DispatchQueue.main.asyncAfter(deadline: when, execute: closure)
}
I think that I did not have to make any changes with the observer - I don't see any changes in my revision history, it looks the same as the addObserver method in the original question.
I was wondering how I could prompt the user with the well known 'app' wants to send you push notifications after the user has registered as a user on my app and not as the user opens my app as default (without even being a user).
How would I do this? I thought you would only configure push notification settings in the appDelegate.swift? Thanks in advance!
You can ask for permission for push notification anywhere you want, where realistically if your app has a login page, then for sure after login you need to do it.
You will need first to place the code which asks for the permission in a function in AppDelegate.swift.
func registerUserNotification() {
if #available(iOS 10.0, *) {
let authOptions : UNAuthorizationOptions = [.alert, .badge, .sound]
UNUserNotificationCenter.current().requestAuthorization(options: authOptions, completionHandler: { (bool, error) in
UNUserNotificationCenter.current().delegate = self
if (error == nil) {
// its required for iOS 11 to avoid getting warning about "UI API called on a background thread"
DispatchQueue.main.async {
UIApplication.shared.registerForRemoteNotifications()
}
}
})
} else {
if UIApplication.shared.responds(to: #selector(UIApplication.registerUserNotificationSettings(_:))) {
let types:UIUserNotificationType = ([.alert, .badge, .sound])
let settings:UIUserNotificationSettings = UIUserNotificationSettings(types: types, categories: nil)
UIApplication.shared.registerUserNotificationSettings(settings)
UIApplication.shared.registerForRemoteNotifications()
}
}
}
Note: Also don't forget to import UserNotifications to be able to use the latest SDK for iOS 10.
in any view controller you need to reference the app delegate and call that function:
let appDelegate = UIApplication.shared.delegate as! AppDelegate
appDelegate.registerUserNotification()
Update 1:
You will need to implement the delegate of UNUserNotificationCenter as the following, place the following code at the end of AppDelegate out of class scope (}), this is required for iOS 10 :
#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) {
print("Userinfo \(notification.request.content.userInfo)")
}
func userNotificationCenter(_ center: UNUserNotificationCenter, didReceive response: UNNotificationResponse, withCompletionHandler completionHandler: #escaping () -> Void) {
print("Userinfo \(response.notification.request.content.userInfo)")
}
}
You can go through all of the remote notification configuration outlined by Firebase but skip the steps that prompt the user with "app wants to send you push notifications". Then when your user has completed your registration process, request push notification authorization from iOS.
The following code shows the critical pieces that will display the system prompt to the user. They can be called at anytime. These are also the sames lines of code that need to be left out at app startup.
func turnOnNotifications() {
if #available(iOS 10.0, *) {
UNUserNotificationCenter
.currentNotificationCenter()
.requestAuthorizationWithOptions([.Alert, .Badge, .Sound]) { authorized, error in
}
} else {
let settings = UIUserNotificationSettings(forTypes: [.Alert, .Badge, .Sound], categories: .None)
UIApplication.sharedApplication().registerUserNotificationSettings(settings)
}
}