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])
}
Related
I have so many screens in my application. So when the user taps on the local notification , I'd like to deep-link one of the screens.
The could be not running or active on one of those screens or inactive or suspended state.
I couldn't see how to do that
If anyone could explain it'd be great
You can use DeepLinkKit for navigation to different screens. I'm using this library in my app, I have about 30-37 deeplinks for every screen in the app. I'm using this with Cordinators pattern for navigation. Let me know if you need any help with implementing.
Send Notification from anywhere.
func sendNotification(){
let info: [String: String] = ["vc_name": "CartViewController"]
NotificationCenter.default.post(name: Notification.Name("YourLocalNotification"), object: nil, userInfo: info)
}
Add Notification handler on Receive Notification in your root controller.
override func viewDidLoad() {
super.viewDidLoad()
NotificationCenter.default.addObserver(self, selector: #selector(self.youNotificationAction(notification:)), name: Notification.Name("YourLocalNotification"), object: nil)
}
Receive notification, Check your notified information and navigate to your respective controller.
#objc func youNotificationAction(notification: Notification) {
if let vcName = notification.userInfo?["vc_name"] as? String {
let storyBoard: UIStoryboard = UIStoryboard(name: "Main", bundle: nil)
let toVC = storyBoard.instantiateViewController(withIdentifier: vcName)
navigationController?.pushViewController(toVC, animated: true);
}
}
Inherit UNUserNotificationCenterDelegate in appdelegate or viewcontroller
Implement below methods
func userNotificationCenter(_ center: UNUserNotificationCenter, willPresent notification: UNNotification, withCompletionHandler completionHandler: #escaping (UNNotificationPresentationOptions) -> Void) {
completionHandler(UNNotificationPresentationOptions.alert)
}
func userNotificationCenter(_ center: UNUserNotificationCenter, didReceive response: UNNotificationResponse, withCompletionHandler completionHandler: #escaping () -> Void) {
completionHandler()
}
This is where you intercept the notification tap and call the appropriate modules either using OpenURL or doing a direct push or present or switch the tab of viewcontroller.
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...
I have two type of push notifications in my app Type A and Type B and according to each type I want to navigate the user to different view controls. right now the app show only the homeVC if user clicked on the
notification and can I pass value from the notifications object to the
view controls.?
func application(_ application: UIApplication, didReceiveRemoteNotification userInfo: [AnyHashable: Any], fetchCompletionHandler completionHandler: #escaping (UIBackgroundFetchResult) -> Void) {
userInfo["Type"] as? String == "TypeA" {
showNotificationA()
} else {
showNotificationB()
}
completionHandler(UIBackgroundFetchResult.newData)
}
func userNotificationCenter(_ center: UNUserNotificationCenter, didReceive response: UNNotificationResponse, withCompletionHandler completionHandler: #escaping () -> Void) {
// How to identify which notification the user clicked to navigate to the right view?
if UIApplication.shared.applicationState == .inactive {
}
completionHandler()
}
Basically, the handling of push notifications consists more or less of the following steps:
Enabling Push notifications in the capabilities section of project settings
Requesting the access to use remote and local notifications, and delegating incoming notifications to your notification observer entity (class, struct)
This is done in the AppDelegate class, in a similar manner to:
UNUserNotificationCenter
.current()
.requestAuthorization(options: [.alert, .badge]) { (granted, error) in
UNUserNotificationCenter.current().delegate = MyCustomDelegateEntity
}
Implementing the func userNotificationCenter(_ center: UNUserNotificationCenter, didReceive response: UNNotificationResponse, withCompletionHandler completionHandler: #escaping () -> Void) in your delegate, which receives incoming notifications
Notifications can be distinguished by an identifier, which is located in the UNNotificationResponse entity. Based on the id, you can decide how to react accordingly.
In your particular case, you should instantiate a desired view controller from the NotificationDelegate in a similar manner to
let rootVC = MyViewController.instantiateFrom(storyboard: "StoryboardName")
let navigationController = UINavigationController(rootViewController: rootVC)
(UIApplication.shared.delegate as? AppDelegate)?.window?.rootViewController = navigationController
The ViewController.instantiateFrom(storyboard:) content looks something like:
return UIStoryboard(name: storyboard, bundle: nil).instantiateViewController(withIdentifier: String(describing: self)) as! MyViewController
You can redirect all your notification to one controller and in this basview controller check which notification type its came from and navigate accordingly.
You can pass data from notification payload and handle accordingly
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
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()
}