How do I Pass An Array Between Two Views Using the SWRevealViewController?

QUICK BACKGROUND:

I have an app that shows a list of songs in a searchable, tabbed interface (a.k.a. "Cheers View Controller"). There are two tabs, one for each category of my cheers. I have a sidebar that allows you to select menu options, one of which is called "My Favorites". It uses the SWReveal Controller (a.k.a. "Sidebar"). I am trying to allow the user to mark favorite cheers before game time, then use the sidebar navigation to quickly reference their list of favorite songs when they're in the dugout.

The initial view controller for my application is a Reveal View Controller.

The front view (sw_front) is the tab bar controller that shows the Cheers View Controller when the app first launches. The data is loaded into an array form a CSV file in the ViewDidLoad of the Cheers View Controller.

The rear controller (sw_rear) is the Sidebar that defines the menu options for my application. One of the menu options on my Sidebar is called My Favorites. It's this sidebar that is conceptually throwing me. See below for a snapshot of the relevant area of my storyboard.

STORYBOARD SNAPSHOT: It turns out that as a new user I cannot post images. :(

GOAL

I am trying to access the array of favorite cheers in the Cheers View Controller from the My Favorites view controller. There is currently no segue from Cheers View Controller to My Favorites and it seems like defining a segue here is wrong conceptually.

I don't want to use a singleton or some other work around, I would rather structure my app the correct way. One easy solution is to move My Favorites over to another tab of the Tab Bar Controller. While I can change the design and things would probably be easier, it feel like I'm missing a valuable concept here that I can use in later apps.

I realize that there are many posts already on the topic of passing data from one controller to another. I've read what I think is all of them but I have not been able to incorporate the solutions into the SWRevealController implementation successfully yet. So clearly I'm confused either on mechanics or on concepts (or both). Most of those posts I read were involving segues or passing fields between two controllers that had segues or relationships of some sort.

MECHANICS QUESTIONS:

I currently prep the favorites array in the ViewDidLoad of the table view controller in the Main Tabbed Interface. I did this immediately after loading the main array with all the songs from a CSV file. I was thinking that I could somehow pass this to the My Favorites table view controller or access it from the My Favorites Table view controller.

//Copy favorite cheers into array
NSPredicate *predicate = [NSPredicate predicateWithFormat:@"SELF.favorite contains[c] %@",@"Yes"];
NSArray *tempArray = [self.cheersArray filteredArrayUsingPredicate:predicate];

self.myFavoriteCheersArray = [NSMutableArray arrayWithArray:tempArray];

Here's where it gets sticky for me - when the user clicks the button to show the SideBar, then clicks on My Favorites:

the prepareForSegue of the Sidebar will execute with this:

- (void) prepareForSegue: (UIStoryboardSegue *) segue sender: (id) sender
{
    // Get the selected row and identify the destination controller you are sending to
    NSIndexPath *indexPath = [self.tableView indexPathForSelectedRow];
    UINavigationController *destViewController = (UINavigationController*)segue.destinationViewController;

    // Set the title of navigation bar by using the menu items
    destViewController.title = [[_menuItems objectAtIndex:indexPath.row] capitalizedString];

    if ( [segue isKindOfClass: [SWRevealViewControllerSegue class]] ) {
        SWRevealViewControllerSegue *swSegue = (SWRevealViewControllerSegue*) segue;

        swSegue.performBlock = ^(SWRevealViewControllerSegue* rvc_segue, UIViewController* svc, UIViewController* dvc) {

            UINavigationController* navController = (UINavigationController*)self.revealViewController.frontViewController;
            [navController setViewControllers: @[dvc] animated: NO ];
            [self.revealViewController setFrontViewPosition: FrontViewPositionLeft animated: YES];
        };

    }

And I was trying to add this to the end of the prepareForSegue to somehow allow My Favorites access to the array...but I'm having trouble identifying the property in the Cheers View Controller from the Sidebar...

    if([segue.identifier isEqualToString:@"myFavorites"]) {

        //Get the Favorites View Controller (which has a navigation controller)
        MyFavoritesViewController *myFavoritesViewController = [[MyFavoritesViewController alloc] init];
        myFavoritesViewController = (MyFavoritesViewController*)destViewController.topViewController;

        //THIS BELOW PART IS SHAKEY AT BEST
        //Get the Cheers View Controller (which is part of a tabbed view controller)
        SWRevealViewController *frontViewController = [[SWRevealViewController alloc] init];
        CheersViewController *cheersViewController = (CheersViewController*)frontViewController.frontViewController;

        //Pass the Favorites Array -- The below code gives me an error that the property I set on the CheersViewController is not found.
        rankingViewController.favoriteCheers = CheersViewController.myFavoriteCheersArray;
    }


}

CONCEPT QUESTIONS I'D LOVE COMMENTARY ON:

  1. What is the best (by best, I mean a balanced approach between implementation reality and academic purity) approach to make the array available to another view controller when a segue is not defined without butchering OO concepts/designs?

  2. The rear view controller (the Sidebar) does not get loaded on app startup - instead it gets loaded on first display. Is it correct that I am therefore loading the data for the first time in the Cheers View Controller (i.e. I could make the Sidebar load on app startup but this seems conceptually wrong as well since the user may never access the Sidebar)?

  3. Is it best to prep the my favorites array when the app first launches in the Cheers View Controller or only when My Favorites is first viewed? The list is rather small but technically if the user never clicked on the sidebar to view their favorites then I would be loading an extra array unnecessarily - probably a nit since the array is small but I am curious.

  4. Is the proper place to load the data where I have it currently?

  5. It seems to me that a delegate conceptually wouldn't make sense since the Cheers View Controller doesn't really need to be aware of a user viewing his/her favorites. All I'm really doing is trying to query a set of master data. Am I correct in thinking this way?

Answers


Here is how these were solved. They work - as for whether or not they are the best, I'll let you know as I learn more. grin

QUESTION 1: Load an initial set of data from a plist, CSV or any other source file in AppDelegate didFinishLaunchingWithOptions. For example:

//Read in from JSON file
NSError *error;
NSData *data = [NSData dataWithContentsOfFile:[[NSBundle mainBundle] pathForResource:@"Cheers" ofType:@"json"]];
NSDictionary *json = [NSJSONSerialization JSONObjectWithData:data options:kNilOptions error:&error];

self.cheersArray = json[@"cheers"];

This array can be immutable (i.e. NSArray) and you can cast this to a view controller who can manipulate it as an NSMutableArray if needed. For example:

//Instantiate initial view controller
UIStoryboard *storyboard = [UIStoryboard storyboardWithName:@"Main" bundle:[NSBundle mainBundle]];
CMViewController *cmViewController = [storyboard instantiateInitialViewController];

//Pass array of data into a table view controller
cmViewController.tableData = (NSArray *)self.tableData;

This assumes your View Controller has a property that can be used to pass the data into.

If you had a JSON file and need it to be a PLIST, there's a great utility via Terminal on your Mac

plutil -convert xml1 InputFileName.json -o OutputFileName.plist

As you learn like I did you may realize plists with a smaller number of rows is preferred over JSON files and a tad bit easier (personal opinion) to deal with when it comes time to saving changes to the data (if your app allows that).

QUESTION 2: It is conceptually wrong from an academic point of view to load the Sidebar on app startup. It should only be loaded on first use.

QUESTION 3:Similar to Question 2, It is best to pass the favorites array to the view controller that needs it on first request (i.e. the user presses on the tab "Favorites". Since technically Favorites is just a specific view of a subset of the master data I am loading, I believe this is correct.

QUESTION 4:I moved it form the first view controller to the App Delegate as shown above. This gave me more flexibility to pass data down (conceptually speaking from a parent to a child view controller) rather than across (form child to child view controller). This simplified my code.

QUESTION 5: I don't believe a delegate is appropriate for the answer to the challenge I was facing.

Feel free to comment!


Need Your Help

Cannot Sync Git in Visual Studio 2013

git visual-studio-2013

I'm working with another developer and we seem to have got ourselves into a GIT pickle.