Push Notification to open next tab?

Discussion in 'iOS Programming' started by Danneman101, Dec 9, 2011.

  1. Danneman101 macrumors 6502

    Joined:
    Aug 14, 2008
    #1
    I've got a tabbed application that uses Push Notifications (PN).

    Whenever a PN is received, I want it to automatically open tab 2 (index 1), both if the app is opened or not.

    For this I intercept the PN in AppDelegate.m>didReceiveRemoteNotification, and call the "OpenEventTab" in FirstViewController:

    AppDelegate.m
    Code:
    ...
    #import "FirstViewController.h"
    ...
    - (void)application:(UIApplication *)application didReceiveRemoteNotification:(NSDictionary *)userInfo 
    {  
        FirstViewController *firstView = [[FirstViewController alloc] init];
        [firstView OpenEventTab];
        NSLog(@"OpenEventTab called");
    }
    
    FirstViewController.m
    Code:
    - (void)OpenEventTab
    {   
        self.tabBarController.selectedIndex = 1;
        NSLog(@"OpenEventTab executed");
    }
    
    Using NSLog I've realized that the "OpenEventTab"-method is in fact called each time a PN is received, but despite this the next tab is not opened.

    Calling the "OpenEventTab" from a button from within FirstViewController does however show that the function does indeed work, and promptly opens the next tab.

    So you can probably appreciate why I'm a bit confused at this behavior. Does anybody have an explanation and hopefully a solution to this?

    Thanks,
    Dan
     
  2. dejo Moderator

    dejo

    Staff Member

    Joined:
    Sep 2, 2004
    Location:
    The Centennial State
    #2
    You sure you want to be creating a new instance of FirstViewController when you receive your remote notification?
     
  3. Danneman101 thread starter macrumors 6502

    Joined:
    Aug 14, 2008
    #3
    Oh, that's right, I should be calling the existing class like so:

    AppDelegate.m
    Code:
    ...
    #import "FirstViewController.h"
    ...
    - (void)application:(UIApplication *)application didReceiveRemoteNotification:(NSDictionary *)userInfo 
    {  
        [B][(FirstViewController*)self OpenEventTab];[/B]
        NSLog(@"OpenEventTab called");
    }
    
    Problem is now I run into an NSInvalidArgumentException instead:
    -[AppDelegate OpenEventTab]: unrecognized selector sent to instance 0x154dd0

    I exposed this method to other classes accordingly:

    FirstViewController.h
    Code:
    #import <UIKit/UIKit.h>
    @interface FirstViewController : UIViewController
    [B]-(void) OpenEventTab;[/B]
    @end
    
    That should be enough, shouldn't it?

    Thanks,
    Dan
     
  4. dejo Moderator

    dejo

    Staff Member

    Joined:
    Sep 2, 2004
    Location:
    The Centennial State
    #4
    Well, not exactly like so.

    self is not of type FirstViewController when you're in AppDelegate, is it? It's of type AppDelegate.

    Show the code where you instantiate your FirstViewController in AppDelegate (probably within your application:didFinishLaunchingWithOptions:)
     
  5. Danneman101, Dec 10, 2011
    Last edited: Dec 11, 2011

    Danneman101 thread starter macrumors 6502

    Joined:
    Aug 14, 2008
    #5
    Oh...

    Well, that's the thing, I just started a new project using the Tabbed Application template (w. storyboard) for the first time, and the AppDelegate doesn't seem to display the code that instantiates the FirstViewController (which is one of the tabs).

    Here's the full code in AppDelegate (using parse.com's code for PN's btw).

    AppDelegate.h
    Code:
    #import <UIKit/UIKit.h>
    @interface AppDelegate : UIResponder <UIApplicationDelegate>
    @property (strong, nonatomic) UIWindow *window;
    @end
    
    AppDelegate.m
    Code:
    #import "AppDelegate.h"
    
    // PUSH NOTIFICATION (parse.com) 
    #import "Parse/Parse.h"
    
    #import "FirstViewController.h"
    
    @implementation AppDelegate
    
    @synthesize window = _window;
    
    
    - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
    {
        
        // PUSH NOTIFICATION (parse.com) 
        // Register:    for push notifications
        [application registerForRemoteNotificationTypes:UIRemoteNotificationTypeBadge|
         UIRemoteNotificationTypeAlert|
         UIRemoteNotificationTypeSound];
        
        return YES;
    }
    
    
    // PUSH NOTIFICATION (parse.com) 
    // Send:    device token to parse.com
    - (void)application:(UIApplication *)application didRegisterForRemoteNotificationsWithDeviceToken:(NSData *)newDeviceToken
    {
        // Tell Parse about the device token.
        [PFPush storeDeviceToken:newDeviceToken];
        // Subscribe to the global broadcast channel.
        [PFPush subscribeToChannelInBackground:@""];
    }
    
    
    // PUSH NOTIFICATION (parse.com) 
    // Show:    notification UI if in-app
    - (void)application:(UIApplication *)application didReceiveRemoteNotification:(NSDictionary *)userInfo 
    {
        [PFPush handlePush:userInfo];
        
        // OPEN EVENTS TAB (TAB 2)
        [B][(FirstViewController*)self OpenEventTab];[/B]
    }
    
    
    @end
    
    There's really no instantiation code of FirstViewController there. The full code for FirstViewController is simply:

    FirstViewController.h
    Code:
    #import <UIKit/UIKit.h>
    @interface FirstViewController : UIViewController
    [B]-(void) OpenEventTab;[/B]
    @end
    
    FirstViewController.m
    Code:
    #import "FirstViewController.h"
    
    // PUSH NOTIFICATION (parse.com) 
    #import "Parse/Parse.h"
    
    @implementation FirstViewController
    ...
    
    // PUSH NOTIF. (parse.com)
    // Open:    Second tab
    // From:    AppDelegate.m > didReceiveRemoteNotification
    [B]- (void)OpenEventTab
    {   
        self.tabBarController.selectedIndex = 1;
        NSLog(@"OpenEventTab in FirstView");
    }[/B]
    
    ...
    
    @end
    
    Perhaps I need to add a UIViewController-object in AppDelegate using IBOutlet, and link that via the Storyboard (much like you would do in a regular nib)? But how do I outlet an object from the AppDelegate when it doesn't have a representative object in the Storyboard?

    I tried to do the following but no such outlets appeared anywhere in the storyboard. At least with the old nib-files you'd have an object called AppDelegate that displayed these outlets...

    AppDelegate.h
    Code:
    ..
    #import "FirstViewController.h"
    @interface AppDelegate : UIResponder <UIApplicationDelegate>
    {
    	IBOutlet FirstViewController		*firstVC;
    }
    @property (nonatomic, retain) FirstViewController *firstVC;
    ..
    
     
  6. jnoxx macrumors 65816

    jnoxx

    Joined:
    Dec 29, 2010
    Location:
    Aartselaar // Antwerp // Belgium
    #6
    Not read your last post, but, you can search for your FirstViewController in the view hiearchy of yourself. Then (what I would do), is set a pointer to that object, and then call the method on it, that's how we do it with our PN's too.
    Not creating new instances.
     
  7. Danneman101, Dec 11, 2011
    Last edited: Dec 11, 2011

    Danneman101 thread starter macrumors 6502

    Joined:
    Aug 14, 2008
    #7
    Even doing a search I can't find where FirstViewController gets instantiated in AppDelegate.

    I tried adding an outlet for a FirstViewController in the AppDelegate as I would normally do in a xib-file (this project uses storyboard):

    AppDelegate.h
    Code:
    ..
    #import "FirstViewController.h"
    @interface AppDelegate : UIResponder <UIApplicationDelegate>
    {
    	IBOutlet FirstViewController		*firstVC;
    }
    @property (nonatomic, retain) FirstViewController *firstVC;
    ..
    
    But it seems I can't outlet an object from the AppDelegate since the AppDelegate it doesn't have a representative object in the Storyboard? Did they remove that object in storyboard? If so, how do I connect the "firstVC" outlet in AppDelegate.h to the FirstViewController?

    [edit]
    Not finding any info about this I tried adding a new object to the storyboard and setting it as an AppDelegate-object (setting Custom Class > Class). I placed this object in the FirstViewController, and linked the outlet firstVC to the FirstViewController-object.

    However, when sending a PN, the same error halted the app.
     
  8. Danneman101 thread starter macrumors 6502

    Joined:
    Aug 14, 2008
    #8
    Well, it seems this storyboarding-business is pretty new to even the more adept, and I've found very little info on how to call methods in another already instantiated class from AppDelegate. I've tried every option to the limits of my limited knowledge, but to no avail.

    So I finally resorted to creating a new project NOT using that infernal storyboard (which if it wasn't for this apparent restriction would be quite nice to use), and of course it works without any problems.

    Anyway, thanks for trying to help solve the issue, I really appreciate it :)

    I'm still curious as to how to solve this with storyboards. I think I'll post a new thread with only that question since this thread has become overly convoluted and it is highly likely that nobody will ever read it. Hope I'm not breaking the forum rules doing so.

    Cheers,
    Daniel
     
  9. chown33 macrumors 604

    Joined:
    Aug 9, 2009
    #9
    Maybe it's not possible with storyboards. Frankly, I don't understand the problem you're trying to solve, but I didn't wade through all the posted code, either.


    I googled for:
    storyboard root view controller site:developer.apple.com

    and came up with this:
    http://developer.apple.com/library/...cellaneous/RN-AdoptingStoryboards/_index.html

    Under the heading "Accessing the First View Controller" it says:
    "The application delegate is not represented in the storyboard. If you need to access the first view controller ..."
    So it might be worth spending some time going through the storyboard docs again, maybe more carefully, and seeing if you missed anything. If there is a problem with Apple's docs, there's a link at the bottom of every page for sending feedback to Apple. It says "Did this document help you?" and offers 3 choices.
     
  10. Danneman101 thread starter macrumors 6502

    Joined:
    Aug 14, 2008
    #10
    Thanks for the input. Well, to boil it down I'm simply trying to expose an IBOutlet of the type FirstViewController (name of my other class) in AppDelegate so that I can reference that class from AppDelegate. Can't really figure out how to do that using storyboards :/
     
  11. dejo Moderator

    dejo

    Staff Member

    Joined:
    Sep 2, 2004
    Location:
    The Centennial State
    #11
    That document chown33 linked to explains exactly how to get a reference to FirstViewController. You don't need it to be an IBOutlet.

    And there are other ways to achieve what you need. NSNotifications come to mind.
     
  12. Danneman101 thread starter macrumors 6502

    Joined:
    Aug 14, 2008
    #12
    I tried the suggested method in the documents, but get the same "unrecognized selector sent"-error.

    Code:
    - (void)application:(UIApplication *)application didReceiveRemoteNotification:(NSDictionary *)userInfo 
    {
        // Show:    Push Notification
        [PFPush handlePush:userInfo];
        
        // Call:  Function in SecondViewController
        UINavigationController *rootNavigationController = (UINavigationController *)self.window.rootViewController;
        SecondViewController *SecVC = (SecondViewController *)[rootNavigationController topViewController];
        [SecVC.myTableView reloadData];
    }
    
    The error reads:
    "NSInvalidArgumentException', reason: '-[UITabBarController topViewController]: unrecognized selector sent to instance 0x14bc80"

    Any ideas?
     
  13. dejo, Jan 28, 2012
    Last edited: Jan 29, 2012

    dejo Moderator

    dejo

    Staff Member

    Joined:
    Sep 2, 2004
    Location:
    The Centennial State
    #13
    Do you have a UINavigationController as your container-controller / rootViewController? That code from the document is just an example, for if you do.
     
  14. Danneman101, Jan 29, 2012
    Last edited: Jan 29, 2012

    Danneman101 thread starter macrumors 6502

    Joined:
    Aug 14, 2008
    #14
    No, you're right, I have a UITabBarController in the top hierarchy:

    [​IMG]

    However, trying to set that as the rootviewcontroller as such (using FirstViewController since SecondViewController is actually a subview of another view)...

    Code:
        UITabBarController *rootNavigationController = (UITabBarController *)self.window.rootViewController;
        FirstViewController *FirstVC = (FirstViewController *)[rootNavigationController topViewController];  //ERROR
    
    ...resulted in a build-error "Receiver typ 'UITabBarController' for instance message does not declare a method with selector 'topViewController'".

    So I tried replacing "topViewController" with "tabBarController", and this did not cause any build-errors. However, when trying to access the methods in FirstViewController, they were not executed.

    Code:
        UITabBarController *rootNavigationController = (UITabBarController *)self.window.rootViewController;
        FirstViewController *FirstVC = (FirstViewController *)[rootNavigationController tabBarController];
        [FirstVC OpenEventTab];   // DOES NOT GET CALLED
    
    Does this mean there is simply no way for the AppDelegate in a TabBar-based app using Storyboard to access any of its other views?
     
  15. dejo Moderator

    dejo

    Staff Member

    Joined:
    Sep 2, 2004
    Location:
    The Centennial State
    #15
    There is. But you simply have to stop throwing seemingly random solutions at the issue and step back and think about what you're trying to achieve and what options/properties/etc. are available for you to use.

    Does UITabBarController have a topViewController property? No, it doesn't. So, it doesn't make sense to try and reference a non-existent property. Think about how UITabBarController is a container for controllers and how those viewControllers (one for each tab) are stored in its object. Then look at the class reference and see if it has a property that would allow you access to that. And then think about how you might get a reference to your FirstViewController from there.

    So, in summary: stop flailing, step back, use the documentation.
     
  16. Danneman101 thread starter macrumors 6502

    Joined:
    Aug 14, 2008
    #16
    You're right, I'm letting my frustration get the best of me.

    I've taken a closer look at the UITabBarController docs, and realized that it is a ViewController container, containing viewControllers in an array-property that is aptly called "viewControllers" which I can get at using the objectAtIndex-method.

    So, I first access the top dog of the app (ie. the tabbarcontroller) as such:

    Code:
        UITabBarController *tabBarController = (UITabBarController *)self.window.rootViewController;            // GET: TabBarCtrl ->
    
    Then I can use this object to find the viewControllers it contains as such:

    Code:
        FirstViewController *FirstVC = [[tabBarController viewControllers] objectAtIndex:0];                    // GET: FirstVC  
    
    To get a subview of a navigationController (which is the case with SecondViewController, see the image with the storyboard), I similarly use:

    Code:
    	UINavigationController *navigationController = [[tabBarController viewControllers] objectAtIndex:1];    // GET: NavController containing SecondViewController
    	SecondViewController *SecondVC = [[navigationController viewControllers] objectAtIndex:0];              // GET: SecondVC 
    
    Finally it works :)

    Thanks for having the patience to set me straight, dejo :)
     
  17. dejo Moderator

    dejo

    Staff Member

    Joined:
    Sep 2, 2004
    Location:
    The Centennial State
    #17
    You're welcome.

    A word of warning though: at first glance, would you think FirstVC and SecondVC are variable names or class names? I'd suggest you follow the standard naming conventions.
     
  18. Danneman101 thread starter macrumors 6502

    Joined:
    Aug 14, 2008
    #18
    True. I usually never use capital letters to begin an instance name. For instance, FirstVC was called firstVC earlier - don't know why I changed it. Sloppy.
     

Share This Page