I want to show notification in foreground for only some selected UIViewControllers.
But When I set NotificationCenter to receive notification in foreground for specific UIViewController then Swift do it for global scope. In some screen I don't want to see notification to appear in foreground and for that I have to specify in every screen to show notification in foreground or not which leads to lot of code usually unmanaged.
You can put the condition in the notification delegate method to show notification for a specific view controller or not.
#available(iOS 10.0, *)
func userNotificationCenter(_ center: UNUserNotificationCenter, willPresent notification: UNNotification, withCompletionHandler completionHandler: #escaping (UNNotificationPresentationOptions) -> Void) {
let navigationController: UINavigationController = self.window?.rootViewController as! UINavigationController
if (navigationController.topViewController is FirstViewController) || (navigationController.topViewController is SecondViewController) {
//Show notification for First and Second ViewController
completionHandler([.alert, .badge, .sound])
}
else {
//Do whatever when you don't want to show notification
}
}
I hope this will be helpful to you...
Related
I have researched and came across a few articles about opening local notifications to a specific view and showing the information within the view controller. Unfortunately, I'm still unable to make the function work in my app.
I have a quote app and I send local and push notifications. When I have a scheduled notification, the user should be able to click the notfication and be directed to the "TopViewController" and the controller should display the notification information. Instead the app opens up to the last view controller the user was on.
Could someone tell me what I am doing wrong?
Here is the code the I have in the "TopViewController" ViewDidLoad
NotificationCenter.default.addObserver(self, selector: ("Scheduled:"), name:NSNotification.Name(rawValue: "Scheduled"), object: nil)
Here is the code I have in the AppDelegate:
#Published var quote = Quote(id: "", text: "", author: "")
func userNotificationCenter(_ center: UNUserNotificationCenter, didReceive response: UNNotificationResponse, withCompletionHandler completionHandler: #escaping () -> Void) {
let application = UIApplication.shared
if(application.applicationState == .active){
print("user tapped the notification bar when the app is in foreground")
}
if(application.applicationState == .inactive)
{
print("user tapped the notification bar when the app is in background")
}
guard let rootViewController = (UIApplication.shared.connectedScenes.first?.delegate as? SceneDelegate)?.window?.rootViewController else {
return
}
print("got active scene")
let storyboard = UIStoryboard(name: "Main", bundle: nil)
if let conversationVC = storyboard.instantiateViewController(withIdentifier: "TopViewController") as? TopViewController,
let tabBarController = self.window?.rootViewController as? UITabBarController
{
conversationVC.shareQuote?.text = response.notification.request.content.body
conversationVC.shareQuote?.author = response.notification.request.content.subtitle
}
completionHandler()
}
The notification doesn't open up to the right view controller nor does it show the notification information.
Here are a few ideas I have, however, I cannot be certain if this is your fix but it was too much to put into the comments.
Are you setting the app delegate as the UNNotificationCenter's delegate ?
UNUserNotificationCenter.current().delegate = self
Does anything print to your console like print("got active scene") or print("user tapped the notification bar when the app is in foreground") ?
If you have done both of the above, can you add breakpoints inside this block of code
{
conversationVC.shareQuote?.text = response.notification.request.content.body
conversationVC.shareQuote?.author = response.notification.request.content.subtitle
}
This will tell you if there is some issue in your logic if it does not enter this block of code
Update
To manage notifications in the foreground, you need to also implement this UNUserNotificationCenterDelegate method:
// This will show the notification when the app is in
// the foreground. This is optional
func userNotificationCenter(_ center: UNUserNotificationCenter,
willPresent notification: UNNotification,
withCompletionHandler completionHandler: #escaping (UNNotificationPresentationOptions) -> Void) {
completionHandler([.list, .banner, .badge, .sound])
}
I have implemented push notification for ios10. Tapping on the notification alert would trigger "didReceive" delegate, were i save the notification in coredata or silent notification if i m in foreground. The problem is if i receive a stack of notification in background and When i bring my app to foreground from background, Is there a possibility to call "didReceive" delegate or any other push notification delegate were i could sync my items to coredata.
Note
I don't want to sync(didReceive or any delegate) the items in background using silent notification nor tapping on the alert. It should sync automatically the stack of push notification when i bring the app to foreground
func handleInboxNotification(didReceiveRemoteNotification userInfo: [AnyHashable: Any]) {
if let inboxValue = userInfo["inbox"] as? String, let value = inboxValue.boolValue(), value {
let mappedMessage = MessageCenterSDK.shared.constructReorderedDictionary(userInfo: userInfo)
MessageCenterDataManager.shared.synchronize(messages: mappedMessage)
messageCenterDelegate?.didFindNewMessages(hasNewMessages: true)
}
}
func userNotificationCenter(_ center: UNUserNotificationCenter,
didReceive response: UNNotificationResponse,
withCompletionHandler completionHandler: #escaping () -> Void) {
/// Handle outbound remote
handleInboxNotification(didReceiveRemoteNotification: response.notification.request.content.userInfo)
completionHandler()
}
I was facing the same issue, but my target was for iOS 13 & I'm using BGTaskScheduler to fetch data from the server in the background as well in terminated state.
I too want to trigger the notification when the app is in background. But not tapped but that seems to be not possible so we have changed our implementation by enabling background mode and it worked for me.
Hope it also helps.
For more reference on BGTaskScheduler, https://developer.apple.com/documentation/backgroundtasks/bgtaskscheduler
you need to use willpresent delegate in appDelegate
func userNotificationCenter(_ center: UNUserNotificationCenter,
willPresent notification: UNNotification,
withCompletionHandler completionHandler: #escaping (UNNotificationPresentationOptions) -> Void) {
// Change this to your preferred presentation option
completionHandler([])
}
This delegate is called when a notification comes in foreground
I am using firebase notifications in my chat app and I am getting it to my device.My question is how to hide remote notification at the particular view controller.when the app is in the foreground I am using
UNUserNotificationCenter, will present delegate method
to display the banner but when I am in chatViewController I don't want to show the notification banner, sound, alert like whats app feature I want..Sorry for my English
func userNotificationCenter(_ center: UNUserNotificationCenter, willPresent notification: UNNotification, withCompletionHandler completionHandler: #escaping (UNNotificationPresentationOptions) -> Void) {
let userInfo:[AnyHashable:Any] = notification.request.content.userInfo
print("\(userInfo)")
completionHandler([.alert, .badge, .sound])
}
You can find current view controller using this methods:
extension UIViewController {
var topViewController: UIViewController? {
return self.topViewController(currentViewController: self)
}
private func topViewController(currentViewController: UIViewController) -> UIViewController {
if let tabBarController = currentViewController as? UITabBarController,
let selectedViewController = tabBarController.selectedViewController {
return self.topViewController(currentViewController: selectedViewController)
} else if let navigationController = currentViewController as? UINavigationController,
let visibleViewController = navigationController.visibleViewController {
return self.topViewController(currentViewController: visibleViewController)
} else if let presentedViewController = currentViewController.presentedViewController {
return self.topViewController(currentViewController: presentedViewController)
} else {
return currentViewController
}
}
}
And than, in func userNotificationCenter(_ center: UNUserNotificationCenter, willPresent notification: UNNotification, withCompletionHandler completionHandler: #escaping (UNNotificationPresentationOptions) -> Void) add this code:
if self.window?.rootViewController?.topViewController is ChatViewController {
completionHandler([])
} else {
completionHandler([.alert, .badge, .sound])
}
Specify UNNotificationPresentationOptionNone to silence the notification completely.
UNNotificationPresentationOptionNone
Pass the empty completionHandler([]) to hide remote notification.
func userNotificationCenter(_ center: UNUserNotificationCenter, willPresent
notification: UNNotification,
withCompletionHandler completionHandler:
#escaping (UNNotificationPresentationOptions) ->
Void) {
let userInfo:[AnyHashable:Any] = notification.request.content.userInfo
print("\(userInfo)")
completionHandler([])
}
This is working for us-
AppDelegate.m
// Called when a notification is delivered to a foreground app.
-(void)userNotificationCenter:(UNUserNotificationCenter *)center willPresentNotification:(UNNotification *)notification withCompletionHandler:(void (^)(UNNotificationPresentationOptions options))completionHandler
{
completionHandler(UNNotificationPresentationOptionNone);
}
If i implement the method to present push notification in ios 10.0
#available(iOS 10.0, *)
func userNotificationCenter(_ center: UNUserNotificationCenter, willPresent notification: UNNotification, withCompletionHandler completionHandler: #escaping (UNNotificationPresentationOptions) -> Void)
{
completionHandler(UNNotificationPresentationOptions.alert)
}
then it present all the notification ,
So, my question is how can i prevent to show particular push notification like (login at another deveice ) i just handle code for that particular push only
My push data is
{
"aps" : {
"alert" : {
"id" : 2091
},
"sound" : "chime.aiff"
},
"acme" : "foo"
}
I have used id because i can separate each push by its id, and based on id i can decide weather to show notification in foreground or not..
Thanks #Anbu.Karthik For reference
as per my question we can handle push even user did't tap on notification in
#available(iOS 10.0, *)
func userNotificationCenter(_ center: UNUserNotificationCenter, willPresent notification: UNNotification, withCompletionHandler completionHandler: #escaping (UNNotificationPresentationOptions) -> Void)
{
print("Handle push from foreground")
let userInfo:[AnyHashable:Any] = notification.request.content.userInfo
let aps:NSDictionary = (userInfo[AnyHashable("aps")] as? NSDictionary)!
let alert:NSDictionary = (aps["alert"] as? NSDictionary)!
let id = Int(alert["id"] as! Int)
if id == your id
{
//Handle push without prompting alert
}
else
{
//Display alert
completionHandler(UNNotificationPresentationOptions.alert)
}
}
And When user tap on notification Following method is called...
func application(_ application: UIApplication, didReceiveRemoteNotification userInfo: [AnyHashable : Any]) {
print(userInfo)
}
As per the official documentation:
// The method will be called on the delegate only if the application
is in the foreground. If the method is not implemented or the handler
is not called in a timely manner then the notification will not be
presented. The application can choose to have the notification
presented as a sound, badge, alert and/or in the notification list.
This decision should be based on whether the information in the
notification is otherwise visible to the user.
- (void)userNotificationCenter:(UNUserNotificationCenter *)center willPresentNotification:(UNNotification *)notification withCompletionHandler:(void (^)(UNNotificationPresentationOptions options))completionHandler __IOS_AVAILABLE(10.0) __TVOS_AVAILABLE(10.0) __WATCHOS_AVAILABLE(3.0);
So to answer your question, if you want to prevent the notification from being shown, either do not implement this method or do not call the handler.
Hope it helps.
I have implemented local notification successfully for iOS 10.2.
But problem is that notification alert only comes if the app is in the background no alert come if the app is foreground.
Is it possible to get local notification in the foreground?
My code is here
func notificationNow(){
print("notification will be triggered in five seconds..Hold on tight")
let content = UNMutableNotificationContent()
content.title = "Intro to Notifications"
content.subtitle = "Lets code,Talk is cheap"
content.body = "Sample code from WWDC"
content.sound = UNNotificationSound.default()
//To Present image in notification
if let path = Bundle.main.path(forResource: "menu2", ofType: "png") {
let url = URL(fileURLWithPath: path)
do {
let attachment = try UNNotificationAttachment(identifier: "sampleImage", url: url, options: nil)
content.attachments = [attachment]
} catch {
print("attachment not found.")
}
}
// Deliver the notification in five seconds.
let trigger = UNTimeIntervalNotificationTrigger.init(timeInterval: 5.0, repeats: false)
let request = UNNotificationRequest(identifier:requestIdentifier, content: content, trigger: trigger)
UNUserNotificationCenter.current().delegate = self
UNUserNotificationCenter.current().add(request){(error) in
if (error != nil){
print(error?.localizedDescription as Any)
}
}
}
In the documentation for UNUserNotificationCenterDelegate:
Important
You must assign your delegate object to the UNUserNotificationCenter object no later before your app finishes launching. For example, in an iOS app, you must assign it in the application(:willFinishLaunchingWithOptions:) or application(:didFinishLaunchingWithOptions:) method.
You appear to be setting the delegate much later — just before the notification is added to the notification center.
I created a simple Swift class singleton with an extension to conform to UNUserNotificationCenterDelegate. In the singleton's init method I assigned the delegate to self. I then initialize the singleton in the AppDelegate's willFinishLaunchingWithOptions method.
Write following code (extension) in the class where you want to observe the local notification
This will notify when you receive notification in foreground or user tapped on notification when the app is in background.
Hope this will solve your problem.
extension ViewController:UNUserNotificationCenterDelegate{
func userNotificationCenter(_ center: UNUserNotificationCenter, didReceive response: UNNotificationResponse, withCompletionHandler completionHandler: #escaping () -> Void) {
print("Tapped in notification")
}
//This is key callback to present notification while the app is in foreground
func userNotificationCenter(_ center: UNUserNotificationCenter, willPresent notification: UNNotification, withCompletionHandler completionHandler: #escaping (UNNotificationPresentationOptions) -> Void) {
print("Notification being triggered")
//You can either present alert ,sound or increase badge while the app is in foreground too with ios 10
//to distinguish between notifications
// if notification.request.identifier == requestIdentifier
// {
completionHandler( [.alert,.sound,.badge])
// }
}
}
When the app is running in Foreground. You need to capture the Local Notification through delegate methods.
So in your AppDelegate implement the listening for delegate in didFinishLaunchingWithOptions method:
It is important to set the delegate before your app finishes launching.
// Do NOT forget to retain your delegate somewhere
let notificationDelegate = UYLNotificationDelegate()
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool {
let center = UNUserNotificationCenter.current()
center.delegate = notificationDelegate
// ...
return true
}
If you want to respond to actionable notifications or receive notifications while your app is in the foreground you need to implement the UNUserNotificationCenterDelegate. This protocol defines two optional methods:
userNotificationCenter(_:willPresent:withCompletionHandler:) is
called when a notification is delivered to a foreground app. You
receive the UNNotification object which contains the original
UNNotificationRequest. You call the completion handler with the
UNNotificationPresentationOptions you want to present (use .none to
ignore the alert).
userNotificationCenter(_:didReceive:withCompletionHandler:) is called
when a user selects an action in a delivered notification. You
receive the UNNotificationResponse object which includes the
actionIdentifier for the user action and the UNNotification object.
The system defined identifiers UNNotificationDefaultActionIdentifier
and UNNotificationDismissActionIdentifier are used when the user taps
the notification to open the app or swipes to dismiss the
notification.
In both cases you must call the completion handler once you finish.
#pragma mark - UNUserNotificationCenterDelegate Methods
func userNotificationCenter(_ center: UNUserNotificationCenter,
willPresent notification: UNNotification,
withCompletionHandler completionHandler: #escaping (UNNotificationPresentationOptions) -> Void) {
// Play sound and show alert to the user
completionHandler([.alert,.sound])
}
Use below method to configure notification to show in foreground,
func userNotificationCenter(_ center: UNUserNotificationCenter, willPresent notification: UNNotification, withCompletionHandler completionHandler: #escaping (UNNotificationPresentationOptions) -> Void)
You can refer Apple documentation for more details!