How to open Viewcontroller from Appdelegate? - ios

I use firebase to send a message to ios device, I debug, I have received data payload in Appdelegate in func
func application(_ application: UIApplication, didReceiveRemoteNotification userInfo: [AnyHashable: Any]) {
if let messageID = userInfo[gcmMessageIDKey] {
print("Message ID: \(messageID)")
}
I want to do how to open different view controllers based on that data, which means that when I click on the message, I will go to the corresponding view controllers.
I used the code below in Appdelegate but failed
let sb = UIStoryboard(name: "Main", bundle: nil)
let otherVC = sb.instantiateViewController(withIdentifier: "UserNotLoginViewController") as! UserNotLoginViewController
self.window?.rootViewController = otherVC;

when you recive notification in didReceiveRemoteNotification delegate then call the function to pushview to nextviewcontroller.
func application(_ application: UIApplication, didReceiveRemoteNotification
data: [AnyHashable : Any]) {
let state: UIApplicationState = UIApplication.shared.applicationState
if state == .background {
// background
pushToPage(data: data)
}
}
func pushToPage(data:[AnyHashable : Any]){
if let appDelegate = UIApplication.shared.delegate as? AppDelegate,
let window = appDelegate.window {
let storyBoard : UIStoryboard = UIStoryboard(name: "Main", bundle:nil)
let nextViewController =
storyBoard.instantiateViewController(withIdentifier: "NextViewController") as! NextViewController
window.rootViewController = nextViewController
}
}

Declare this function in your appDelegate and then use it to change rootViewController.
public func makeRootVC(vcName : String) {
let vc = UIStoryboard(name: "Main", bundle: Bundle.main).instantiateViewController(withIdentifier: vcName)
let navigation = UINavigationController(rootViewController: vc)
navigation.navigationBar.isHidden = true
self.window?.rootViewController = navigation
}
usage:
self.makeRootVC("YourViewControllerStoryboardID")

Here it is
func handlePushNotification(userInfo: [String: Any]) {
guard let notificationType = userInfo["nt"] as? String else {
return
}
if notificationType.toInt() == 1 {
self.navigateToViewController1()
} else if notificationType.toInt() == 2 {
self.navigateToViewController2()
}
}
And for navigation, you can use this below function
fileprivate func navigateToViewController1() {
if let rootViewController = self.window?.rootViewController as? UINavigationController {
if let _ = rootViewController.topViewController as? VC1 {
let vc = AppStoryboard.Main.viewController(viewControllerClass: VC3.self)
rootViewController.pushViewController(vc, animated: true)
}
}
}
fileprivate func navigateToViewController2() {
if let rootViewController = self.window?.rootViewController as? UINavigationController {
if let homeVC = rootViewController.topViewController as? VC2 {
}
}
}
Still, you face any issue so please let me know.

Related

How to redirect to a particular ViewController on Notification Click?

I want to redirect to particular screen once user tap the push notification. and for that i have tried below code and the view presented successfully but i'm unable to use the app navigation flow after setting the particular view as RootViewController.
func application(_ application: UIApplication, didReceiveRemoteNotification userInfo: [AnyHashable : Any], fetchCompletionHandler completionHandler: #escaping (UIBackgroundFetchResult) -> Void) {
//let alertMsg = (userInfo["aps"] as! NSDictionary)["alert"] as! NSDictionary
//let payload = userInfo["sendbird"] as! NSDictionary
//APS DATA
if let apsData = userInfo["aps"] as? Dictionary<String, Any> {
print(apsData)
}
//USER DATA
if let userData = userInfo["user_details"] as? Dictionary<String, Any> {
print(userData)
}
//NOTIFICATION DATA
if let notificationData = userInfo["notification_details"] as? Dictionary<String, Any> {
print(notificationData)
let nNotificationType = notificationData["notification_type"] as! Int
let nNotificationID = notificationData["notification_id"] as! Int
}
let storyBoard : UIStoryboard = UIStoryboard(name: "Group", bundle:nil)
if Device.IS_IPHONE_X {
let controller = storyBoard.instantiateViewController(withIdentifier: "GroupInfoViewController_iPhoneX") as! GroupInfoViewController
let navController = UINavigationController.init(rootViewController: controller)
}
else {
let controller = storyBoard.instantiateViewController(withIdentifier: "GroupInfoViewController_iPhone8") as! GroupInfoViewController
let navController = UINavigationController.init(rootViewController: controller)
}
// Your custom way to parse data
completionHandler(UIBackgroundFetchResult.newData)
}
Please guide me how can i use the normal app navigation after move to particular ViewController on notification tap. same like android app.
Please guide me
After some R&D, i found the working solution to move to particular controller on notification tap over PRESENTED or PUSHED ViewController.
Here is the working code.
func application(_ application: UIApplication, didReceiveRemoteNotification userInfo: [AnyHashable : Any], fetchCompletionHandler completionHandler: #escaping (UIBackgroundFetchResult) -> Void) {
DispatchQueue.main.asyncAfter(deadline: .now() + 1) { // change your delay here
let storyboard = UIStoryboard(name: "Group", bundle: nil)
if Device.IS_IPHONE_X {
let vc = storyboard.instantiateViewController(withIdentifier: "GroupInfoViewController_iPhoneX") as! GroupInfoViewController
vc.nNotificationID = notificationID
if let topVC = UIApplication.getTopViewController() {
topVC.navigationController?.pushViewController(vc, animated: false)
}
}
else {
let vc = storyboard.instantiateViewController(withIdentifier: "GroupInfoViewController_iPhone8") as! GroupInfoViewController
vc.nNotificationID = notificationID
if let topVC = UIApplication.getTopViewController() {
topVC.navigationController?.pushViewController(vc, animated: false)
}
}
}
}
Extension
public extension UIApplication {
class func getTopViewController(base: UIViewController? = UIApplication.shared.keyWindow?.rootViewController) -> UIViewController? {
if let nav = base as? UINavigationController {
return getTopViewController(base: nav.visibleViewController)
} else if let tab = base as? UITabBarController, let selected = tab.selectedViewController {
return getTopViewController(base: selected)
} else if let presented = base?.presentedViewController {
return getTopViewController(base: presented)
}
return base
}
}
Create a function like this:
func handleNotification(userInfo: [String:Any]) {
DispatchQueue.main.asyncAfter(deadline: .now() + 1.0) {
if let mainNav = AppDelegate.shared.window?.rootViewController as? UINavigationController {
let storyboard = UIStoryboard(name: "Main", bundle: nil)
let vc = storyboard.instantiateViewController(withIdentifier: "YourVC") as! YourVC
mainNav.pushViewController(vc, animated: false)
}
}
}
You can also pass information in userInfo If you want to perform certain operations or just removed that parameter.
And call this function in your this method
func application(_ application: UIApplication, didReceiveRemoteNotification userInfo: [AnyHashable : Any], fetchCompletionHandler completionHandler: #escaping (UIBackgroundFetchResult) -> Void) {
print("APNs received with: \(userInfo)")
self.handleNotification(userInfo: userInfo as! [String : Any])
}
let vc = storyboard.instantiateViewController(withIdentifier: "YourVC") as!
YourVC
mainNav.pushViewController(vc, animated: false)
use push or present to pass one view controller to another

How to Navigate another page when application launch first time in swift 3

I am implementing an iOS app. when User Launch the application first time, I need to navigate to another page and from next time onwards I need to navigate Home Page. How to do this task in iOS swift?
Updated: if you get this error: "Value of type 'AppDelegate' has no member 'window' ".
Answer is : you have to write your codes inside of SceneDelegate.swift
func scene(_ scene: UIScene, willConnectTo session: UISceneSession, options connectionOptions: UIScene.ConnectionOptions) {
let defaults = UserDefaults.standard
guard let a=UserDefaults.standard.object(forKey: "wantOverview") else {
let storyBoard = UIStoryboard.init(name: "Main", bundle: nil)
let initialVc = storyBoard.instantiateViewController(withIdentifier: "firstTimeViewController") as! firstTimeViewController
self.window?.rootViewController = initialVc
self.window?.makeKeyAndVisible()
defaults.set(false, forKey: "wantOverview")
return
}
let storyBoard = UIStoryboard.init(name: "Main", bundle: nil)
let initialVc = storyBoard.instantiateViewController(withIdentifier: "ViewController") as! ViewController
self.window?.rootViewController = initialVc
self.window?.makeKeyAndVisible()
guard let _ = (scene as? UIWindowScene) else { return }
}
The following solution to this common problem assumes that
At the outset — in this example, that's before the "username" key has been set in the user defaults — we want to launch to the storyboard's Initial View Controller (where the user is supposed to set the "username" key).
Thereafter, we always want to launch to a different view controller in the storyboard, whose identifier in this example is "root".
It's very short and simple!
class AppDelegate: UIResponder, UIApplicationDelegate {
var window: UIWindow?
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool {
if let rvc = self.window?.rootViewController {
if UserDefaults.standard.object(forKey:"username") as? String != nil {
self.window!.rootViewController = rvc.storyboard!.instantiateViewControllerWithIdentifier("root")
}
}
return true
}
}
This can be achieved using userdefaults. Just make a new controller like SplashViewController which will only redirect to desired ViewController.
import UIKit
class SplashViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
decideWhichScreenToShow()
}
// This logic will do
func decideWhichScreenToShow(){
if !UserDefaults.standard.bool(forKey: "your_key"){
// This will execute first time, in that controller's viewDidLoad() make this true
}else{
}
}
}
You can write same logic in Appdelegate.
Create func for checking that if your key in UserDefaults or not using this link & Then check by if...else
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool {
window = UIWindow(frame: UIScreen.main.bounds)
window?.makeKeyAndVisible()
if isKeyPresentInUserDefaults(key: "isFirstTime") {
let home = HomeVC()
window?.rootViewController = home
} else {
let login = LoginVC()
window?.rootViewController = login
UserDefaults.standard.set(true, forKey: "isFirstTime")
}
return true
}
func isKeyPresentInUserDefaults(key: String) -> Bool {
return UserDefaults.standard.object(forKey: key) != nil
}
Try this:
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool {
if firstTime = UserDefaults.standard.object(forKey: "firstTime")
{
let storyboard = UIStoryboard(name: "SecondStoryBoard", bundle: nil)
let MainView = storyboard.instantiateViewController(withIdentifier: "SecondTimeViewController") as! SecondTimeViewController
let navController = UINavigationController.init(rootViewController: MainView)
self.window?.rootViewController = navController
}
else
{
UserDefaults.standard.set(true, forKey: "firstTime")
let storyboard = UIStoryboard(name: "FirstStoryBoard", bundle: nil)
let MainView = storyboard.instantiateViewController(withIdentifier: "FirstTimeViewController") as! FirstTimeViewController
let navController = UINavigationController.init(rootViewController: MainView)
self.window?.rootViewController = navController
}
}
You may use change the didFinishLaunchingWithOptions method in AppDelegate to achieve this.
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool {
self.window = UIWindow(frame: UIScreen.main.bounds)
guard let firstTime = UserDefaults.standard.object(forKey: "IsFirstTime") else {
UserDefaults.standard.set(true, forKey: "IsFirstTime")
let storyBoard = UIStoryboard.init(name: "FirstStoryBoard", bundle: nil)
let initialVc = storyBoard.instantiateViewController(withIdentifier: "FirstControllerId") as! FirstTimeViewController
self.window?.rootViewController = initialVc
self.window?.makeKeyAndVisible()
return true
}
let storyBoard = UIStoryboard.init(name: "Main", bundle: nil)
let initialVc = storyBoard.instantiateViewController(withIdentifier: "NormalInitialController") as! ViewController
self.window?.rootViewController = initialVc
self.window?.makeKeyAndVisible()
return true
}
You check if the value for key, "IsFirstTime", exists in UserDefaults. There won't be a value if the app is launching for the first time.
In this case, you can launch your FirstTimeViewController and set a value to the key in UserDefaults.
If a value exists in UserDefaults, just initiate your normal ViewController.

Swift - How to open specific view controller when push notification received?

I got stuck specific view controller is not move when I tap on push notification alert when application is not open stage totally.
Here is my code:
func application(_ application: UIApplication, didReceiveRemoteNotification userInfo: [AnyHashable: Any], fetchCompletionHandler completionHandler: #escaping (UIBackgroundFetchResult) -> Void) {
/*
fetch and add push notification data
*/
goAnotherVC()
}
func goAnotherVC() {
if (application.applicationState == UIApplicationState.active) {
/* active stage is working */
} else if (application.applicationState == UIApplicationState.inactive || application.applicationState == UIApplicationState.background) {
if (type == "1" || type == "2") {
let storyboard: UIStoryboard = UIStoryboard(name: "MyAppointments", bundle: nil)
let apptVC = storyboard.instantiateViewController(withIdentifier: "NotificationDetailViewController") as! NotificationDetailViewController
let navigationController = UINavigationController.init(rootViewController: apptVC)
self.window?.rootViewController = navigationController
self.window?.makeKeyAndVisible()
} else if (type == "3") {
let storyboard: UIStoryboard = UIStoryboard(name: "MyAppointments", bundle: nil)
let apptVC = storyboard.instantiateViewController(withIdentifier: "NotificationDetailViewController") as! NotificationDetailViewController
let navigationController = UINavigationController.init(rootViewController: apptVC)
self.window?.rootViewController = navigationController
self.window?.makeKeyAndVisible()
} else if (type == "4") {
let storyboard: UIStoryboard = UIStoryboard(name: "Enquiry", bundle: nil)
let enqVC = storyboard.instantiateViewController(withIdentifier: "EnquiryDetailViewController") as! EnquiryDetailViewController
let navigationController = UINavigationController.init(rootViewController: enqVC)
self.window?.rootViewController = navigationController
self.window?.makeKeyAndVisible()
}
}
}
I can get notification and tap to move specific VC when application is active. Please help me what I am missing.
Swift 5
Simply, implement the following function which will be called when the user clicked on the notification.
In AppDelegate:
// This method is called when user clicked on the notification
func userNotificationCenter(_ center: UNUserNotificationCenter, didReceive response: UNNotificationResponse, withCompletionHandler completionHandler: #escaping () -> Void)
{
// Do whatever you want when the user tapped on a notification
// If you are waiting for specific data from the notification
// (e.g., key: "target" and associated with "value"),
// you can capture it as follows then do the navigation:
// You may print `userInfo` dictionary, to see all data received with the notification.
let userInfo = response.notification.request.content.userInfo
if let targetValue = userInfo["target"] as? String, targetValue == "value"
{
coordinateToSomeVC()
}
completionHandler()
}
private func coordinateToSomeVC()
{
guard let window = UIApplication.shared.keyWindow else { return }
let storyboard = UIStoryboard(name: "YourStoryboard", bundle: nil)
let yourVC = storyboard.instantiateViewController(identifier: "yourVCIdentifier")
let navController = UINavigationController(rootViewController: yourVC)
navController.modalPresentationStyle = .fullScreen
// you can assign your vc directly or push it in navigation stack as follows:
window.rootViewController = navController
window.makeKeyAndVisible()
}
Note:
If you navigate to a specific controller based on the notification, you should care about how you will navigate back from this controller because there are no controllers in your stack right now. You must instantiate the controller you will back to. In my case, when the user clicked back, I instantiate the home controller and make it the app root again as the app will normally start.
When you app is in closed state you should check for launch option in
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool { }
and call your API.
Example:
if let option = launchOptions {
let info = option[UIApplicationLaunchOptionsKey.remoteNotification]
if (info != nil) {
goAnotherVC()
}
}
Swift 5, iOS 13 -
Since iOS 13 "window" is available in SceneDelegate. But the didReceiveNotification method is still present in AppDelegate.
So you have to first access the window from SceneDelegate
let window = (UIApplication.shared.connectedScenes.first?.delegate as? SceneDelegate)?.window
Now You can set the rootViewController property of the window
window.rootViewController = viewControllerObject
window.makeKeyAndVisible()

Launch ViewController from AppDelegate, tabbar missing from bottom

I am developing my first app, where I am trying to manage a session in my app, where I am trying to check if user has logged in recently. If user has logged in recently, then I want to skip login page, and move him to next page.
Here what I am doing, however I am unable to proceed forward
func application(application: UIApplication, didFinishLaunchingWithOptions launchOptions: [NSObject: AnyObject]?) -> Bool {
let mainStoryboard: UIStoryboard = UIStoryboard(name: "Main", bundle: nil)
let viewControllerB = mainStoryboard.instantiateViewControllerWithIdentifier("account") as! AccountDetails
let navController = UINavigationController(rootViewController: viewControllerB)
let appDelegate = (UIApplication.sharedApplication().delegate as! AppDelegate)
appDelegate.window?.rootViewController = viewControllerB
let vc = self.window?.rootViewController
UIApplication.sharedApplication().keyWindow?.rootViewController?.presentViewController(navController, animated: true, completion: nil)
return true
}
I am able to get to the desired view,but the Tabbar on that view is missng. I want to restore the same.
Second Screen (My Account which I want to show)
class AccountDetails: UIViewController,UIImagePickerControllerDelegate,UINavigationControllerDelegate {
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
// Initialize Tab Bar Item
tabBarItem = UITabBarItem(title: "Account Details", image: UIImage(named: "Account.png"), tag: 1)
}
}
Replace the line self.window?.rootViewController?.presentViewController(navController, animated: true, completion: nil)
with
self.window?.rootViewController = navController
Change window's rootViewController:
func application(application: UIApplication, didFinishLaunchingWithOptions launchOptions: [NSObject: AnyObject]?) -> Bool {
if(User loggin last time) {
let yourTargetViewController = UIViewController()
yourTargetViewController.view.backgroundColor = UIColor.redColor()
self.window?.rootViewController = yourTargetViewController
}
return true
}
This is the working solution
let mainStoryboard: UIStoryboard = UIStoryboard(name: "Main", bundle: nil)
let viewControllerB = mainStoryboard.instantiateViewControllerWithIdentifier("tabbar") as! UITabBarController
let navController = UINavigationController(rootViewController: viewControllerB)
let appDelegate = (UIApplication.sharedApplication().delegate as! AppDelegate)
appDelegate.window?.rootViewController = viewControllerB
UIApplication.sharedApplication().keyWindow?.rootViewController?.presentViewController(navController, animated: true, completion: nil)
//tabbar is the storyboard ID of tabbarcontroller

Show uiviewcontroller from remote notification when app is quitted

I'm trying to show a specific viewcontroller when I receive a remote push notification. I've added all of my code into the method didReceiveRemoteNotification:
func application(application: UIApplication, didReceiveRemoteNotification userinfo: [NSObject: AnyObject])
I've added following code:
func application(application: UIApplication, didReceiveRemoteNotification userInfo: [NSObject : AnyObject]) {
let code = (userInfo["aps"] as! [String: AnyObject])
// Call to retrieve blog
if let blog = code["b"] as? NSNumber {
let blogId = blog as! Int
// Show blog from notification
let mainStoryboard: UIStoryboard = UIStoryboard(name: "Main",bundle: nil)
var controller = mainStoryboard.instantiateViewControllerWithIdentifier("blogCtrl") as! BlogController
controller.blogId = blogId
var rootController = mainStoryboard.instantiateViewControllerWithIdentifier("navCtrl1") as! UINavigationController
self.window?.rootViewController = rootController
rootController.pushViewController(controller, animated: true)
self.window?.makeKeyAndVisible()
}
if let tonic = code["t"] as? NSNumber {
let mainStoryboard: UIStoryboard = UIStoryboard(name: "Main",bundle: nil)
var controller = mainStoryboard.instantiateViewControllerWithIdentifier("tonicDetail") as! TonicDetailController
controller.tonicId = tonic as! Int
var rootController = mainStoryboard.instantiateViewControllerWithIdentifier("navCtrl1") as! UINavigationController
self.window?.rootViewController = rootController
rootController.pushViewController(controller, animated: true)
self.window?.makeKeyAndVisible()
}
if let gin = code["g"] as? NSNumber {
let mainStoryboard: UIStoryboard = UIStoryboard(name: "Main",bundle: nil)
var controller = mainStoryboard.instantiateViewControllerWithIdentifier("GinDetail") as! GinDetailController
controller.ginId = gin as! Int
var rootController = mainStoryboard.instantiateViewControllerWithIdentifier("navCtrl1") as! UINavigationController
self.window?.rootViewController = rootController
rootController.pushViewController(controller, animated: true)
self.window?.makeKeyAndVisible()
}
}
When the app is in background everything works but when the app is quited, and I receive a remote notification it only starts up the application.
Is there a method which can be called if the application was quited before?
When your app launches after quitting the app, in the meanwhile you got remote notification, then didFinishLaunchingWithOptions is first method in AppDelegate which fires on launching the app, check whether you have received any notification or not and perform your operation accordingly.
You must look for this method in app delegate:-
-(BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
Now check whether your app has received any push notification or not
UILocalNotification *notification = [launchOptions objectForKey:UIApplicationLaunchOptionsRemoteNotificationKey];
if (notification) {
NSLog(#"app received a notification %#",notification);
[self application:application didReceiveRemoteNotification:(NSDictionary*)notification];
}else{
NSLog(#"app did not receive a notification");
}

Resources