66

I have an iOS app I created as a "view-based app" in xCode. I have only one viewController, but it is displayed automatically, and I see no code that ties it to my appDelegate. I need to pass data from my appDelegate to my viewController, but don't know how to pull that off.

My app delegate.h:

#import <UIKit/UIKit.h>


@interface AppDelegate : UIResponder <UIApplicationDelegate>

@property (strong, nonatomic) UIWindow *window;
@property (strong, nonatomic) NSDictionary *queryStrings;

@end

Also, appDidFinishLoadingWithOptions:

- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
    // Override point for customization after application launch.
    [[JMC sharedInstance] configureJiraConnect:@"https://cmsmech.atlassian.net/"           projectKey:@"WTUPLOAD" apiKey:@"7fc060e1-a795-4135-89c6-a7e8e64c4b13"];

    if ([launchOptions objectForKey:UIApplicationLaunchOptionsURLKey] != nil) {
        NSURL *url = [launchOptions objectForKey: UIApplicationLaunchOptionsURLKey];
        NSLog(@"url received: %@", url);
        NSLog(@"query string: %@", [url query]);
        NSLog(@"host: %@", [url host]);
        NSLog(@"url path: %@", [url path]);
        queryStrings = [self parseQueryString:[url query]];
        NSLog(@"query dictionary: %@", queryStrings);
    }
    else {
        queryStrings = [self parseQueryString:@"wtID=nil"];
    }

    return YES;
}
Till
  • 27,559
  • 13
  • 88
  • 122
HackyStack
  • 4,887
  • 3
  • 22
  • 28

6 Answers6

107

You can access it with:

MyViewController* mainController = (MyViewController*)  self.window.rootViewController;

If you are nesting your view behind a tabviewcontroller or navigation controller it will return that to you and you will need to access your view controller inside of it

Marcus Adams
  • 53,009
  • 9
  • 91
  • 143
utahwithak
  • 6,235
  • 2
  • 40
  • 62
  • 2
    Sorry, what is `self` in this context? Are you referring to the `-window` property in UIApplicationDelegate? If so, that iOS 5+ only - just a heads-up. – Conrad Shultz Apr 04 '12 at 16:47
  • @ConradShultz yeah, it was the UIApplicationDelegate. That is important to know, Thanks! – utahwithak Apr 04 '12 at 16:49
  • I'm not sure this is the best or only way to do it, but this is how I chose to do it and it works fine. Thanks. – HackyStack Apr 05 '12 at 14:05
  • what if the view controller i want to access is a viewcontroller I'm presenting from rootviewcontroller? I am presenting it through identifier in storyboard.How can i access it? – Suraj K Thomas Jan 15 '14 at 07:25
  • how can I declare that in ios 11 with objective c it gives me errors – iOS Developer Apr 07 '18 at 08:00
22

How about using good old fashioned NSNotifications to send a message from your app delegate to anyone listening (e.g. your view contorller) that something needs to be updated? or you can use Key Value Observing so your view controller can be watching some property in your app delegate.

Michael Dautermann
  • 88,797
  • 17
  • 166
  • 215
  • NSNotificationCenter is good for loosely coupled relationships. If this project has a single view, delegation might be a better approach due to the tighter relationship. – jmstone617 Apr 04 '12 at 16:57
  • @jmstone - make the root view controller a *delegate* of the application delegate? conceptually that seems possible but it sure seems potentially confusing, especially for newbie Objective C people. – Michael Dautermann Apr 04 '12 at 17:06
  • Sure. I'm still convinced he has the view controller as a property. Even if it's set up automatically in IB, he can set an IBOutlet and just hook it up. – jmstone617 Apr 04 '12 at 17:10
  • The issue is that ViewController.h and ViewController.m were created by xCode as well as my AppDelegate.m & .h. Nowhere in my appDelegate is my ViewController instance created, set as rootViewController or anything else.... Here's my AppDelegate.h it only has window: – HackyStack Apr 04 '12 at 17:19
  • Please edit your question and post the code from your applicationDidFinishLaunchingWithOptions method. – jmstone617 Apr 04 '12 at 17:20
  • I still think my solutions are the best ones. ;-) – Michael Dautermann Apr 04 '12 at 17:20
  • Let me be clear... the issue is I am opening app with URL and then openURL() is called in the appDelegate, but I need a way to pass the query string parameters into the viewController... – HackyStack Apr 04 '12 at 17:23
  • @jmstone - It is NOT a property, I need to make it one. When you create the view-based app, xCode automatically creates the AppDelegate, ViewController, and "MainStoryboard_Ipad.storyboard" and wires it up, but I can't locate any of the "wiring" in code is the problem... – HackyStack Apr 04 '12 at 17:27
  • @HackyStack that indeed is a big drawback of using StoryBoard. Why insisting on using it anyways? – Till Apr 04 '12 at 17:48
  • @Michael -- Key Value Observing looks like it could work. I don't know how I feel about that concept from a coupling point of view, but it's clear that that would work for me... – HackyStack Apr 04 '12 at 18:47
  • what if the view controller i want to access is a viewcontroller I'm presenting from rootviewcontroller? I am presenting it through identifier in storyboard.How can i access it? – Suraj K Thomas Jan 15 '14 at 07:25
12

Since you only have one view controller, the generic way (independent of how your app was set up):

UIViewController *vc = [[[UIApplication sharedApplication] keyWindow] rootViewController];
Conrad Shultz
  • 8,748
  • 2
  • 31
  • 33
9

If you want to get any other UIViewController, not just the rootViewController:

UIWindow *window=[UIApplication sharedApplication].keyWindow;
UIViewController *root = [window rootViewController];

UIStoryboard *storyboard = root.storyboard;
CustomViewController *vcc =(CustomViewController *) [storyboard instantiateViewControllerWithIdentifier:@"storyBoardID"];
Arben Pnishi
  • 591
  • 5
  • 11
6

Swift way to do it, you can call this from anywhere, not just appdelegate:

/// EZSwiftExtensions - Gives you the VC on top so you can easily push your popups
public var topMostVC: UIViewController? {
    var presentedVC = UIApplication.sharedApplication().keyWindow?.rootViewController
    while let pVC = presentedVC?.presentedViewController {
        presentedVC = pVC
    }

    if presentedVC == nil {
        print("EZSwiftExtensions Error: You don't have any views set. You may be calling them in viewDidLoad. Try viewDidAppear instead.")
    }
    return presentedVC
}

Its included as a standard function in:

https://github.com/goktugyil/EZSwiftExtensions

Esqarrouth
  • 38,543
  • 21
  • 161
  • 168
0

If you used the View-Based Application template, the single view controller should be accessible via a property in your app delegate. It's the same view controller that gets set as the root view controller of the navigation controller.

If for some reason your project was set up differently, you can get the root view controller of the window, which should be the navigation controller, and then get its top view controller.

EDIT: So the issue is this: With iOS5 and storyboards, Apple has abstracted a lot of the initial set up that you used to have access to (and still do if you opt out of storyboards). They've altered the arguments passed to main() and do more of the set up in the storyboard (which is really just a nib). IMHO, this is in part to keep people from overloading the AppDelegate with heavy operations, like it appears you are doing in yours. The AppDelegate, in essence, exists to manage the Application lifecycle. It's not really meant to be performing methods that hand off information to your view controllers. It's your view controller's job, really, to do the heavy lifting to provide itself with data. I would suggest moving the code into your view controller, perhaps in viewDidLoad. If you need to know the result of the launchOptions objectForKey test it appears you're doing, you could very simply create a property on your AppDelegate, say BOOL launchOptionsURLKeyExists, and set it appropriately. Then you just grab the value of this property in your view controller. You AppDelegate is already a singleton, so you can access it either by [UIApplication sharedApplication].delegate

EDIT 2: viewWillAppear/viewDidAppear gets called when the view is added to the UIApplicationDidEnterForegroundNotification notification in your view controller and respond appropriately when that message is posted.

jmstone617
  • 5,707
  • 2
  • 24
  • 26
  • There is no property and I don't know where it's set, that's what I'm saying here... where is it set??? Here's my appDelegate.h: #import – HackyStack Apr 04 '12 at 17:15
  • I've edited my response. I never use Storyboards, so I forgot they abstract a lot of this stuff now. – jmstone617 Apr 04 '12 at 17:49
  • The issue is when the app becomes active I need to pull the new query string and get it to my view controller, but the only method I know of that's invoked on the app becoming active is in the app delegate (willBecomeActive or whatever). I need the view controller to realize that it might have become active with a DIFFERENT query string... – HackyStack Apr 04 '12 at 18:57
  • NOT TRUE. That's the problem! ViewWillAppear is where my code in the controller grabs the query string but it's not called when the app becomes active (after being in background) it's only called on initial launch. Please use this thread now: http://stackoverflow.com/questions/10017305/viewwillappear-is-not-called-in-my-view-controller-when-the-app-becomes-active – HackyStack Apr 04 '12 at 19:00