79

I am trying to create a push notification which determines which view to open according to information obtained from the push.

I have managed to get the information from the push, but I am now struggling to get the view to open

Looking at other stack overflow questions I have the following currently:

App Delegate Did finish loading:

     //Extract the notification data
    if let notificationPayload = launchOptions?[UIApplicationLaunchOptionsRemoteNotificationKey] as? NSDictionary {
        // Get which page to open
        let viewload = notificationPayload["view"] as? NSString
        let storyBoard : UIStoryboard = UIStoryboard(name: "Main", bundle:nil)
        //Load correct view
        if viewload == "circles" {

            var viewController = self.window?.rootViewController?.storyboard?.instantiateViewControllerWithIdentifier("Circles") as! UIViewController
            self.window?.rootViewController = viewController

                        }
    }

Currently this is failing on the var ViewController = self... line.

Lister
  • 1,098
  • 1
  • 9
  • 19
  • Check [this](http://stackoverflow.com/a/36158926/1223728) answer – Borzh Mar 22 '16 at 15:44
  • 2
    I tried this and it works as far as opening "Circles" view controller. But let say "Circles" view controller is itself within a sub tree of navigation tree of my app. How do I make sure that once I open my app in "Circles" view controller, all the back navigations work? Right now, it opens "Circles" view controller without showing the back navigation. – Dogahe Aug 29 '16 at 16:02

11 Answers11

119

You have to set ViewController StoryBoardId property as below image.

enter image description here

open viewController using coding as below in swift

func application(application: UIApplication, didFinishLaunchingWithOptions launchOptions: [NSObject: AnyObject]?) -> Bool {

         let mainStoryboardIpad : UIStoryboard = UIStoryboard(name: "Main", bundle: nil)
         let initialViewControlleripad : UIViewController = mainStoryboardIpad.instantiateViewControllerWithIdentifier("Circles") as UIViewController
         self.window = UIWindow(frame: UIScreen.main.bounds)
         self.window?.rootViewController = initialViewControlleripad
         self.window?.makeKeyAndVisible()

         return true
    }

For iOS 13+ (based on an article by dev2qa)
Open SceneDelegate.swift and add following

func scene(_ scene: UIScene, willConnectTo session: UISceneSession, options connectionOptions: UIScene.ConnectionOptions) {

    // If this scene's self.window is nil then set a new UIWindow object to it.
    self.window = self.window ?? UIWindow()

    // Set this scene's window's background color.
    self.window!.backgroundColor = UIColor.red

    // Create a ViewController object and set it as the scene's window's root view controller.
    self.window!.rootViewController = ViewController()

    // Make this scene's window be visible.
    self.window!.makeKeyAndVisible()

    guard scene is UIWindowScene else { return }
}

There is an open-source navigation utility which attempts to make this easier. Example

Ky -
  • 30,724
  • 51
  • 192
  • 308
Kirit Modi
  • 23,155
  • 15
  • 89
  • 112
  • 2
    But this is creating a new instance of the controller. I want the controller that already has state. Or am I missing something? – rjcarr Jun 28 '17 at 07:39
  • app delegate method is not called when receive a User Notification , why? – ArgaPK Apr 12 '18 at 08:27
  • Same for swift vertion but only syntax change. – Kirit Modi Jul 28 '18 at 16:23
  • but viewDidLoad is not worked in that viewController ("circles") – Rashid KC Sep 27 '18 at 06:19
  • 1
    latest: put the answer code in `SceneDelegate.scene(_ scene: UIScene, willConnectTo session: UISceneSession, options connectionOptions: UIScene.ConnectionOptions) ` (FYI, i created a [navigation util](https://github.com/hovermind/ios-wiki/blob/master/docs/util/navigation-util.md) based on [this](https://www.dev2qa.com/how-to-set-application-root-view-controller-programmatically-in-xcode-11/)) – MD TAREQ HASSAN Dec 13 '19 at 05:06
55

Swift 3:

This is my preferred approach when presenting a new viewController from the current viewController through the AppDelegate. This way you don't have to completely tear down your view hierarchy when handling a push notification or universal link

if let controller = UIStoryboard(name: "Main", bundle: nil).instantiateViewController(withIdentifier: "someController") as? SomeController {
    if let window = self.window, let rootViewController = window.rootViewController {
        var currentController = rootViewController
        while let presentedController = currentController.presentedViewController {
            currentController = presentedController
        }
        currentController.present(controller, animated: true, completion: nil)
    }
}
Quy Bui
  • 679
  • 5
  • 5
  • 2
    This is the best approach, You can dismiss this view as well self.dismiss(animated: true, completion: nil) – Imtee Jan 15 '18 at 05:29
  • @Imtee: dismiss the presented viewController you mean? – Lohith Korupolu Feb 19 '18 at 11:57
  • @LohithKorupolu, if u want to move from present view controller. – Imtee Feb 21 '18 at 03:11
  • This worked for me, well done! I put this in the `applicationWillEnterForeground` method of my appDelegate to force restart my app at the splash screen when being reopened (that way, I can properly refresh my data and UI by maintaining a simple 'all through one path' model lol :) Thanks! – Justin Jul 06 '18 at 03:17
  • Perfect, Thank you. – Mostafa Sh Oct 31 '18 at 13:10
  • I used the accepted answer previously and ran into problems managing the view hierarchy, and this was exactly what we needed! – Z. Bagley Feb 22 '19 at 03:52
  • @Imtee didn't dismiss in my case .. can you help? – Krutika Sonawala Aug 29 '19 at 08:27
  • let controller = AppStoryboard.FriendsGroups.viewController(viewControllerClass: RTCVideoViewController.self) controller.isCallReceived = true controller.notification = notification if let window = self.window, let rootViewController = window.rootViewController { var currentController = rootViewController while let presentedController = currentController.presentedViewController { currentController = presentedController } currentController.present(controller, animated: true, completion: nil) } – Krutika Sonawala Aug 29 '19 at 08:28
  • 1
    @KrutikaSonawala , Use self.dismiss(animated: true, completion: nil) hope that work – Imtee Sep 03 '19 at 11:11
  • Best solution!! – Utku Dalmaz May 22 '20 at 06:50
14

Swift 3

To present the view together with the navigation controller:

let storyboard = UIStoryboard(name: "Main", bundle: nil)
let viewController = storyboard.instantiateViewController(withIdentifier :"InboxViewController") as! InboxViewController
let navController = UINavigationController.init(rootViewController: viewController)

   if let window = self.window, let rootViewController = window.rootViewController {
       var currentController = rootViewController
       while let presentedController = currentController.presentedViewController {
           currentController = presentedController
        }
           currentController.present(navController, animated: true, completion: nil)
   }
8

First Initialize the window

self.window = UIWindow(frame: UIScreen.mainScreen().bounds)
let storyBoard = UIStoryboard(name: "Main", bundle: nil)

For setting rootViewController inside AppDelegate Class

let viewController = storyBoard.instantiateViewControllerWithIdentifier("Circles") as UIViewController
self.window?.rootViewController = viewController
self.window?.makeKeyAndVisible()
Pushpendra
  • 1,492
  • 1
  • 17
  • 33
iDhaval
  • 3,175
  • 1
  • 11
  • 21
6

There is a swift 4 version

func application(_ application: UIApplication, 
didFinishLaunchingWithOptions launchOptions: 
[UIApplicationLaunchOptionsKey: Any]?) -> Bool {
let mainStoryboardIpad : UIStoryboard = UIStoryboard(name: "Main", bundle: nil)
let initialViewControlleripad : UIViewController = mainStoryboardIpad.instantiateViewController(withIdentifier: "Circles") as UIViewController
            self.window = UIWindow(frame: UIScreen.main.bounds)
            self.window?.rootViewController = initialViewControlleripad
            self.window?.makeKeyAndVisible()

return true}
Community
  • 1
  • 1
sixsixsix
  • 1,768
  • 21
  • 19
5

In Swift 3

        let mainStoryboard : UIStoryboard = UIStoryboard(name: StorybordName, bundle: nil)
        let initialViewControlleripad : UIViewController = mainStoryboard.instantiateViewController(withIdentifier: identifierName) as UIViewController
        if let navigationController = self.window?.rootViewController as? UINavigationController
        {
            navigationController.pushViewController(initialViewControlleripad, animated: animation)
        }
        else
        {
            print("Navigation Controller not Found")
        }
Amul4608
  • 1,390
  • 14
  • 30
3

I'd say creating UIWindow each time you want to change rootViewController is bad idea. After couple changes of rootVC (using upper solution) you are gonna have many UIWindows in your app at one time.

In my opinion better solution is:

  1. Get new rootVC: let rootVC = UIStoryboard(name: "StoryboardName", bundle: nil).instantiateViewControllerWithIdentifier("newRootVCIdentifier") as UIViewController
  2. Set frame for new rootVC from UIScreen's bounds: rootVC.view.frame = UIScreen.mainScreen().bounds
  3. Set new root controller for current window (here with animation): UIView.transitionWithView(self.window!, duration: 0.5, options: .TransitionCrossDissolve, animations: { self.window!.rootViewController = rootVC }, completion: nil)

Done!

You don't need method window?.makeKeyAndVisible(), cause this solution works on current app window.

Community
  • 1
  • 1
3

Swift 3 SWRevealViewController

    self.window = UIWindow(frame: UIScreen.main.bounds)
    let storyBoard = UIStoryboard(name: "Main", bundle: nil)

    let viewController = storyBoard.instantiateViewController(withIdentifier: "SWRevealViewController") as! SWRevealViewController
    self.window?.rootViewController = viewController
    self.window?.makeKeyAndVisible()
Anil
  • 272
  • 3
  • 14
1
let storyboard = UIStoryboard(name: "Main", bundle: nil)

    let destinationViewController = storyboard.instantiateViewController(withIdentifier: "LandVC") as! LandingPageVC

    destinationViewController.webpageURL = NotificationAdvertisement._htmlpackagePath
    destinationViewController.adID = NotificationAdvertisement._adID
    destinationViewController.toneID = NotificationAdvertisement.toneID

    let navigationController = self.window?.rootViewController as! UIViewController

    navigationController.showDetailViewController(destinationViewController, sender: Any?.self)
harsh shah
  • 31
  • 2
0

SWIFT 4

let storyboard = UIStoryboard(name: "Main", bundle: nil)

    let destinationViewController = storyboard.instantiateViewController(withIdentifier: "LandVC") as! LandingPageVC

    destinationViewController.webpageURL = NotificationAdvertisement._htmlpackagePath
    destinationViewController.adID = NotificationAdvertisement._adID
    destinationViewController.toneID = NotificationAdvertisement.toneID

    let navigationController = self.window?.rootViewController as! UIViewController

    navigationController.showDetailViewController(destinationViewController, sender: Any?.self)
Sachin Rasane
  • 1,501
  • 12
  • 17
-1
func scene(_ scene: UIScene, willConnectTo session: UISceneSession, options connectionOptions: UIScene.ConnectionOptions) {
    guard let windowScene = (scene as? UIWindowScene) else { return }
    self.window = UIWindow(windowScene: windowScene)

    let mainStoryboard : UIStoryboard = UIStoryboard(name: "Main", bundle: nil)
    let vc : UIViewController = mainStoryboard.instantiateViewController(withIdentifier: "LoginViewController")
    let rootNC = UINavigationController(rootViewController: vc)
    self.window?.rootViewController = rootNC
    self.window?.makeKeyAndVisible()

}