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

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)
}

Related

How to handle push notification click in iOS Swift?

I'm trying to handle push notification click. When push notification comes in, I want to direct the user to a certain part of the application after clicking on push notification. Firstly, there is SplashScreen in the application, where it shows a TabBar as "Present Modally" by checking whether the user has previously logged in from API Call. What I want to show the user is the content of a feed in the table view in the navigation controller in the first tab of the TabBar. But I can't reach it and show it as clicked on tableview.
-SplashScreen -------> TabBar -> Navigation Controller -> Table View Controller -> ContentView(I want to show)
I tried writing the UIApplication extension but it didn't work.
Here's the extension
extension UIApplication {
class func topViewController(base: UIViewController? = (UIApplication.shared.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
}
}
I wrote the following code in didFinishLaunchingWithOptions
if let option = launchOptions {
let info = option[UIApplication.LaunchOptionsKey.remoteNotification] as? [String:Any]
if let info = info {
if info["type"] as! String == "site-notification" {
let contentVC = Destination().FeedCoontentVC
contentVC.selectedSite = (info["link"] as! String)
contentVC.title = "asd"
UIApplication.topViewController()?.present(contentVC, animated: true, completion: nil)
}
}
}
if info["type"] as! String == "site-notification" {
let tabbarSB = UIStoryboard(name: StoryBoard.tabBar, bundle: nil)
let tabbarVC = tabbarSB.instantiateViewController(withIdentifier: ViewController.TabBar) as! TabBarController
self.window?.rootViewController = tabbarVC
tabbarVC.viewControllers = [] // add your tab view controllers
for child in (tabbarVC.childViewControllers) {
if child.restorationIdentifier == "tablevc" //add restoration id to storyboard {
tabbarVC.selectedIndex = 1
let tableVC = (child.childViewControllers[0]) as! TableViewController
let contentSB = UIStoryboard(name: StoryBoard.contentSB , bundle: nil)
let contentVC = contentSB.instantiateViewController(withIdentifier: ViewController.contentVC ) as! FeedCoontentVC
contentVC.selectedSite = (info["link"] as! String)
contentVC.title = "asd"
tableVC.navigationController?.pushViewController(contentVC, animated: false)
}
}
}

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]?
}

How can I handle FCM when my app is not running?

I can receive FCM when app is active or background.
But can't receive when not running.
How can I receive FCM when application state not running?
in didFinishLaunchingWithOptions.There is a parameter launchOptions which contains push notification.Store the data and handle it in firstViewController you open.I did it by writing these lines in appDelegate I used swiftyJson and take userPush in singleton class to 1:
guard let pushNotification = launchOptions?
[UIApplicationLaunchOptionsKey.remoteNotification] else { return true }
print(pushNotification)
let data = JSON(pushNotification)
UserDataSingleton.sharedInstance.notificationData = data
UserDataSingleton.sharedInstance.userPush = 1
Now in my splashViewController I did:
override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(true)
if (UserDataSingleton.sharedInstance.userPush == 1){
UserDataSingleton.sharedInstance.userPush = 0
let appDelegate = UIApplication.shared.delegate as! AppDelegate
appDelegate.handlePushNavigation(userInfo: UserDataSingleton.sharedInstance.notificationData!)
}
else{
}
}
handlePushNotification is a func i have written for all 3 cases in AppDelegate:
//MARK: - HAndle Push Notification Methods
extension AppDelegate{
func handlePush(userInfo : JSON){
handlePushNavigation(userInfo: userInfo )
}
func handlePushNavigation(userInfo : JSON){
let storybard = UIStoryboard(name: "Main", bundle: nil)
switch userInfo["type"].intValue {
case 1:
guard let VC = storybard.instantiateViewController(withIdentifier: "ChatViewController") as? ChatViewController else{return}
navigateToVc(vc: VC)
default:break
}
}
func navigateToVc(vc:UIViewController){
ez.topMostVC?.presentVC(vc)
}
}

Onesignal push notification only accept English language ones in Swift 3

I have 2 segment in onesignal panel bir EN another FR but in my app have language change and people can change app language everytime in app inside , when I send push from one signal I send by segments but i want to add appdelegate inside language section if user language is English only must be alert English pushes , if user language France only must be alert France pushes.
In my app side language selection string is
let appcurrentlanguage = prefs.value(forKey: "lang") as! String
How can I do it ?
My codes under below
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool {
// Override point for customization after application launch.
//HERE APP LANGUAGE
let appcurrentlanguage = prefs.value(forKey: "lang") as! String
GMSServices.provideAPIKey("xxx")
OneSignal.initWithLaunchOptions(launchOptions, appId: "xxx") { (result) in
let payload = result?.notification.payload
if let additionalData = payload?.additionalData, let actionSelected = additionalData["comes"] as? String {
if actionSelected == "1" {
let mainStoryboardIpad : UIStoryboard = UIStoryboard(name: "Main", bundle: nil)
let initialViewControlleripad : UIViewController = mainStoryboardIpad.instantiateViewController(withIdentifier: "ViewController") as! ViewController
self.window = UIWindow(frame: UIScreen.main.bounds)
self.window?.rootViewController = initialViewControlleripad
self.window?.makeKeyAndVisible()
}else if actionSelected == "2" {
if let newsID = additionalData["id"] as? String {
prefs.setValue(newsID, forKeyPath: "gelenID")
prefs.synchronize()
}
if let newsType = additionalData["type"] as? String {
prefs.setValue(newsType, forKeyPath: "newsType")
prefs.synchronize()
}
}
}
}
sleep(2)
return true
}

Swift: Unable to refresh rootViewController with pushNotification's info when the application is running in the background

I am implementing a news App. Once I receive a notification with a breaking news id and message, i am supposed to open the app and refresh the rootViewController with the id received in the push notifications.
Things work fine when the application is not running in the background while an issue arises when the application is running in the background.
In the AppDelegate.swift i have set the didReceiveRemoteNotifications as follows
func application(application: UIApplication, didReceiveRemoteNotification userInfo: [NSObject : AnyObject], fetchCompletionHandler completionHandler: (UIBackgroundFetchResult) -> Void) {
strNotificationId = (userInfo["id"] as? String)!
}
In my applicationWillEnterForeground i have called the rootViewController method activeController.request() that reloads the page with the id received as shown below
func applicationWillEnterForeground(application: UIApplication) {
print("Push notifications are not supported in the iOS Simulator.")
if let navigationController = window?.rootViewController as?UINavigationController {
navigationController.popToRootViewControllerAnimated(false)
}
let navigationController = window?.rootViewController as? UINavigationController
let activeController = navigationController!.visibleViewController as! FeedTableViewController
activeController.request("http://example.net/rsssite.xml")
}
My rootViewController implements the request("http://example.net/rsssite.xml") method. The request method reads the notification id as shown below
delegate.notificationId = [NSObject: AnyObject]()
notificationId = delegate.strNotificationId as String
My issue is showing that whenever i click on my notification received in the IOS notifications list, and when the app happens to be running in the backend, the request("http://example.net/rsssite.xml") that is implemented in the rootViewController is neither being triggered in my rootViewController viewDidLoad nor in applicationWillEnterForeground
Below is a full the full code of my request(url) method in my rootViewController which might be useless for your feedback
//Method to request and parser the rss feeds
func request(urlLink: String){
forceError = false
let alert = UIAlertController(title: nil, message: Util.getMessage("loading"), preferredStyle: .Alert)
alert.view.tintColor = UIColor.blackColor()
let loadingIndicator: UIActivityIndicatorView = UIActivityIndicatorView(frame: CGRectMake(10, 5, 50, 50)) as UIActivityIndicatorView
loadingIndicator.hidesWhenStopped = true
loadingIndicator.activityIndicatorViewStyle = UIActivityIndicatorViewStyle.Gray
loadingIndicator.startAnimating();
alert.view.addSubview(loadingIndicator)
self.view.addSubview(alert.view)
self.view.bringSubviewToFront(loadingIndicator)
let viewsDictionary = ["alert":alert.view]
let loadingMetrics = ["xDimension":(self.view.bounds.size.width - alert.view.frame.width ) / 2, "yDimension": (self.view.bounds.size.height - alert.view.frame.height ) / 2]
let horizontalLoadingConstraint = NSLayoutConstraint.constraintsWithVisualFormat("H:|-50-[alert]", options: NSLayoutFormatOptions.AlignAllCenterX, metrics:loadingMetrics, views: viewsDictionary)
let verticalLoadingConstraint = NSLayoutConstraint.constraintsWithVisualFormat("V:|-100-[alert]", options: NSLayoutFormatOptions.AlignAllCenterX, metrics: loadingMetrics, views: viewsDictionary)
self.view.addConstraints(horizontalLoadingConstraint)
self.view.addConstraints(verticalLoadingConstraint)
presentViewController(alert, animated: true, completion: nil)
let delegate = UIApplication.sharedApplication().delegate as! AppDelegate
let notificationsData = delegate.notificationId
for aButton in notificationsData {
let key = aButton.0
let value = aButton.1
if(key == "id"){
notificationId = value as! String
}
}
delegate.notificationId = [NSObject: AnyObject]()
notificationId = delegate.strNotificationId as String
//notificationId = "17579"
if (notificationId != "" && self.directLoad==false) {
//var notificationURLLink = defaults.stringForKey("notificationId")
let diceRoll = Int(arc4random_uniform(9999) + 1) as NSNumber
let notificationURLLink = "http://example.net/"+notificationId+"-rss.xml?_=" + diceRoll.stringValue
let queue = NSOperationQueue()
queue.addOperationWithBlock() {
let url = NSURL(string: notificationURLLink)
let feedParser = MWFeedParser(feedURL: url)
feedParser.delegate = self
feedParser.parse()
NSOperationQueue.mainQueue().addOperationWithBlock() {
let item = self.feedItems[0] as MWFeedItem
self.slectedItems = item
self.sideBar.showSidebar(false)
self.performSegueWithIdentifier("DetailsViewControllerSegue", sender: self)
self.dismissViewControllerAnimated(false, completion: nil)
self.notificationId = ""
self.directLoad = true
}
}
}else{
let queue = NSOperationQueue()
queue.addOperationWithBlock() {
let url = NSURL(string: urlLink)
let feedParser = MWFeedParser(feedURL: url)
feedParser.delegate = self
feedParser.parse()
NSOperationQueue.mainQueue().addOperationWithBlock() {
self.tableView.reloadData()
self.dismissViewControllerAnimated(false, completion: nil)
}
}
}
delegate.strNotificationId = ""
}
Thank you
Try move your code in applicationWillEnterForeground to didReceiveRemoteNotification. The process should be done right after you receive the notification.
func application(application: UIApplication, didReceiveRemoteNotification userInfo: [NSObject : AnyObject], fetchCompletionHandler completionHandler: (UIBackgroundFetchResult) -> Void) {
strNotificationId = (userInfo["id"] as? String)!
print("Push notifications are not supported in the iOS Simulator.")
if let navigationController = window?.rootViewController as?UINavigationController {
navigationController.popToRootViewControllerAnimated(false)
}
let navigationController = window?.rootViewController as? UINavigationController
let activeController = navigationController!.visibleViewController as! FeedTableViewController
activeController.request("http://example.net/rsssite.xml")
}
Also please check which function execute first when you open the notification while the apps in background, applicationWillEnterForeground or didReceiveRemoteNotification ?

Resources