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
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 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.
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 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()
}
I am trying to perform a segue after getting a notification.
Here is what I have in my app delegate.
func application(application: UIApplication, didReceiveRemoteNotification userInfo: [NSObject : AnyObject]) {
let viewController = self.window!.rootViewController!.storyboard!.instantiateViewControllerWithIdentifier("SecondViewController")
self.window?.rootViewController = viewController
}
This takes me directly to the view I would like it to go to, however the navbar and tab bar are hidden. From the app delegate, how do I perform a segue from one view to another? So that I can reach by second view as if I had pushed from my first?
Did you try with:
viewController.performSegueWithIdentifier("my_segue_identifier", sender: nil)
Also the more robust way is to broadcast a NSNotification from your AppDelegate and have your viewcontroller listen for a specific notification.
My solution:
add public variable in AppDelegate (etc. flag =1)
#available(iOS 10.0, *)
func userNotificationCenter(_ center: UNUserNotificationCenter, didReceive response: UNNotificationResponse, withCompletionHandler completionHandler: #escaping () -> Void) {
print("User Info = ",response.notification.request.content.userInfo)
flag = 1; <---
completionHandler()
}
and check the flag on rootviewcontroller-> ViewDidLoad. if it's true ? call your spesific segue
override func viewDidLoad() {
super.viewDidLoad()
if (flag == 1) {
self.performSegue(withIdentifier: "roottanBildirimSegue", sender: self)
}