I've been doing a navigation stack that populates a table with a plist. However I can seem to pass the "Name" information to my third table when I click the "City" because nothing displays. I do get a new table to popup but it should populate with "Grand Canyon" but it doesn't. I have successfully passed from "Destination" to "City" but not to "Name". I think the problem is where the beginning loop which does not recognizing the key to write the "Name".
Plist:
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<array>
<dict>
<key>Destination</key>
<string>Arizona</string>
<key>Tours</key>
<array>
<dict>
<key>City</key>
<string>Phoenix</string>
<key>Options</key>
<array>
<dict>
<key>Name</key>
<string>Grand Canyon</string>
</dict>
</array>
</dict>
</array>
</dict>
<dict>
<key>Destination</key>
<string>California</string>
<key>Tours</key>
<array>
<dict>
<key>City</key>
<string>San Diego</string>
</dict>
<dict>
<key>City</key>
<string>Calexico</string>
</dict>
</array>
</dict>
</array>
</plist>
Code:
#import "OptionsViewController.h"
@interface OptionsViewController ()
@property NSArray *options;
@end
@implementation OptionsViewController
- (id)initWithStyle:(UITableViewStyle)style
{
self = [super initWithStyle:style];
if (self) {
// Custom initialization
}
return self;
}
- (void)viewDidLoad
{
[super viewDidLoad];
self.title = @"Available Tours";
// Uncomment the following line to preserve selection between presentations.
// self.clearsSelectionOnViewWillAppear = NO;
// Uncomment the following line to display an Edit button in the navigation bar for this view controller.
// self.navigationItem.rightBarButtonItem = self.editButtonItem;
}
- (void)didReceiveMemoryWarning
{
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}
#pragma mark - Table view data source
-(void)setToursAvailable:(NSString *)toursAvailable{
if (_toursAvailable != toursAvailable) {
_toursAvailable = toursAvailable;
NSString *filePath = [[NSBundle mainBundle]pathForResource:@"Destination" ofType:@"plist"];
NSArray *tours =[NSArray arrayWithContentsOfFile:filePath];
for (int i = 0; i < [tours count]; i++) {
NSDictionary *tourDictionary = [tours objectAtIndex:i];
NSString *tempTour = [tourDictionary objectForKey:@"City"];
if ([tempTour isEqualToString:_toursAvailable]) {
self.options = [tourDictionary objectForKey:@"Options"];
}
}
}
}
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView
{
// Return the number of sections.
return 1;
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
// Return the number of rows in the section.
return [self.options count];
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *CellIdentifier = @"Cell";
[tableView registerClass:[UITableViewCell class] forCellReuseIdentifier:CellIdentifier];
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier forIndexPath:indexPath];
//Fetch Options
NSDictionary *opt = [self.options objectAtIndex:[indexPath row]];
// Configure the cell...
[cell.textLabel setText:[opt objectForKey:@"Name"]];
cell.accessoryType = UITableViewCellAccessoryDisclosureIndicator;
return cell;
}
Above is your plist structure shown in Xcode, let me help you to debug with your code.
NSString *filePath = [[NSBundle mainBundle]pathForResource:@"Destination" ofType:@"plist"];
NSArray *tours =[NSArray arrayWithContentsOfFile:filePath];
Above code will load all data in tours as array i.e. it will contain Item 0, Item 1 and so on, lets look your loop,
for (int i = 0; i < [tours count]; i++) {
NSDictionary *tourDictionary = [tours objectAtIndex:i];
}
Now tourDictionary is dictionary and it contains 2 objects Destination (Which is NSString) and Tours (Which is NSArray), so now to get your Options from the Tours you have to do like
NSArray *allTours = [tourDictionary objectForKey:@"Tours"];
Now you will have to loop through that array again and find the exact city options you are looking for,
i.e.
for(NSDictionary *allCities in allTours){
if([[allCities objectForKey:@"City"] isEqualToString:_toursAvailable]){
self.options = [allCities objectForKey:@"Options"];
break; //You got your options so just exit this loop.
}
}
So your final code would be like,
for (int i = 0; i < [tours count]; i++) {
NSDictionary *tourDictionary = [tours objectAtIndex:i];
NSArray *allTours = [tourDictionary objectForKey:@"Tours"];
for(NSDictionary *allCities in allTours){
if([[allCities objectForKey:@"City"] isEqualToString:_toursAvailable]){ //Assuming _toursAvailable is City
self.options = [allCities objectForKey:@"Options"];
break; //You got your options so just exit this loop.
}
}
}
This code is suspect:
for (int i = 0; i < [tours count]; i++) {
NSDictionary *tourDictionary = [tours objectAtIndex:i];
NSString *tempTour = [tourDictionary objectForKey:@"City"];
if ([tempTour isEqualToString:_toursAvailable]) {
self.options = [tourDictionary objectForKey:@"Options"];
}
}
I think that the error is that @"City"
is not a key in the outer dictionary.
You could try:
NSString *path = [[NSBundle mainBundle] pathForResource:@"Destination" ofType:@"plist"];
NSArray *destinations = [NSArray arrayWithContentsOfFile:path];
[destinations enumerateObjectsUsingBlock:^(id destObj, NSUInteger idx, BOOL *stop) {
NSUInteger index = [destObj[@"Tours"] indexOfObjectPassingTest:^BOOL(id tourObj, NSUInteger idx, BOOL *stop) {
return [tourObj[@"City"] isEqualToString:_toursAvailable];
}];
if( index != NSNotFound ) {
self.options = destObj[@"Tours"][index][@"Options"];
*stop = YES;
}
}];
It appears that the prior UITableViewController
in that stack must be setting toursAvailable
although we don't see that code or the OptionsViewController
class interface. If that's the case, then you need to ascertain that that it's being set properly.
Since you did say you'd appreciate any help, I have a tangentially-related suggestion:
Use a proper object graph to represent your model. You are working with destinations, tour, cities, etc. Why not represent those in your application's model? Granted, at this stage you are just seeding your app with code from a plist; but what happens when you want to download tours via a web API, or allow users to make comments about tours, or whatever. The plist-as-model-layer approach makes that difficult. It's also difficult for someone else to pickup your code and read it. It's much easier to work with object relationships through an explicit classes of objects than with arrays of dictionaries containing arrays containing dictionaries, etc.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With