viewWillAppear is not called in my view controller when the app becomes active

Go To StackoverFlow.com

2

Disclaimer: I'm an iOS noob.

I created a view based app using Xcode's template that uploads pictures to a website. Xcode created AppDelegate.m (& .h) and ViewController.m as well as a storyboard for me automatically. The app is launched from a website using a URL schema, and I use that information in my view controller. I have two questions:

1) Is this a good way to pass query strings from my AppDelegate to the view controller?
I have a NSDictionary property in my AppDelegate called queryStrings which stores the query strings. Then in viewWillAppear in my view controller I use the following code to access it:

AppDelegate *appDelegate = (AppDelegate *)[[UIApplication sharedApplication] delegate];
    NSDictionary *queryStrings = appDelegate.queryStrings;
    wtID = [queryStrings valueForKey:@"wtID"];

2) When the app is already launched but in background and the user opens the app again via a url on the webpage (with different query string), I have implemented openURL in the AppDelegate which populates my queryStrings property in the AppDelegate. Problem is: the view controller's viewWillAppear method is never called so the above code isn't executed. How do I get the query string data to the view controller? How does the view controller know the app just became active?

NOTE: Because the project was created using the "view-based app" template in Xcode, the only property my AppDelegate has is the window property. I can't tell where in code that my view controller is ever even referenced by the AppDelegate... It's never set as the rootViewController or anything in the code anyway... I guess it's just some IB magic that I can't see.

In case it helps, here's my AppDelegate.h:

#import <UIKit/UIKit.h>


@interface AppDelegate : UIResponder <UIApplicationDelegate>

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

@end

and here's my didFinishLoadingWithOptions, openURL, and my helper method: parseQueryString:

- (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;
}


-(BOOL)application:(UIApplication *)application openURL:(NSURL *)url sourceApplication:(NSString *)sourceApplication annotation:(id)annotation
{
    NSLog(@"openURL executed");
    if (!url) 
        return NO;
    NSLog(@"query string: %@", [url query]);
    queryStrings = [self parseQueryString:[url query]];

    mainViewController.wtID = [queryStrings valueForKey:@"wtID"];
    return YES;
}

-(NSDictionary *)parseQueryString:(NSString *)query
{
    NSMutableDictionary *dictionary = [[NSMutableDictionary alloc] initWithCapacity:6];
    NSArray *pairs = [query componentsSeparatedByString:@"&"];
    for (NSString *pair in pairs)
    {
        NSArray *elements = [pair componentsSeparatedByString:@"="];
        NSString *key = [[elements objectAtIndex:0] stringByReplacingPercentEscapesUsingEncoding:NSUTF8StringEncoding];
        NSString *val = [[elements objectAtIndex:1] stringByReplacingPercentEscapesUsingEncoding:NSUTF8StringEncoding];
        [dictionary setObject:val forKey:key];
    }
    return dictionary;
}

Thanks in advance!

2012-04-04 18:40
by HackyStack
For 2: Could you maybe put the code into applicationWillEnterForeground - sooper 2012-04-04 18:47
No the code in question is in the controller not the app delegate.. - HackyStack 2012-04-04 19:24


1

So, storyboards are starting to get on my nerves, and I'm not even building your app! :)

As I have stated in your other question (of which this is a duplicate, but hey), the lack of the reference to the view controller is due to storyboards. viewDidAppear and viewWillAppear get called when the view is added to the hierarchy. Going to and from the background and foreground doesn't qualify as being added to the hierarchy. Luckily, your app sends notifications that correspond to the methods in the AppDelegate. Simply register as an observer of the UIApplicationWillEnterForegroundNotification notification. Then you can update your UI in the method that gets fired as a result of receiving that notification!

EDIT: Adding the link to the duplicated question for good measure: How do I access my viewController from my appDelegate? iOS

2012-04-04 19:32
by jmstone617
For the record, I asked 2 tightly related questions in this post. One of which is similar to the one I asked in the other post, but I recomposed it in this one to be more specific about what the issue is. Thanks for your comments and attempts to contribute though! Unfortunately, no one has a great answer.. - HackyStack 2012-04-05 13:38
The answer to THIS question (why does viewWillAppear not get called when returning from background) has been answered. There is also an answer for how to get notified of changes when you DO return from the background. Let me know if I can make my answer here clearer - jmstone617 2012-04-05 20:54


0

Hm, so why there is no UIViewController declared in your AppDelegate?

UPDATE

When you created a new project, you should have smth like this in your AppDelegate:

@interface AppDelegate : UIResponder <UIApplicationDelegate>

@property (strong, nonatomic) UIWindow *window;
@property (strong, nonatomic) UIViewController *viewController;

@end

//

#import "ViewController.h"

@implementation AppDelegate

@synthesize window, viewController;

- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
    self.window = [[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]];
    self.viewController = [[ViewController alloc] init];

    [self.window setRootViewController:self.viewController];
    [self.window makeKeyAndVisible];

    return YES;
}

//...

@end
2012-04-04 18:48
by demon9733
That's my question! I know it's because I used the "view-based app" template when I created the project in Xcode, which I believe wired it up behind the scenes, which is a real PITA when I'm now trying to access my view controller from the app delegate. You've pretty much summed up my entire problem.. - HackyStack 2012-04-04 18:52
Setting up a new project with Storyboards does not give you a UIViewController in the app delegate. It handles all of the set up of this in the unloading of the storyboard, as I explained in a question @HackyStack started - jmstone617 2012-04-04 18:57
Ok, but now I'm stuck with a 99% fully functional app that has a storyboard. I need access to the view controller so when the app becomes active (launched by URL with new query string) and openURL gets called in my app delegate I need to tell the view controller that the query string has changed.. - HackyStack 2012-04-04 18:59
see update, pleas - demon9733 2012-04-04 18:59
No. That is certainly not there and jmstone has said and I agree that it's because it uses what I'm starting to want to refer to as "that stupid storyboard"... So now that we know it doesn't add that code... what do I do about it? should I add the code above to my appDelegate manually? Add the vc property? Is that going to result in two instances? One I create explicitly and one IB created behind the scenes in the storyboard??? This is crap! Hiding the code that defines the rootViewController is CRAP - HackyStack 2012-04-04 19:05
Honestly, I had never used storyboard before. And the code above, that I have in each my project, has always worked - demon9733 2012-04-04 19:12
Maybe I can access the one it created with something like: self.window.rootViewController?? - HackyStack 2012-04-04 19:26
Ads