I have managed to get push notifications working correctly and going to the right device using FireBase. However if a user clicks on the push notification I would like all of them to go to a specific Controller Called "NotificationC" right now if a user clicks on a push notification it goes to a controller called HomeC which I don't want . I have read Firebase push notification action but still can't get it to go to that controller here is my code.
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool {
UNUserNotificationCenter.current().delegate = self as? UNUserNotificationCenterDelegate
UNUserNotificationCenter.current().requestAuthorization(options: [.badge, .sound, .alert], completionHandler: {(granted, error) in
if (granted)
{
UIApplication.shared.registerForRemoteNotifications()
Messaging.messaging().delegate = self as? MessagingDelegate
}
else{
//Do stuff if unsuccessful...
}
})
application.registerForRemoteNotifications()
NotificationCenter.default.addObserver(self, selector:
#selector(tokenRefreshNotification), name: NSNotification.Name.InstanceIDTokenRefresh, object: nil)
FirebaseApp.configure()
return true
}
func tokenRefreshNotification(notification: NSNotification) {
// NOTE: It can be nil here
let login = LoginC()
login.getDeviceToken(id: MYID)
newDeviceToken = true
}
// The callback to handle data message received via FCM for devices running iOS 10 or above.
func applicationReceivedRemoteMessage(_ remoteMessage: MessagingRemoteMessage) {
let homeC = HomeC()
homeC.getMyNotifcations()
}
#available(iOS 10.0, *)
func userNotificationCenter(_ center: UNUserNotificationCenter, willPresent notification: UNNotification, withCompletionHandler completionHandler: #escaping (UNNotificationPresentationOptions) -> Void) {
print("User Info = ",notification.request.content.userInfo)
completionHandler([.alert, .badge, .sound])
}
//Called to let your app know which action was selected by the user for a given notification.
#available(iOS 10.0, *)
func userNotificationCenter(_ center: UNUserNotificationCenter, didReceive response: UNNotificationResponse, withCompletionHandler completionHandler: #escaping () -> Void) {
let homeC = HomeC()
homeC.getMyNotifcations()
completionHandler()
}
The code above
let homeC = HomeC()
homeC.getMyNotifcations()
is essentially a segue which should get users to my Notification Controller and this is how I have it done
func getMyNotifcations() {
let Popup = UIStoryboard(name: "Main", bundle: nil).instantiateViewController(withIdentifier: "NotificationC") as! NotificationC
Popup.notificationCount = Int(notificationCount!)
self.addChildViewController(Popup)
Popup.view.frame = self.view.frame
self.view.addSubview(Popup.view)
Popup.didMove(toParentViewController: self)
}
When I put the getMyNotifcations in a IBAction it works . Again I simply want the user to go to the NotificationC Controller when they tap on the Notification any suggestions would be great . I also put a breakpoint on all those functions in the AppDelegate and none of them fire off on tap .
It seems like your view controller is not instantiated properly, you are just making the object of the class.
Try this and maybe add delay too.
let storyboard : UIStoryboard = UIStoryboard(name: "Main", bundle: nil)
let homeC = storyboard.instantiateViewController(withIdentifier: "YOUR_VIEWCONTROLLER_IDENTIFIER_IN_STORYBOARD") as? HomeC
if homeC != nil {
homeC!.view.frame = (self.window!.frame)
self.window!.addSubview(homeC!.view)
self.window!.bringSubview(toFront: homeC!.view)
homeC.getMyNotifcations()
}
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'm implemented push notification in my app in this way
//MARK:- Register notifications
func registerForPushNotifications() {
if #available(iOS 10.0, *){
let center = UNUserNotificationCenter.current()
center.delegate = self
center.requestAuthorization(options:[.badge, .alert, .sound]) { (granted, error) in
if (granted)
{
UIApplication.shared.registerForRemoteNotifications()
}
else{
//Do stuff if unsuccessful...
}
// Enable or disable features based on authorization.
}
}
else
{
//If user is not on iOS 10 use the old methods we've been using
let types: UIUserNotificationType = [UIUserNotificationType.badge, UIUserNotificationType.alert, UIUserNotificationType.sound]
let settings: UIUserNotificationSettings = UIUserNotificationSettings( types: types, categories: nil )
UIApplication.shared.registerUserNotificationSettings( settings )
UIApplication.shared.registerForRemoteNotifications()
}
}
//MARK: Push Notifications Delegate Methods
func application( _ application: UIApplication, didRegisterForRemoteNotificationsWithDeviceToken deviceToken: Data ) {
var token = ""
for i in 0..<deviceToken.count {
//token += String(format: "%02.2hhx", arguments: [chars[i]])
token = token + String(format: "%02.2hhx", arguments: [deviceToken[i]])
}
USER_DEFAULTS.setValue(token, forKey: "Device_ID")
USER_DEFAULTS.synchronize()
}
func application( _ application: UIApplication, didFailToRegisterForRemoteNotificationsWithError error: Error ) {
print( error.localizedDescription )
}
func application(_ application: UIApplication, didReceiveRemoteNotification userInfo: [AnyHashable: Any]) {
UIApplication.shared.applicationIconBadgeNumber = 0
alertRemoteNotification(userInfo as NSDictionary)
}
//Code for showing alert when in foreground
func alertRemoteNotification(_ userInfo : NSDictionary)
{
if UIApplication.shared.applicationState == .active {
if let aps = userInfo as? NSDictionary {
if let apsDidt = aps.value(forKey: "aps") as? NSDictionary {
if let alertDict = apsDidt.value(forKey: "alert") as? NSDictionary {
if let notification_type = alertDict.value(forKey: "name") as? String {
if let notification_Message = alertDict.value(forKey: "body") as? String {
let alert = UIAlertController(title: notification_type.capitalized + " Alert", message: notification_Message, preferredStyle: UIAlertControllerStyle.alert)
let okayBtn = UIAlertAction(title: "OK", style: .default, handler: { (action) -> Void in
// When Okay
UIApplication.shared.applicationIconBadgeNumber = 0
if #available(iOS 10.0, *) {
let center = UNUserNotificationCenter.current()
center.removeAllDeliveredNotifications() // To remove all delivered notifications
center.removeAllPendingNotificationRequests()
} else {
// Fallback on earlier versions
UIApplication.shared.cancelAllLocalNotifications()
}
let rootViewController = self.window!.rootViewController as! UINavigationController
let mainStoryboard: UIStoryboard = UIStoryboard(name: "Main", bundle: nil)
let dashBoardVC = mainStoryboard.instantiateViewController(withIdentifier: "DashBoardVC") as! DashBoardVC
rootViewController.pushViewController(dashBoardVC, animated: false)
})
let cancelBtn = UIAlertAction(title: "Cancel", style: .default, handler: { (action) -> Void in
UIApplication.shared.applicationIconBadgeNumber = 0
if #available(iOS 10.0, *) {
let center = UNUserNotificationCenter.current()
center.removeAllDeliveredNotifications() // To remove all delivered notifications
center.removeAllPendingNotificationRequests()
} else {
// Fallback on earlier versions
UIApplication.shared.cancelAllLocalNotifications()
}
})
alert.addAction(okayBtn)
alert.addAction(cancelBtn)
self.window?.rootViewController!.present(alert, animated: true, completion: nil)
}
}
}
}
}
}
else {
let rootViewController = self.window!.rootViewController as! UINavigationController
let mainStoryboard: UIStoryboard = UIStoryboard(name: "Main", bundle: nil)
let dashBoardVC = mainStoryboard.instantiateViewController(withIdentifier: "DashBoardVC") as! DashBoardVC
rootViewController.pushViewController(dashBoardVC, animated: false)
}
}
//Delegate methods
#available(iOS 10.0, *)
func userNotificationCenter(_ center: UNUserNotificationCenter, willPresent notification: UNNotification, withCompletionHandler completionHandler: #escaping (UNNotificationPresentationOptions) -> Void) {
completionHandler([.sound, .alert, .badge])
UIApplication.shared.applicationIconBadgeNumber = 0
}
#available(iOS 10.0, *)
func userNotificationCenter(_ center: UNUserNotificationCenter, didReceive response: UNNotificationResponse, withCompletionHandler completionHandler: #escaping () -> Void) {
let userInfo = response.notification.request.content.userInfo as NSDictionary
completionHandler()
self.alertRemoteNotification(userInfo as NSDictionary)
}
I could able to access the responce after tapping notification banner but the actual issue is when i'm in foreground i need to show an alert with the notification responce without tapping on notification banner. Please let me know how can get responce without tapping on notification banner.
iOS 10+ Provides delegate userNotificationCenter:willPresentNotification:withCompletionHandler
Asks the delegate how to handle a notification that arrived while the
app was running in the foreground.
And this will call only if app opened.
Also you can use CONTENT-AVAILABLE=1 for triggering methods.
FLOW:(Without Taping Notification, content-available:1)
App Opened State:- willPresentNotification(ios10+) -> didReceiveRemoteNotification:fetchCompletionHandler
App in Background:- didReceiveRemoteNotification:fetchCompletionHandler
App Closed:- You won't get notification data unless, the app opened by clicking Notification
Alternate method: Using Rich Notification
You can use Notification Extensions to create custom push notifications(contents including images/videos). Notification Service Extension & Notification Content Extension used to achieve this. mutable-content:1 required to trigger this. Here you can download images, get data, etc. [But the data can be shared with App ONLY through UserDefaults(App Groups), correct me if i'm wrong]
You could search for some random tutorials
Create notification service extension to process notification data. you will get 30seconds to process pushnotification via notification service extension
override func didReceive(_ request: UNNotificationRequest, withContentHandler contentHandler: #escaping (UNNotificationContent) -> Void) {
if let copy = request.content.mutableCopy() as? UNMutableNotificationContent {
// Process your notification here
contentHandler(copy)
}
}
Hope this will help you
#available(iOS 10.0, *)
func userNotificationCenter(_ center: UNUserNotificationCenter, willPresent notification: UNNotification, withCompletionHandler completionHandler: #escaping (UNNotificationPresentationOptions) -> Void) {
completionHandler([.sound, .alert, .badge])
UIApplication.shared.applicationIconBadgeNumber = 0
// Added This line
alertRemoteNotification(notification.request.content.userInfo as NSDictionary)
}
Working without any issues.
If the user clicks on the icon, then the application starts with the start page. How can I make sure that the user clicked on the push notification to launch the application with another start page?
func application(_ application: UIApplication, didReceive notification: UILocalNotification) {
}
func application(_ application: UIApplication, didRegisterForRemoteNotificationsWithDeviceToken deviceToken: Data) {
Messaging.messaging().apnsToken = deviceToken;
}
func application(_ application: UIApplication, didFailToRegisterForRemoteNotificationsWithError error: Error) {
print("Registration failed!")
}
// Firebase notification received
// This function will work app is in foreground state
#available(iOS 10.0, *)
func userNotificationCenter(_ center: UNUserNotificationCenter, willPresent notification: UNNotification, withCompletionHandler completionHandler: #escaping (_ options: UNNotificationPresentationOptions) -> Void) {
// custom code to handle push while app is in the foreground
if notification.request.content.userInfo["cook"] != nil{
return;
}
print(notification.request.content.userInfo)
UIApplication.shared.applicationIconBadgeNumber = 1;
DispatchQueue.main.async {
// add some code here..
// window?.rootViewController = LoginController()
// If you use the storyboard, then you can get controller by
// storyboard?.instantiateViewController(withIdentifier: "identifier") as! UIViewController
}
}
// This function will work app is in state of background or closed.
#available(iOS 10.0, *)
func userNotificationCenter(_ center: UNUserNotificationCenter, didReceive response: UNNotificationResponse, withCompletionHandler completionHandler: #escaping () -> Void) {
// if you set a member variable in didReceiveRemoteNotification, you can check if this is from closed or background
print("Handle push from background or closed\(response.notification.request.content.userInfo)")
if response.notification.request.content.userInfo["somevariable"] != nil{
return;
}
UIApplication.shared.applicationIconBadgeNumber = 1;
DispatchQueue.main.async {
// add some code here..
}
}
Please use this this code into AppDelegate.swift
Please see the function called userNotificationCenter and comments in
code.
Ok..i will make it simple for you.
First step-
Add this function inside your AppDelegate file.
func userNotificationCenter(_ center: UNUserNotificationCenter, didReceive response: UNNotificationResponse, withCompletionHandler completionHandler: #escaping () -> Void) {
print("Hi Kirill...")
}
If you add this in your AppDelegate file, after running your app when you tap on the notification you will see this on your debugger, .
Like this -
And finally, to show a specific page in application add this inside the function.
let sb = UIStoryboard(name: "Main", bundle: nil)
let otherVC = sb.instantiateViewController(withIdentifier: " Your view controller identifier name") as! yourViewControllerClassName
window?.rootViewController = otherVC;
It will look like this -
func userNotificationCenter(_ center: UNUserNotificationCenter, didReceive response: UNNotificationResponse, withCompletionHandler completionHandler: #escaping () -> Void) {
print("Hi Kirill...")
let sb = UIStoryboard(name: "Main", bundle: nil)
let otherVC = sb.instantiateViewController(withIdentifier: " Your view controller identifier name") as! yourViewControllerClassName
window?.rootViewController = otherVC;
}
Make sure you add a another viewController, what you want to redirect/show when you tap on the notification. Thank you.
I have an iOS APP and a push notifications system which sends programatically notifications to the storing their token in a database.
I have some views which I show or hide in the default View Controller. I would like to make the APP show a specific view when the user taps the notification. I have created a function, but I dont know how to make it work.
You can catch a push notifications inside AppDelegate and add Observer inside your controller file where you want to show a specific view on tap.
Send push notification payload data in userInfo or object in NotificationCenter.default.post.
#available(iOS 10.0, *)
func userNotificationCenter(_ center: UNUserNotificationCenter, didReceive response: UNNotificationResponse, withCompletionHandler completionHandler: #escaping () -> Void) {
UIApplication.shared.applicationIconBadgeNumber = 0
NotificationCenter.default.post(name: NSNotification.Name(rawValue: "NewNotification") , object: nil, userInfo: response.notification.request.content.userInfo)
}
#available(iOS 10.0, *)
func userNotificationCenter(_ center: UNUserNotificationCenter, willPresent notification: UNNotification, withCompletionHandler completionHandler: #escaping (UNNotificationPresentationOptions) -> Void) {
completionHandler(.alert)
}
Add Notification Observer in your controller file :
NotificationCenter.default.addObserver(self, selector: #selector(pushNotificationHandler(_:)) , name: NSNotification.Name(rawValue: "NewNotification"), object: nil)
then call UI update method :
func pushNotificationHandler(_ notification : NSNotification) {
// self.udateUI() // Add code to show a specific view on tap.
}
In AppDelegate APNS delegate method make a check according to the userInfo you get with your notification/payload and then call that function either
func userNotificationCenter(_ center: UNUserNotificationCenter, didReceive response: UNNotificationResponse, withCompletionHandler completionHandler: #escaping () -> Void) {
if response.notification.request.content.userInfo["ViewType"] as! String == "ShowNotificationView" {
// Then you can implement your functionaliton here according to the need.
}
if you need any other help please let me know by posting the actuall code & requirement over
First of all, it is required to add one more key to the push notification payload as screen_type based on the content to which screen you need to navigate or show the particular view controller(screen).
After that, add the following method to your AppDelegate class which implements the notifications handler
func application(_ application: UIApplication, didReceiveRemoteNotification userInfo: [AnyHashable : Any]) {
let payload = userInfo
let type = payload["type"]
if type == "all-posts" || type == "default"{
/*
* I'm assuming you have your own HomeViewController object
*/
let homeVC = HomeViewController()
// present or push
UIApplication.topViewController()?.present(homeVC, animated: true, completion: nil)
}else if type = "post-details"{
/*
* I'm assuming you have your own PostDetailViewController object
*/
let postDetailVC = PostDetailViewController()
// present or push
UIApplication.topViewController()?.present(postDetailVC, animated: true, completion: nil)
}else if type == "friend-request"{
/*
* I'm assuming you have your own UserViewController object
*/
let userVC = UserViewController()
// present or push
UIApplication.topViewController()?.present(userVC, animated: true, completion: nil)
}
}
Helper method
extension UIApplication {
class func topViewController(base: UIViewController? = (UIApplication.sharedApplication().delegate as! AppDelegate).window?.rootViewController) -> UIViewController? {
if let nav = base as? UINavigationController {
return topViewController(base: nav.visibleViewController)
}
if let tab = base as? UITabBarController {
if let selected = tab.selectedViewController {
return topViewController(base: selected)
}
}
if let presented = base?.presentedViewController {
return topViewController(base: presented)
}
return base
}
}
Note: If you are using latest version of swift, you are required to implement the UNUserNotificationCenterDelegate methods
Currently I have my app set up to receive push notifications in ios 9 where it works perfectly but with iOS 10 I'm not receiving them. I've looked over various responses on stackoverflow and came across this:
Push Notifications not being received on iOS 10, but working on iOS 9 and before
which appears to work for the poster. I'm not entirely sure what code I'm supposed to add under the willPresentNotification and didReceiveNotificationResponse sections. If anyone has any examples of how these sections work it will be appreciated. This is my relevant code for handling push notifications so far:
import UserNotifications
import Whisper
#UIApplicationMain
class AppDelegate: UIResponder, UIApplicationDelegate, UNUserNotificationCenterDelegate {
func application(application: UIApplication, didFinishLaunchingWithOptions launchOptions: [NSObject: AnyObject]?) -> Bool {
registerForPushNotifications(application)
}
//MARK: Push Notification Settings
func registerForPushNotifications(application: UIApplication) {
//check to see if phone is updated to iOS 10
if #available(iOS 10.0, *){
UNUserNotificationCenter.currentNotificationCenter().delegate = self
UNUserNotificationCenter.currentNotificationCenter().requestAuthorizationWithOptions([.Badge, .Sound, .Alert], completionHandler: {(granted, error) in
if (granted)
{
UIApplication.sharedApplication().registerForRemoteNotifications()
}
else{
print("registering for push notifications unsuccessful")
}
})
}
else{ //If user is not on iOS 10 use the old methods we've been using
let notificationSettings = UIUserNotificationSettings(
forTypes: [.Badge, .Sound, .Alert], categories: nil)
application.registerUserNotificationSettings(notificationSettings)
}
}
//Notification handling for iOS 10
#available(iOS 10.0, *)
func userNotificationCenter(center: UNUserNotificationCenter, willPresentNotification notification: UNNotification, withCompletionHandler completionHandler: (UNNotificationPresentationOptions) -> Void) {
//Handle the notification - NOT SURE WHAT GOES HERE
}
#available(iOS 10.0, *)
func userNotificationCenter(center: UNUserNotificationCenter, didReceiveNotificationResponse response: UNNotificationResponse, withCompletionHandler completionHandler: () -> Void) {
//Handle the notification -NOT SURE WHAT GOES HERE
}
//This is called if user selects to receive push notifications
func application(application: UIApplication, didRegisterUserNotificationSettings notificationSettings: UIUserNotificationSettings) {
// if notificationSettings.types != .None {
application.registerForRemoteNotifications()
// }
}
func application(application: UIApplication, didRegisterForRemoteNotificationsWithDeviceToken deviceToken: NSData) {
let tokenChars = UnsafePointer<CChar>(deviceToken.bytes)
var tokenString = ""
for i in 0..<deviceToken.length {
tokenString += String(format: "%02.2hhx", arguments: [tokenChars[i]])
}
//save device token to keychain
self.deviceToken = tokenString
userInfo.sharedInstance.savePushNotDeviceToken(tokenString)
NSUserDefaultsManager.sharedManager.pushNotifications = true
//register device token to api
registerPushNotificationDevice(tokenString)
print("Device Token:", tokenString)
}
func application(application: UIApplication, didFailToRegisterForRemoteNotificationsWithError error: NSError) {
print("Failed to register:", error)
//save push notifications state
NSUserDefaultsManager.sharedManager.pushNotifications = false
}
//In- App push notifications
func application(application: UIApplication, didReceiveRemoteNotification userInfo: [NSObject : AnyObject]) {
if application.applicationState == .Active {
let navigationController = self.window!.rootViewController as! UINavigationController
let alert = [String: String]()
let title = ""
let body = ""
// Default printout of userInfo
print("All of userInfo:\n\( userInfo)\n")
if let aps = userInfo["aps"] as? NSDictionary {
if let alert = aps["alert"] as? NSDictionary {
if let title = alert["title"] as? NSString {
if let body = alert["body"] as? NSString {
let announcement = Announcement(title: title as String, subtitle: body as String, image: UIImage(named: "Image"))
show(shout: announcement, to: navigationController)
}
}
}
}
}
}
}
For remote and local notification in iOS 10 we have UserNotifications.framework. To handle notification there are two delegate methods of UNUserNotificationCenterDelegate available in UserNotifications.framework. You need to do the same code you are doing in didReceiveRemoteNotification method to get userInfo.This two methods are available to handle userInfo according to your app requirements.
//UNUserNotificationCenterDelegate delegate methods to get userInfo
#available(iOS 10.0, *)
func userNotificationCenter(_ center: UNUserNotificationCenter, willPresent notification: UNNotification, withCompletionHandler completionHandler: #escaping (_ options: UNNotificationPresentationOptions) -> Void) {
//Called when a notification is delivered to a foreground app.
let userInfo = notification.request.content.userInfo as? NSDictionary
print("\(userInfo)")
}
#available(iOS 10.0, *)
func userNotificationCenter(_ center: UNUserNotificationCenter, didReceive response: UNNotificationResponse, withCompletionHandler completionHandler: #escaping () -> Void) {
// Called to let your app know which action was selected by the user for a given notification.
let userInfo = response.notification.request.content.userInfo as? NSDictionary
print("\(userInfo)")
}