Reputation: 859
I've programmatically created a UITableView within my MatchCenterViewController, however it doesn't seem to populate with the JSON data being returned by my cloud code function. It crashes and gives me the following error:
2014-06-08 20:56:23.762 Parse+Storyboard[9136:607] *** Terminating app due to uncaught exception 'NSRangeException', reason: '*** -[__NSArrayI objectAtIndex:]: index 0 beyond bounds for empty array'
*** First throw call stack:
(
0 CoreFoundation 0x02a8c1e4 __exceptionPreprocess + 180
1 libobjc.A.dylib 0x0264a8e5 objc_exception_throw + 44
2 CoreFoundation 0x02a408b2 -[__NSArrayI objectAtIndex:] + 210
3 Parse+Storyboard 0x00005e6a -[MatchCenterViewController tableView:cellForRowAtIndexPath:] + 218
4 UIKit 0x0140311f -[UITableView _createPreparedCellForGlobalRow:withIndexPath:] + 412
5 UIKit 0x014031f3 -[UITableView _createPreparedCellForGlobalRow:] + 69
6 UIKit 0x013e4ece -[UITableView _updateVisibleCellsNow:] + 2428
7 UIKit 0x013f96a5 -[UITableView layoutSubviews] + 213
8 UIKit 0x01379964 -[UIView(CALayerDelegate) layoutSublayersOfLayer:] + 355
9 libobjc.A.dylib 0x0265c82b -[NSObject performSelector:withObject:] + 70
10 QuartzCore 0x0064f45a -[CALayer layoutSublayers] + 148
11 QuartzCore 0x00643244 _ZN2CA5Layer16layout_if_neededEPNS_11TransactionE + 380
12 QuartzCore 0x006430b0 _ZN2CA5Layer28layout_and_display_if_neededEPNS_11TransactionE + 26
13 QuartzCore 0x005a97fa _ZN2CA7Context18commit_transactionEPNS_11TransactionE + 294
14 QuartzCore 0x005aab85 _ZN2CA11Transaction6commitEv + 393
15 QuartzCore 0x006685b0 +[CATransaction flush] + 52
16 UIKit 0x013089bb _UIApplicationHandleEventQueue + 13095
17 CoreFoundation 0x02a1577f __CFRUNLOOP_IS_CALLING_OUT_TO_A_SOURCE0_PERFORM_FUNCTION__ + 15
18 CoreFoundation 0x02a1510b __CFRunLoopDoSources0 + 235
19 CoreFoundation 0x02a321ae __CFRunLoopRun + 910
20 CoreFoundation 0x02a319d3 CFRunLoopRunSpecific + 467
21 CoreFoundation 0x02a317eb CFRunLoopRunInMode + 123
22 GraphicsServices 0x02ce95ee GSEventRunModal + 192
23 GraphicsServices 0x02ce942b GSEventRun + 104
24 UIKit 0x0130af9b UIApplicationMain + 1225
25 Parse+Storyboard 0x00002b4d main + 141
26 libdyld.dylib 0x038e56d9 start + 1
)
libc++abi.dylib: terminating with uncaught exception of type NSException
From what I can understand, it's telling me that self.matchCenterArray
is empty, but I can't seem to figure out why the array isn't populating with the JSON being returned.
MatchCenterViewController.h:
#import <UIKit/UIKit.h>
#import <Parse/Parse.h>
#import "AsyncImageView.h"
#import "SearchViewController.h"
@interface MatchCenterViewController : UIViewController <UITableViewDataSource>
@property (nonatomic) IBOutlet NSString *itemSearch;
@property (nonatomic, strong) NSArray *imageURLs;
@property (strong, nonatomic) NSString *matchingCategoryCondition;
@property (strong, nonatomic) NSString *matchingCategoryLocation;
@property (strong, nonatomic) NSNumber *matchingCategoryMaxPrice;
@property (strong, nonatomic) NSNumber *matchingCategoryMinPrice;
@property (strong, nonatomic) NSArray *matchCenterArray;
@end
MatchCenterViewController.m:
#import "MatchCenterViewController.h"
#import <UIKit/UIKit.h>
@interface MatchCenterViewController () <UITableViewDataSource, UITableViewDelegate>
@property (nonatomic, strong) UITableView *matchCenter;
@end
@implementation MatchCenterViewController
- (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil
{
self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil];
if (self) {
// self.matchCenter = [[UITableView alloc] initWithFrame:self.view.bounds style:UITableViewStylePlain];
// _matchCenter.dataSource = self;
// _matchCenter.delegate = self;
// [self.view addSubview:self.matchCenter];
}
return self;
}
- (void)viewDidLoad
{
[super viewDidLoad];
[self.matchCenter registerClass:[UITableViewCell class] forCellReuseIdentifier:@"Cell"];
self.matchCenter = [[UITableView alloc] initWithFrame:self.view.bounds style:UITableViewStylePlain];
_matchCenter.dataSource = self;
_matchCenter.delegate = self;
[self.view addSubview:self.matchCenter];
self.matchCenterArray = [[NSArray alloc] init];
}
- (void)viewDidAppear:(BOOL)animated
{
self.matchCenterArray = [[NSArray alloc] init];
[PFCloud callFunctionInBackground:@"MatchCenterTest"
withParameters:@{
@"test": @"Hi",
}
block:^(NSDictionary *result, NSError *error) {
if (!error) {
self.matchCenterArray = [result objectForKey:@"Top 3"];
dispatch_async(dispatch_get_main_queue(), ^{
[_matchCenter reloadData];
});
NSLog(@"Test Result: '%@'", result);
}
}];
}
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView
{
return 1;
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
return 3;
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *CellIdentifier = @"Cell";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
NSDictionary *matchCenterDictionary= [self.matchCenterArray objectAtIndex:indexPath.row];
cell.textLabel.text = [matchCenterDictionary objectForKey:@"Title"];// title of the first object
// if([matchCenterDictionary objectForKey:@"Price"] != NULL)
// {
// cell.detailTextLabel.text = [NSString stringWithFormat:@"$%@",[matchCenterDictionary objectForKey:@"Price"]];
// }
return cell;
}
- (void)didReceiveMemoryWarning
{
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}
/*
#pragma mark - Navigation
// In a storyboard-based application, you will often want to do a little preparation before navigation
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender
{
// Get the new view controller using [segue destinationViewController].
// Pass the selected object to the new view controller.
}
*/
@end
JSON that's returned:
{
"Top 3" : [
{
"Title" : "Apple iPhone 5s (Latest Model) - 16GB - Silver (AT&T) Smartphone",
"Price" : "400.0",
"Image URL" : "http://thumbs2.ebaystatic.com/m/mewfVG0QbBiu1nZytMuAlZw/140.jpg",
"Item URL" : "http://www.ebay.com/itm/Apple-iPhone-5s-Latest-Model-16GB-Silver-AT-T-Smartphone-/181431570117?pt:Cell_Phones"
},
{
"Title" : "Apple iPhone 5c (Latest Model) - 16GB - Pink (Verizon) Smartphone",
"Price" : "350.0",
"Image URL" : "http://thumbs4.ebaystatic.com/m/mMPAT67KjfCZF9oorbTf3uw/140.jpg",
"Item URL" : "http://www.ebay.com/itm/Apple-iPhone-5c-Latest-Model-16GB-Pink-Verizon-Smartphone-/191204844039?pt:Cell_Phones"
},
{
"Title" : "Apple iPhone 5 16GB, White, works with Virgin Mobile US NEW",
"Price" : "359.99",
"Image URL" : "http://thumbs3.ebaystatic.com/m/m5x1uj1iSS2fr691tifrvrw/140.jpg",
"Item URL" : "http://www.ebay.com/itm/Apple-iPhone-5-16GB-White-works-Virgin-Mobile-US-NEW-/141227441998?pt:Cell_Phones"
}
]
}
Upvotes: 2
Views: 4548
Reputation: 90117
UITableView will request cells before your API call has returned data. Network request can take a long time, and if the user is not connected to the network it will fail completely. Don't assume that your network calls will return immediately.
And because you start your asynchronous (!) API call only in viewDidAppear:
the tableView starts to request cells even before you have started the API call.
Since you have specified that there are 3 rows in your first section tableView:cellForRowAtIndexPath:
tries to access this:
// for first row
NSDictionary *matchCenterDictionary= [self.matchCenterArray objectAtIndex:0];
Which raises an out of bounds exception because your array is empty, and does not have an object at index 0.
Don't return 3 in tableView:numberOfRowsInSection:
if you don't have 3 objects in your array, return the actual number of objects that are contained in your array:
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
return [self.matchCenterArray count];
}
and to prevent your next crash, move the call where you register the cell to a place where self.matchCenter is not nil, i.e. after the alloc init of the tableView.
e.g.:
self.matchCenter = [[UITableView alloc] initWithFrame:self.view.bounds style:UITableViewStylePlain];
/* ... */
[self.matchCenter registerClass:[UITableViewCell class] forCellReuseIdentifier:@"Cell"];
Upvotes: 4