Asynchronous call not refreshing table - objective-c

I'm noticing some very weird behavior, I'm hoping to see if anyone else has come across this. I am making an asynchronous api call (code below). When the call completes an array is populated from the results of the call, and then I reload my table (which should cause cellForRowAtIndexPath to be called), which should update my table view with my array's data. However, the data in the tableview still doesn't appear until it needs to be reloaded from some other means - for instance, if I change views by clicking on a tab and then going back to the original view. It seems like there is some aspect of "refreshing the table" that I am missing, however I am calling reloadData when the asynchronous call returns.
Code:
-(void)refreshWeeksOffers
{
[array removeAllObjects];
NSMutableURLRequest *request =
[WebRequests createPostRequestWithApiCall:#"getResults" bodyData:#"params={\"locale\" : \"US\"}"];
[NSURLConnection
sendAsynchronousRequest:request
queue:[[NSOperationQueue alloc] init]
completionHandler:^(NSURLResponse *response,
NSData *data,
NSError *error)
{
if ([data length] >0 && error == nil)
{
// parse home page offers from resulting json
JsonParser *parser = [[JsonParser alloc] initWithData:data];
array = [parser parseHomepageResults];
[self.topWeekTable reloadData];
}
else if ([data length] == 0 && error == nil)
{
NSLog(#"Nothing was downloaded.");
}
else if (error != nil){
NSLog(#"Error = %#", error);
}
}];
[self.topWeekTable reloadData];
}
-(NSInteger)numberOfSectionsInTableView:(UITableView *)tableView
{
return 1;
}
-(NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
return [array count];
}
-(UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *cellIdentifier = #"Cell";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:cellIdentifier];
if(!cell)
{
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:cellIdentifier];
}
Offer *currentOffer = (Offer *)[array objectAtIndex:indexPath.row];
cell.textLabel.text = [NSString stringWithFormat:#"%.1f%% Back", currentOffer.advertisedRate];
NSData *data = [NSData dataWithContentsOfURL:currentOffer.storeImage];
UIImage *img = [[UIImage alloc] initWithData:data];
cell.imageView.image = img;
return cell;
}

It's because you are invoking UIKit from a background thread, which is not supported.
Try this:
[ self.tableView performSelectorOnMainThread:#selector( reloadData ) withObject:nil waitUntilDone:NO ] ;
Another strategy which I prefer is like this:
-(void)startAsyncSomething
{
[ obj operationWithAsyncHandler:^{
[ [ NSThread mainThread ] performBlock:^{
... handle completion here ...
} ]
}]
}
You can add -performBlock: to NSThread with a category something like this:
#implementation NSThread (BlockPerforming)
-(void)performBlock:(void(^)())block
{
if ( !block ) { return ; }
[ self performSelector:#selector( performBlock: ) onThread:self withObject:[ block copy ] waitUntilDone:NO ] ;
}
#end

Related

Populating Custom TableViewCell from Twitter using JSON Serialization

I am new to Objective-C. I spent countless hours being stuck on getting a blank tableview, I am desperate at this point.
I am loading twitter data through a JSON call, using their API. I store everything in a NSDictionary, run a for loop to select only "text" values. I store the filtered dictionary in an object which I later use in the TableView initialization.
I created a subclass of UItableViewCell for my custom cell.
My dataSources and delegates seem to be well connected as well (that is what I think at least)
I am having a hard time finding my problem. If someone could help me out please.
#import "ViewController.h"
#import "myCell.h"
#import <TwitterKit/TwitterKit.h>
#interface ViewController ()
#end
#implementation ViewController
#synthesize myTableView;
NSMutableArray *tweetObject;
NSDictionary *dictionary;
NSString *name;
NSString *text;
- (void)viewDidLoad {
tweetObject = [[NSMutableArray alloc] init];
[super viewDidLoad];
self.myTableView.dataSource = self;
self.myTableView.delegate = self;
text = #"text";
[[Twitter sharedInstance] logInGuestWithCompletion:^(TWTRGuestSession *guestSession, NSError *error) {
if (guestSession) {
// make API calls that do not require user auth
NSString *statusesShowEndpoint = #"https://api.twitter.com/1.1/statuses/user_timeline.json?screen_name=goofernator";
NSError *clientError;
NSURLRequest *request = [[[Twitter sharedInstance] APIClient]
URLRequestWithMethod:#"GET"
URL:statusesShowEndpoint
parameters:0
error:&clientError];
if (request) {
[[[Twitter sharedInstance] APIClient]
sendTwitterRequest:request
completion:^(NSURLResponse *response,
NSData *data,
NSError *connectionError) {
if (data) {
// handle the response data e.g.
NSError *jsonError;
NSDictionary *json = [NSJSONSerialization
JSONObjectWithData:data
options:0
error:&jsonError];
for (NSDictionary *dataDict in json) {
NSString *text = [dataDict valueForKeyPath: #"text"];
dictionary = [NSDictionary dictionaryWithObjectsAndKeys:text,#"bodytext",nil];
[tweetObject addObject:dictionary];
}
}
else {
NSLog(#"Error: %#", connectionError);
}
}];
}
else {
NSLog(#"Error: %#", clientError);
}
} else {
NSLog(#"error: %#", [error localizedDescription]);
}
}];
}
- (void)didReceiveMemoryWarning {
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView {
return 1;
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
return tweetObject.count;
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
static NSString *CellIdentifier = #"Cell";
myCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil) {
cell=[[myCell alloc]initWithStyle:
UITableViewCellStyleSubtitle reuseIdentifier:CellIdentifier];
}
NSDictionary *tmpDict = [self.tweetObject objectAtIndex:indexPath.row];
cell.txtLblOutput.text = [tmpDict objectForKey:text];
[tableView reloadData];
return cell;
}
#end
Here you can see the way my storyboard is put together and the references I am using
http://postimg.org/image/79w7pqmu3/
http://postimg.org/image/ixq9kabyz/
You should call [tableView reloadData]; in your request completion handler, after you've filled your array. Check if you receive any data. Does the array get filled with dictionary objects?
But mate, seriously, you need to read some good books about coding, your code really lacks understanding of what you're doing. Please, remove [tableView reloadData]; from the - ...cellForRowAtIndexPath:... method.

Getting profile image in iOS via STTwitter

I am using the the STTwitter API to make an App only twitter Feed. I have successfully output the tweet to the table cell, but now I'm attempting to connect user profile images and I am running in to some problems. I tried implementing the code I found here, but I was getting an error stating "No known class method for selector 'imageWithContentsOfURL:'" so I fixed the problem by replacing UIImage with CIImage. However, now my app is crashing because I'm trying to output a CIImage to an UIImageView. My code and errors are as follows:
Code:
- (void)viewDidLoad
{
[super viewDidLoad];
NSString* boldFontName = #"Avenir-Black";
[self styleNavigationBarWithFontName:boldFontName];
self.title = #"Twitter Feed";
self.feedTableView.dataSource = self;
self.feedTableView.delegate = self;
self.feedTableView.backgroundColor = [UIColor whiteColor];
self.feedTableView.separatorColor = [UIColor colorWithWhite:0.9 alpha:0.6];
//self.profileImages = [NSArray arrayWithObjects:#"profile.jpg", #"profile-1.jpg", #"profile-2.jpg", #"profile-3.jpg", nil];
STTwitterAPI *twitter = [STTwitterAPI twitterAPIAppOnlyWithConsumerKey:#"stuff"
consumerSecret:#"stuff"];
[twitter verifyCredentialsWithSuccessBlock:^(NSString *username) {
[twitter getUserTimelineWithScreenName:#"RileyVLloyd"
successBlock:^(NSArray *statuses) {
self.twitterDataSource = [NSMutableArray arrayWithArray:statuses];
for (int i=1; i <= _twitterDataSource.count; i++) {
NSLog(#"%d", i);
NSDictionary *tweetDictionary = self.twitterDataSource[i];
NSString *final = tweetDictionary[#"profile_image_url"];
NSLog(#"%#", final);
}
[self.feedTableView reloadData];
} errorBlock:^(NSError *error) {
NSLog(#"%#", error.debugDescription);
}];
} errorBlock:^(NSError *error) {
NSLog(#"%#", error.debugDescription);
}];
//[self getTimeLine];
}
#pragma mark Table View Methods
-(NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
return self.twitterDataSource.count;
}
-(UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
static NSString *cellID = #"FeedCell3" ;
FeedCell3 *cell = [tableView dequeueReusableCellWithIdentifier:cellID];
if (cell == nil) {
cell = [[FeedCell3 alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:cellID];
}
cell.nameLabel.text = #"RileyVLloyd";
cell.likeCountLabel.text = #"293 likes";
cell.commentCountLabel.text = #"55 comments";
//NSString* profileImageName = self.profileImage[indexPath.row%self.profileImage.count];
cell.profileImageView.image = _profileImage;
NSInteger idx = indexPath.row;
NSDictionary *t = self.twitterDataSource[idx];
cell.updateLabel.text = t[#"text"];
cell.dateLabel.text = #"1 day ago";
return cell;
}
An easier way to accomplish this would be to use the SDWebImage API. The API asynchronously loads the image, so then you won't have to worry about loading due to the main thread being used for loading the images. Also, this API only requires a couple lines of code below to be added to your UITableViewCell method.
NSString *aURL = t[#"user"][#"profile_image_url"];
[cell.profileImageView setImageWithURL:[NSURL URLWithString:aURL]
placeholderImage:[UIImage imageNamed:#"placeholder.png"]];
Your code does crash because you're trying to get a value for the key profile_image_url on username which is a string, hence the exception <__NSCFString ...> is not key value coding-compliant for the key profile_image_url.
Let's assume that what you really want to do here is retrieving the images for each tweet author.
You have to iterate over the statuses and, for each of them, extract the profile_image_url and create a UIImage with it.

Dispatch Queue and global NSMutableDictionary - Objective C

I'm trying to use a global NSMutableDictionary from a dispatch queue. However, the items keep coming back NULL.
What I'm trying to do is access an external json file with a dispatch_queue, then populate a UITableView with this info.
Here's what I have
vc.h:
#interface viewcontroller {
NSMutableDictionary *jsonArray;
}
vc.m:
#define kBgQueue dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0) //1
#define jsonTest [NSURL URLWithString:#"http://www.sometest.com/test.php"]
-(void)viewDidLoad {
dispatch_async(kBgQueue, ^{
NSData* data = [NSData dataWithContentsOfURL:
jsonTest];
[self performSelectorOnMainThread:#selector(fetchedData:)
withObject:data waitUntilDone:YES];
// if I run the log here, I can access jsonArry and the log prints correctly
NSLog(#"City: %#", [jsonArray objectForKey:#"city"];
});
}
-(NSMutableDictionary *)fetchedData:(NSData *)responseData {
NSError *error;
jsonArray = [NSJSONSerialization JSONObjectWithData:responseData options:kNilOptions error:&error];
return jsonArray;
}
/********************* Table formatting area **********************/
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView
{
return 1;
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
if (tableView == self.ipTable) {
if ([ipArray count] == 0){
return 1;
} else { // meta table
return [ipArray count];
}
} else { // IP Meta Data
return [jsonArray count];
}
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
if (tableView == self.myTable) {
NSString *CellIdentifier = NULL;
if ([ipArray count] == 0) {
CellIdentifier = #"No Cells";
} else {
CellIdentifier = #"IP Cell";
}
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil) {
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier];
}
if ([ipArray count] == 0)
{
[cell.textLabel setText:NSLocalizedString(#"None Found", nil)];
return cell;
} else {
IPAddr *theip = [ipArray objectAtIndex: [indexPath row]];
NSString *theipname = [theip ipName];
if ([theipname isEqualToString:#""]) {
[cell.textLabel setText: [theip ipNum]];
[cell.detailTextLabel setText:NSLocalizedString(#"noName", nil)];
} else {
[cell.textLabel setText: [theip ipName]];
[cell.detailTextLabel setText: [theip ipNum]];
}
return cell;
}
} else { // meta table
static NSString *CellIdentifier = #"metaCell";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil) {
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier];
}
// jsonArray content would go here to fill the cells.
/******************** something here to fill the cells using jsonArray ********************/
return cell;
}
} // END UITAbleViewCell
If I access the jsonArray inside the queue, it returns fine and prints the log for the city.
However, if I try to use it outside the queue, it returns NULL.
I'm trying to figure out what is happening, any ideas?
I need to use jsonArray in different methods in the same view, so I need it to be global.
I am fairly sure that the problem is that the data source methods (numberOfRowsInSection,
cellForRowAtIndexPath) are called before the background thread has finished and
filled the jsonArray. Therefore you have to reload the table view when the background
thread has finished:
- (void)viewDidLoad
{
[super viewDidLoad];
dispatch_async(kBgQueue, ^{
NSData *data = [NSData dataWithContentsOfURL:jsonTest];
NSError *error;
NSArray *tmpArray = [NSJSONSerialization JSONObjectWithData:data options:kNilOptions error:&error];
dispatch_sync(dispatch_get_main_queue(), ^{
// Assign new data to data source and reload the table view:
jsonArray = tmpArray;
[self.metaTableView reloadData];
});
});
}
So the table view would be empty initially, and reloaded later when the data has
arrived.
Try to call the other method(which is using your jsonarray) through nsnotification...I am not sure there might some other ideas/ways of doing this.But i am presenting what i have in my mind.
Put this code inside your fetchedData method,
NSNotificationCenter *nc = [NSNotificationCenter defaultCenter];
[nc addObserver:self selector:#selector(someMethod:) name:#"JSonDownloaded" object: jsonArray];
[[NSNotificationCenter defaultCenter] postNotificationName:#"JSonDownloaded" object: jsonArray];
-(void)someMethod:(NSNotification *)nspk
{
NSLog(#"%#",nspk.object);
//Only after this you can able to access the jsonArray.
}
Don't forget to unregister the observer.
jsonArray is just an instance variable, but not a property. Thus, assigning an object to it does not retain it, and the object may be released as soon as the program returns to the run loop.
I suggest replacing the iVar by #property (strong) NSMutableDictionary *jsonArray; and #synthesize jsonArray;, and assigning the object to it by self.jsonArray = ...
EDIT (see comment of Martin R below):
Thus, if you are not using ARC, assigning an object to it does not retain it, and the object may be released as soon as the program returns to the run loop.
In this case, I suggest replacing the iVar by #property (retain) NSMutableDictionary *jsonArray; and #synthesize jsonArray;, and assigning the object to it by self.jsonArray = ...

Error when swipe to delete a table cell

I'm trying to delete a row when user swipes. I got this error driving me crazy. I've spent last three hours trying to figure out why. But, I have got no clue so far.
Here is my code to accomplish that.
in .h
#import <UIKit/UIKit.h>
#import "CustomCell.h"
#interface FollowersTableViewController : UITableViewController
#property (nonatomic,strong)NSMutableArray *arrayWithUser ;
#end
and in .m i have this code .
#import "FollowersTableViewController.h"
#implementation FollowersTableViewController
#synthesize arrayWithUser ;
- (id)initWithStyle:(UITableViewStyle)style
{
self = [super initWithStyle:style];
if (self) {
// Custom initialization
}
return self;
}
-(void)viewDidLoad
{
[super viewDidLoad];
NSDictionary *dicUrlList= [NSDictionary dictionaryWithContentsOfFile:[[NSBundle mainBundle] pathForResource:#"Urls" ofType:#"plist"]];
NSString *baseURl = [dicUrlList objectForKey:#"urlWithUser"];
baseURl = [baseURl stringByAppendingFormat:#"getfollowers"];
NSURL *urlToGetFollowers = [NSURL URLWithString:baseURl];
NSURLRequest *request = [NSURLRequest requestWithURL:urlToGetFollowers];
NSError *error = nil ;
NSURLResponse *response = nil ;
NSData *data = [NSURLConnection sendSynchronousRequest:request returningResponse:&response error:&error];
arrayWithUser = [NSJSONSerialization JSONObjectWithData:data options:NSJSONReadingMutableLeaves error:nil];
}
- (void)viewDidUnload
{
[super viewDidUnload];
}
- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation
{
return (interfaceOrientation == UIInterfaceOrientationPortrait);
}
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView
{
return 1;
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
return [arrayWithUser count];
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *MyIdentifier = #"Cell";
CustomCell *customCell = (CustomCell *)[tableView dequeueReusableCellWithIdentifier:MyIdentifier];
if (customCell == nil)
{
customCell = [[CustomCell alloc] initWithFrame:CGRectMake(0, 0, 320, 50)] ;
}
NSDictionary *dicWithUser = [arrayWithUser objectAtIndex:indexPath.row];
NSString *photoUrl = [dicWithUser objectForKey:#"profilePhotoUrl"];
if(![photoUrl isEqualToString:#""])
[customCell.thumbnail setImageWithURL:[dicWithUser objectForKey:#"profilePhotoUrl"] placeholderImage:[UIImage imageNamed:#"placeholder.png"] ];
else
{
[customCell.thumbnail setImage:[UIImage imageNamed:#"placeholder.png"]];
}
customCell.titleLabel.text = [dicWithUser objectForKey:#"username"];
UIButton *buttonFollow = [UIButton buttonWithType:UIButtonTypeRoundedRect];
[buttonFollow setTitle:#"Follow" forState:UIControlStateNormal];
CGRect frame = buttonFollow.frame ;
frame = CGRectMake(200, 10, 60, 30);
buttonFollow.frame = frame ;
buttonFollow.tag = indexPath.row ;
[buttonFollow addTarget:self action:#selector(followButtonPressed:) forControlEvents:UIControlEventTouchUpInside];
customCell.accessoryView = buttonFollow ;
return customCell;
}
-(CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath
{
return 60 ;
}
- (BOOL)tableView:(UITableView *)tableView canEditRowAtIndexPath:(NSIndexPath *)indexPath
{
// Return NO if you do not want the specified item to be editable.
return YES;
}
- (void)tableView:(UITableView *)tableView commitEditingStyle:(UITableViewCellEditingStyle)editingStyle forRowAtIndexPath:(NSIndexPath *)indexPath
{
if (editingStyle == UITableViewCellEditingStyleDelete) {
// Delete the row from the data source
[arrayWithUser removeObjectAtIndex:indexPath.row];
}
}
So far,I'm able to see delete button, but when i pressed it gives me this error
[__NSCFArray removeObjectAtIndex:]: mutating method sent to immutable object.
Since I've already used NSMutableArray, I've no idea, why I'm getting this error?
I've already try to clean project. It didn't make any difference.
Your Json call returning an NSArray. You can go to create a mutableCopy - so you're will be able to use the "removeAtIndex.." method.
NSArray *rData = [NSJSONSerialization JSONObjectWithData:data options:NSJSONReadingMutableLeaves error:nil];
arrayWithUser = [rData mutableCopy];
The assignment to arrayWithUser from the JSON call is returning an NSArray not an NSMutableArray in viewDidLoad. Fix that.
Actually, your array(arrayWithUser) is strongly pointing to the array return by the JSONObjectWithData, as you don't have the ownership of the returned array, you can't remove its object.
You better do one thing ,take ownership of that array.
arrayWithUser = [[NSMutableArray alloc]arrayByAddingObjectsFromArray:[NSJSONSerialization JSONObjectWithData:data options:NSJSONReadingMutableLeaves error:nil]];
Just replace below line
arrayWithUser = [NSJSONSerialization JSONObjectWithData:data options:NSJSONReadingMutableLeaves error:nil];
with
arrayWithUser = [NSJSONSerialization JSONObjectWithData:data options:NSJSONReadingMutableContainers error:nil];
From Apple's documentation:
NSJSONReadingMutableContainers: Specifies that arrays and dictionaries are created as mutable objects.
NSJSONReadingMutableLeaves: Specifies that leaf strings in the JSON object graph are created as instances of NSMutableString.
You used in viwDidLoad
arrayWithUser = [NSJSONSerialization JSONObjectWithData:data options:NSJSONReadingMutableLeaves error:nil];
Lets try like this
NSMutableArray *userMutarr = [NSMutableArray alloc]initWithCapacity:3];
self.arrayWithUser = userMutarr;
[userMutarr release];
Then
self.arrayWithUser = [NSJSONSerialization JSONObjectWithData:data options:NSJSONReadingMutableLeaves error:nil];

iOS 5 JSON to tableView error

I was trying to parse a JSON string from USGS. However I get error "-[__NSCFDictionary objectAtIndex:]: unrecognized selector sent to instance 0x68a34c0" Basically, I would like to parse the USGS JSON into the tableview. Can anyone help? Here are my codes.
ViewController.h
#interface EarthquakeViewController : UITableViewController
{
NSArray *json;
NSDictionary *earthquakeReport;
}
ViewController.m
- (void)viewDidLoad
{
//content = [NSMutableArray arrayWithObjects:#"one", #"two", nil];
[super viewDidLoad];
[self fetchReports];
}
- (void)fetchReports
{
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
NSData* data = [NSData dataWithContentsOfURL:
[NSURL URLWithString: #"http://earthquake.usgs.gov/earthquakes/feed/geojson/all/hour"]];
NSError* error;
json = [NSJSONSerialization JSONObjectWithData:data options:NSJSONReadingAllowFragments error:&error];
NSLog(#"%#", json);
dispatch_async(dispatch_get_main_queue(), ^{
[self.tableView reloadData];
});
});
}
- (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 json.count;
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *CellIdentifier = #"Cell";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil) {
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier];
}
earthquakeReport = [json objectAtIndex:indexPath.row];
NSString *country = [[[earthquakeReport objectForKey:#"features"] objectForKey:#"properties"] objectForKey:#"place"];
NSString *mag = [[[earthquakeReport objectForKey:#"features"] objectForKey:#"properties"] objectForKey:#"mag"];
cell.textLabel.text = country;
cell.detailTextLabel.text = mag;
return cell;
}
The error is showing at the line earthquakeReport = [json objectAtIndex:indexPath.row];
If you look at your JSON data in a web browser (http://earthquake.usgs.gov/earthquakes/feed/geojson/all/hour) you should notice that the array you are trying to access is not at the root level. The root level is a dictionary, and the array you're looking for is in the "features" key.
To access it properly, first change your json ivar declaration into an NSDictionary:
NSDictionary *json;
Then, in your tableView:cellForRowAtIndexPath: method, access the report thusly:
earthquakeReport = [[json objectForKey:#"features"] objectAtIndex:indexPath.row];