Perform Action when Notification is received? - ios

I want to switch the root view in App Delegate based off when the user receives a notification. Right now, what I have works but the timing is off. As soon as I generate the notification and schedule it, the observer triggers its Selector method, changing a Boolean too soon. The Notification is set to fire 20 seconds in the future.
I'm using a singleton called StatusOverseer to guide the notifications and view switching. I put this in init():
// Notification Observation
NSNotificationCenter.defaultCenter().addObserver(self, selector: "bankDidApprove", name: "bankApproval", object: nil)
NSNotificationCenter.defaultCenter().addObserver(self, selector: "bankDidNotApprove", name: "bankNoApproval", object: nil)
This is the method in StatusOverseer to generate a notification:
func generateNotification(isGood: Bool) {
if !setNotification && userDidFinishApplication {
let currentTime = NSDate()
let fireTime = NSDate(timeInterval: 20.0, sinceDate: currentTime)
var notification = UILocalNotification()
if isGood {
notification.alertBody = "Congratulations! You've been approved!"
notificationWasGood = true
NSNotificationCenter.defaultCenter().postNotificationName("bankApproval", object: nil)
}
else {
notification.alertBody = "Bank Status is available"
notificationWasGood = false
NSNotificationCenter.defaultCenter().postNotificationName("bankNoApproval", object: nil)
}
notification.alertAction = "open"
notification.fireDate = fireTime
notification.soundName = UILocalNotificationDefaultSoundName
UIApplication.sharedApplication().scheduleLocalNotification(notification)
setNotification = true
println("Notification Set")
}
}
The selector methods:
func bankDidApprove() {
println("Banks approved")
banksDidRespond = true
notificationWasGood = true
}
func bankDidNotApprove() {
println("Banks no approved")
banksDidRespond = true
notificationWasGood = false
}
And I have this in applicationWillEnterForeground:
var so = StatusOverseer.sharedOverseer
// Decide whether or not to present the waiting view
if so.userDidFinishApplication && !StatusOverseer.sharedOverseer.banksDidRespond {
let sb = UIStoryboard(name: "Main", bundle: nil)
let vc = sb.instantiateViewControllerWithIdentifier("submitWaiting") as! UIViewController
UIApplication.sharedApplication().keyWindow?.rootViewController = vc
}
else if so.banksDidRespond {
let sb = UIStoryboard(name: "Main", bundle: nil)
if so.notificationWasGood {
let vc = sb.instantiateViewControllerWithIdentifier("approved") as! UIViewController
UIApplication.sharedApplication().keyWindow?.rootViewController = vc
}
else {
let vc = sb.instantiateViewControllerWithIdentifier("notApproved") as! UIViewController
UIApplication.sharedApplication().keyWindow?.rootViewController = vc
}
}
else if so.userDidAcceptTerms {
let sb = UIStoryboard(name: "Main", bundle: nil)
let vc = sb.instantiateViewControllerWithIdentifier("LicenseView") as! UIViewController
UIApplication.sharedApplication().keyWindow?.rootViewController = vc
}
So is there a better way I should be doing this?
Thanks!

Related

How to pass notification.userinfo from PushNotification to a specific viewcontroller in Swift

I have code like this.
if I receive a pushnotification and I tap on it I want to redirect to specific viewcontroller but I want to pass a value (hidden inside notification) to a specific view controller
func userNotificationCenter(_ center: UNUserNotificationCenter, didReceive, response: UNNotificationResponse, withCompletionHandler completionHandler: #escaping () -> Void) {
let application = UIApplication.shared
if(application.applicationState == .inactive)
{
LocalData.indexTabBar = nil
let storyboard = UIStoryboard(name: "Main", bundle: nil)
if LocalData.emailLogin != nil && LocalData.passwordLogin != nil && LocalData.tokenLogin != nil {
let initialViewController = storyboard.instantiateViewController(withIdentifier: "MainControllerIfSavedLogin") as! UITabBarController
initialViewController.selectedIndex = 2 //Selecting tab here
self.window?.rootViewController = initialViewController
} else{
let initialViewController = storyboard.instantiateViewController(withIdentifier: "loginViewController") as UIViewController
self.window?.rootViewController = initialViewController
}
self.window?.makeKeyAndVisible()
}
completionHandler()
}
Get it with
let userInfo = response.notification.request.content.userInfo
Then send it
let tab = ------ as! MainControllerIfSavedLogin
let vc = tab.viewControllers!.first as! LoginViewController
vc.info = userInfo
class LoginViewController : UIViewController {
var info:[String:Any]?
}

Properly redirect to a view controller inside tab bar's navigation controller when push notification is clicked

I have a tabBar. Each of its tabs' ViewControllers have been embedded inside their respective NavigationControllers.
Hierarchy:
Tabbarcontroller --> NavigationController --> VC1 --> VC2 -->...VCn (for tab1)
I have to redirect to one of the viewcontrollers say VC2 when i click on push notifications. The code i am using is as follows.
let navigationController = UINavigationController()
let storyboard = UIStoryboard.init(name: "Main", bundle: Bundle.main)
if let chatViewController = storyboard.instantiateViewController(withIdentifier: "chatViewController") as? ChatViewController {
navigationController.viewControllers = [chatViewController]
window?.rootViewController = navigationController
}
By using this, I am redirected to the respective ViewController. However, i am not able to get back to the tabBar. So is it possible to redirect in such a way that it allows me the get back to the tab bar, thus providing the same hierarchy as in the UI?
EDIT:
The image below shows my layout.
I would like to accomplish the following:
When the user taps the push notification, the app should be directed to VC-B. *It should not create new object for VC-B and add to the navigation stack if VC-B is already on top of navigation stack.
If the app had been terminated and the user taps on the notification, it should open VC-B.
For determining if the app had been terminated, I set a flag as:
func applicationWillTerminate(_ application: UIApplication) {
UserDefaults.standard.set(true, forKey: UserDefaultsKey.isTerminated)
}
This flag is set false at the end of didFinishLaunchingWithOptions function.
For redirection, I check this flag to determine if the app had been terminated:
func performRedirectionToSuitableViewController(userInfo: [AnyHashable: Any]) {
let isTerminated = UserDefaults.standard.object(forKey: UserDefaultsKey.isTerminated) as! Bool
if isTerminated {
let storyboard = UIStoryboard.init(name: "Main", bundle: Bundle.main)
let tab = storyboard.instantiateViewController(withIdentifier: "tabBar") as! UITabBarController
tab.selectedIndex = 0
let nav = tab.viewControllers![0] as! UINavigationController
let chatViewController = storyboard.instantiateViewController(withIdentifier: "chatViewController") as! ChatViewController
chatViewController.chatThreadId = userInfo["thread_id"] as? String
nav.pushViewController(chatViewController, animated: true)
} else {
if let tab = window?.rootViewController?.presentedViewController as? UITabBarController {
let storyboard = UIStoryboard.init(name: "Main", bundle: Bundle.main)
let chatViewController = storyboard.instantiateViewController(withIdentifier: "chatViewController") as! ChatViewController
chatViewController.chatThreadId = userInfo["thread_id"] as? String
if let nav = tab.selectedViewController as? UINavigationController {
nav.pushViewController(chatViewController, animated: true)
}
}
}
}
With this code, my first requirement is partially fulfilled. Need to determine if the viewcontroller is at the top of the navigation stack.
And, in the case of terminated app, clicking the push notification opens the tab bar, with the default selection index being selected.
I have spent several days trying to fix this. But cannot get it work.
I suppose you have to do something like this:
let tabBar: UITabBarController // get your tab bar
tabBar.selectedIndex = 0 // eg. zero. To be sure that you are on correct tab
if let navigation = tabBar.viewControllers?[tabBar.selectedIndex] as? UINavigationController {
let storyboard = UIStoryboard.init(name: "Main", bundle: Bundle.main)
if let chatViewController = storyboard.instantiateViewController(withIdentifier: "chatViewController") as? ChatViewController {
navigation.pushViewController(chatViewController, animated: true)
}
}
Well, you are setting current window.rootViewController, which should be that TabBarViewController I'd say. That's why you cannot get back to the TabBar
What you should do is
let storyboard = UIStoryboard.init(name: "Main", bundle: nil)
if let chatViewController = storyboard.instantiateViewController(withIdentifier: "chatViewController") as? ChatViewController, let tabBar = storyboard.instantiateViewController(withIdentifier: "tabBar") as? UITabBarController {
let navigationController = UINavigationController(rootViewController: chatViewController)
navigationController.viewControllers = [chatViewController]
tabBar.viewControllers = [navigationController]
window?.rootViewController = tabBar
}
To get the current navigation controller from the tabbar controller. You can use this function
func getVisibleViewController(_ rootViewController: UIViewController?) -> UIViewController? {
var rootVC = rootViewController
if rootVC == nil {
rootVC = UIApplication.shared.keyWindow?.rootViewController
}
if rootVC?.presentedViewController == nil {
return rootVC
}
if let presented = rootVC?.presentedViewController {
if presented.isKind(of: UINavigationController.self) {
let navigationController = presented as! UINavigationController
return navigationController.viewControllers.last!
}
if presented.isKind(of: UITabBarController.self) {
let tabBarController = presented as! UITabBarController
return tabBarController.selectedViewController!
}
//UIAlertController
if presented.isKind(of: UIAlertController.self) {
let alertController = presented as! UIAlertController
return alertController.presentingViewController
}
return getVisibleViewController(presented)
}
return nil
}
Using the function you can navigate your viewcontroller like below.
let mainStoryboardIpad : UIStoryboard = UIStoryboard(name: "Main", bundle: nil)
let yourView = mainStoryboardIpad.instantiateViewController(withIdentifier: "yourView") as! NewnoteViewController
let navi = self.getVisibleViewController(self.window!.rootViewController) as! UINavigationController
navi.pushViewController(yourView, animated: true)
You can create a method in your RootViewController that will redirect user to a specific view after receiving a push notification. Here's what I did in my previous project.
class RootViewController: UIViewController {
private var currentView: UIViewController
init() {
self.currentView = ViewController()
super.init(nibName: nil, bundle: nil)
}
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
override func viewDidLoad() {
super.viewDidLoad()
addChildViewController(currentView) // 1
currentView.view.frame = view.bounds // 2
view.addSubview(currentView.view) // 3
currentView.didMove(toParentViewController: self) // 4
}
func showDetailsScreen() {
if let updateDetailView = UIStoryboard(name: "Main", bundle: nil).instantiateViewController(withIdentifier: "UpdateDetailsNotif") as? UpdateDetailsViewController{
let navController = UINavigationController(rootViewController: updateDetailView)
addChildViewController(navController)
navController.view.frame = view.bounds
view.addSubview(navController.view)
navController.didMove(toParentViewController: self)
currentView.willMove(toParentViewController: nil)
currentView.view.removeFromSuperview()
currentView.removeFromParentViewController()
currentView = navController
}
}
}
Then you can call that method on your AppDelegate like this:
func userNotificationCenter(_ center: UNUserNotificationCenter, didReceive response: UNNotificationResponse, withCompletionHandler completionHandler: #escaping () -> Void) {
let application = UIApplication.shared
AppDelegate.shared.rootViewController.showDetailsScreen()
completionHandler()
}
}

Unable to navigate to specific viewController after clicking push notification in foreground using FCM below iOS 10

I am trying to get the notification in foreground as well as in background, i am getting notification in both states, but i'm unable to navigate it to desired view controller after tapping the notification when app is in foreground, Any help will be highly appreciated, thanks in advance
i am using third party for custom banner, running on iphone 4s device, ios 9
func application(_ application: UIApplication, didReceiveRemoteNotification userInfo: [AnyHashable: Any]) {
// If you are receiving a notification message while your app is in the background,
// this callback will not be fired till the user taps on the notification launching the application.
// TODO: Handle data of notification
// With swizzling disabled you must let Messaging know about the message, for Analytics
Messaging.messaging().appDidReceiveMessage(userInfo)
print("In did Recieve Notification")
// Print message ID.
print("userInfoNotification=\(userInfo)")
if let contactID = userInfo["contactID"] as? String {
self.contactID = contactID
print(contactID)
}
let state = UIApplication.shared.applicationState
if state == .active {
print("App in Foreground")
if self.contactID != AppDelegate.openedChatContactId {
print("openedID=\(AppDelegate.openedChatContactId)")
if let aps = userInfo["aps"] as? NSDictionary {
if let alert = aps["alert"] as? NSDictionary {
let body = alert["body"] as! String
let title = alert["title"] as! String
let banner = Banner(title: title, subtitle: body, image: UIImage(named: "AppIcon"), backgroundColor: UIColor(red:31.00/255.0, green:136.0/255.0, blue:254.5/255.0, alpha:1.000))
banner.dismissesOnTap = true
banner.show(duration: 3.0)
// let storyboard = UIStoryboard(name: "Main", bundle: nil)
// let viewController = storyboard.instantiateViewController(withIdentifier: "chatMessageVC") as! ChatMessagesVC
// UIApplication.shared.keyWindow?.rootViewController = viewController;
} else if let alert = aps["alert"] as? NSString {
}
}
}
}
if state == .inactive || state == .background {
let storyboard = UIStoryboard(name: "Main", bundle: nil)
var destinationViewController = storyboard.instantiateViewController(withIdentifier: "chatMessageVC") as! ChatMessagesVC
UserDefaults.standard.set(contactID, forKey: "contactID")
UserDefaults.standard.synchronize()
destinationViewController.contactID = self.contactID
let navigationController = self.window?.rootViewController as! UINavigationController
navigationController.pushViewController(destinationViewController, animated: false)
}
}
Implement below extension of UIApplication
extension UIApplication {
class func topViewController(controller: UIViewController? = UIApplication.shared.keyWindow?.rootViewController) -> UIViewController? {
if let navigationController = controller as? UINavigationController {
return topViewController(controller: navigationController.visibleViewController)
}
if let tabController = controller as? UITabBarController {
if let selected = tabController.selectedViewController {
return topViewController(controller: selected)
}
}
if let presented = controller?.presentedViewController {
return topViewController(controller: presented)
}
return controller
}
}
Please change your code to navigate to specific screen while app is in foreground.
let storyboard = UIStoryboard(name: "Main", bundle: nil)
let viewController = storyboard.instantiateViewController(withIdentifier: "chatMessageVC") as! ChatMessagesVC
UIApplication.topViewController()?.navigationController?.pushViewController(viewController, animated: false)
Hope this finds you well and let me know in case of any queries.
UPDATE
If the application is running in the foreground, iOS won't show a notification banner/alert. That's by design. But we can achieve it by using UILocalNotification as follows
if application.applicationState == .active {
var localNotification = UILocalNotification()
localNotification.userInfo = userInfo
localNotification.soundName = UILocalNotificationDefaultSoundName
localNotification.alertBody = message
localNotification.fireDate = Date()
UIApplication.shared.scheduleLocalNotification(localNotification)
}

Show a button on another controller

I have two controller, one controller is controllerOne.swift, in this I receive notifications and I need when one notification arrive, show a button on controllerTwo.swift.
My code is:
ControllerOne.swift
public func websocket(token: Any){
self.ws.open("ws://"+String(APIHOST)+":"+String(port)+"/ws?token="+String(describing: token))
self.ws.event.message = { message in
let res = self.convertToDictionary(text: message as! String)
if ((res!["notification"]) != nil) {
self.count_total_notifications_ws = self.count_total_notifications_ws! + 1
let presentView = UIApplication.shared.keyWindow?.rootViewController?.presentedViewController as? SWRevealViewController
let tabbarController = presentView?.frontViewController as? UITabBarController
if (tabbarController?.selectedIndex != 0) {
tabbarController?.tabBar.items?[0].badgeValue = self.count_total_notifications_ws?.description
}else{
//Here I need to show a showNotificationsbtn button
}
}
}
}
ControllerTwo.swift
class NewDashboardViewController: UIViewController, UITableViewDataSource, UITabBarControllerDelegate, UITableViewDelegate {
//This is the button that I need show
#IBOutlet weak var showNotificationsbtn: UIButton!
#IBAction func showNotifications(_ sender: Any) {true
self.viewDidAppear(true)
showNotificationsbtn.isHidden = true
}
}
Someone know how to I can do?
Thanks for your help.
In ViewControllerOne
if ((res!["notification"]) != nil) {
self.count_total_notifications_ws = self.count_total_notifications_ws! + 1
let presentView = UIApplication.shared.keyWindow?.rootViewController?.presentedViewController as? SWRevealViewController
let tabbarController = presentView?.frontViewController as? UITabBarController
if (tabbarController?.selectedIndex != 0) {
tabbarController?.tabBar.items?[0].badgeValue = self.count_total_notifications_ws?.description
}else{
//Here I need to show a showNotificationsbtn button
NotificationCenter.default.post(name: NSNotification.Name(rawValue: "remoNotificationArrived"), object: nil, userInfo: nil )
}
}
In ViewControllerTwo
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(true)
DispatchQueue.main.async {
NotificationCenter.default.addObserver(self, selector: #selector(self.showButton), name: NSNotification.Name(rawValue: "remoNotificationArrived"), object: nil)
}
}
func showButton(){
showNotificationsbtn.isHidden = false
}
First hide your button.
Now to unhide that button,you have multiple options.
1. Use delgate/protocol for communicating between viewcontrollers
2. You may add an observer

I am unable to post the notifications

I had searched stack over flow and all sites but unable to post notifications and I need to pass data from this class to another class I need to send the bool value to have validations can anyone help me how to pass the bool value ?
here is the code for it
radioSelected = false
NotificationCenter.default.addObserver(self, selector: #selector(paymentRadioEnable(n:)), name: NSNotification.Name.init(rawValue: "notification"), object: nil)
self.shippingmethodURL()
shippingTableView.delegate = self
shippingTableView.dataSource = self
shippingTableView.rowHeight = UITableViewAutomaticDimension
shippingTableView.estimatedRowHeight = shippingTableView.rowHeight
// Initialization code
}
func paymentRadioEnable(n:NSNotification){
}
func paymentRadioAction(button : KGRadioButton) {
_ = button.center
let centralPoint = button.superview?.convert(button.center, to:self.shippingTableView)
let indexPath = self.shippingTableView.indexPathForRow(at: centralPoint!)
if button.isSelected {
} else{
chekIndex = indexPath
radioSelected = true
self.shippingTableView.reloadData()
}
}
this is another class to which I need to post the bool value to check
#IBAction func continueButtonAction(_ sender: Any) {
NotificationCenter.default.post(name: NSNotification.Name(rawValue: "notification"), object: nil)
if radioSelected == false {
let radiobutton = SCLAlertView()
_ = radiobutton.showError("Warning", subTitle: "Please select shipping method", closeButtonTitle: "OK")
}else{
let storyboard = UIStoryboard(name: "Main", bundle: nil)
let addtoCartVC = storyboard.instantiateViewController(withIdentifier: "payment") as! PaymentMethodViewController
self.navigationController?.pushViewController(addtoCartVC, animated: true)
}
}
You can send the data into the object at the time of posting the Notification
let data : NSDictionary = ["DataKey" : "DataValue"]
NotificationCenter.default.post(name: NSNotification.Name(rawValue:
"notification"), object: data)
And after posting , you can get the same data in notification Handler.

Resources