Search code examples
ios5uistoryboardfacebook-ios-sdkuistoryboardsegue

Using Facebook "iOS native deep linking" and openURL with iOS 5 storyboard/segue based app?


I have an Facebook enabled, iOS 5 app that uses a storyboard and segue based navigation and am confused on how to implement "iOS native deep linking." The example code at Improving App Distribution on iOS just displays a UIAlertView but I am trying to initiate two consecutive seque operations.

For purposes of this question, I've simplified the application to three view controllers: MYCategoryTableViewController, MYItemsTableViewController and MYItemViewController. In the normal flow, the application opens to MYCategoryTableViewController, which displays a table of categories. When a category is selected by the user, there is a segue to MYItemsTableViewController which displays a table of items for that selected category. Lastly, when an item is selected, there is a segue to MYItemViewController which displays an item detail view.

The prepareForSegue from MYCategoryTableViewController sets a property on the destination view controller that represents that category:

- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender {
    if ([[segue identifier] isEqualToString:@"ITEMS_SEGUE"]) {
        MYItemsTableViewController *vc = [segue destinationViewController];        
        MYCategory *mycategory = [self.fetchedResultsController objectAtIndexPath:[self.tableView indexPathForSelectedRow]];
        vc.mycategory = mycategory;
    }
}

The prepareForSegue from MYItemsTableViewController sets a property on the destination view controller that represents that category:

- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender {
    if ([[segue identifier] isEqualToString:@"ITEM_SEGUE"]) {
        MYItemViewController *vc = [segue destinationViewController];
        MYItem *myitem = [self.fetchedResultsController objectAtIndexPath:[self.tableView indexPathForSelectedRow]];
        vc.myitem = myitem;
    }
}

Question: I know that I need to implement something in application:openURL, but not sure what to do next. Assume the incoming URL gives identifiers to lookup the MYCategory and MYItem objects. I found performSegueWithIdentifier but not sure how that interacts with prepareForSegue and how I set my model objects on the destination view controllers.

- (BOOL)application:(UIApplication *)application openURL:(NSURL *)url sourceApplication:(NSString *)sourceApplication annotation:(id)annotation {
    // get "target_url" from incoming url
    // and parse out MYCategory and MYItem identifiers

    // something like this???
    [self.window makeKeyAndVisible];
    [self.window.rootViewController performSegueWithIdentifier:@"ITEM_SEGUE" sender:self];

    return [facebook handleOpenURL:url];
}

Update: Selecting programmatically a cell on a tableview doesn't perform associated segue has given me an idea. Maybe I just save off the url from application:openURL: and let MYCategoryTableViewController load naturally. Then during viewWillAppear, call tableView selectRowAtIndexPath and then performSegueWithIdentifier to transition to MYItemsTableViewController. Repeat the same pattern in MYItemsTableViewController, but clear out the url before the performSegueWithIdentifier call.


Solution

  • Here is what I got working. In MYAppDelegate, I captured a string represented the id of the deep link.

    - (BOOL)application:(UIApplication *)application openURL:(NSURL *)url sourceApplication:(NSString *)sourceApplication annotation:(id)annotation {
        NSString *deepLinkId;  
        // more code that parses url
    
        // only deep link if MYCategoryTableViewController is active controller
        UIViewController *rootContoller = self.window.rootViewController;
        if ([rootContoller isKindOfClass:[UINavigationController class]]) {
            UINavigationController *navController = (UINavigationController *)rootContoller;
            if ([navController.topViewController isKindOfClass:[MYCategoryTableViewController class]]) {
                self.deepLinkId = deepLinkId;
            }
        }
    }
    

    Then, when MYCategoryTableViewController loads, call selectRowAtIndexPath and then performSegueWithIdentifier.

    - (void)processDeepLink {  
        if (_appDelegate.deepLinkId) {
            MYItem *myitem = [MYItem lookupById:_appDelegate.deepLinkId inManagedObjectContext:_appDelegate.dataDocument.managedObjectContext];
            if (myitem) {
                NSIndexPath *indexPath = [self.fetchedResultsController indexPathForObject:myitem.mycategory];
                [self.tableView selectRowAtIndexPath:indexPath animated:NO scrollPosition:UITableViewScrollPositionMiddle];
                [self performSegueWithIdentifier:@"ITEMS_SEGUE" sender:self];
            }
        }
    }
    

    And in when MYItemViewController loads, a similar flow.

    if (_appDelegate.deepLinkId) {
        MYItem *plate = [MYItem lookupById:_appDelegate.deepLinkId inManagedObjectContext:_appDelegate.dataDocument.managedObjectContext];
        NSIndexPath *indexPath = [self.fetchedResultsController indexPathForObject:plate];
        [self.tableView selectRowAtIndexPath:indexPath animated:NO scrollPosition:UITableViewScrollPositionMiddle];
    
        _appDelegate.deepLinkId = nil;
        [self performSegueWithIdentifier:@"ITEM_SEGUE" sender:self];
    }
    

    I also had to observe UIApplicationDidBecomeActiveNotification for the use case when the application was already open.

    [[NSNotificationCenter defaultCenter] addObserver:self
                                             selector:@selector(processDeepLink)
                                                 name:UIApplicationDidBecomeActiveNotification
                                               object:nil];