This is the given API that I have to parse.I also have to show the name on my UITableView. I want to fetch the value of name n show it on table view.
This is the code:
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
return [ episodes count];
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *simpleTableIdentifier = #"SimpleTableItem";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:simpleTableIdentifier];
if (cell == nil) {
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:simpleTableIdentifier];
}
cell.textLabel.text = [[ episodes objectAtIndex:indexPath.row] objectForKey:#"names"];
return cell;
}
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
UIAlertView *logoutConfirm = [[UIAlertView alloc]initWithTitle:#"Toilet Finder" message:#"" delegate:self cancelButtonTitle:nil otherButtonTitles:#"Display on Map",#"Route",#"Cancel", nil];
logoutConfirm.tag = 111;
[logoutConfirm show];
}
- (void) parseJSONWithURL:(NSURL *) jsonURL
{
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_BACKGROUND, 0);
[UIApplication sharedApplication].networkActivityIndicatorVisible = YES;
dispatch_async(queue, ^{
NSError *error = nil;
NSString *json = [NSString stringWithContentsOfURL:jsonURL
encoding:NSASCIIStringEncoding error:&error];
if (error == nil){
WebInServe=#"http://api.kivaws.org/v1/loans/search.json?status=fundraising";
NSLog(#"URL %#", WebInServe);
NSURL *Final_Url = [NSURL URLWithString:WebInServe];
NSData* jsonData = [NSData dataWithContentsOfURL: Final_Url];
NSError* error;
jsonDict = [NSJSONSerialization JSONObjectWithData:jsonData options:kNilOptions error:&error];
NSLog(#"What is contain let me check %#",jsonDict);
NSArray* latestLoans = [jsonDict objectForKey:#"loans"];
NSLog(#"loans in array: %#", latestLoans);
episodes = [[jsonDict valueForKey:#"latestLoans"]valueForKey:#"name"];
if (error == nil)
{
dispatch_async(dispatch_get_main_queue(), ^{
[UIApplication sharedApplication].networkActivityIndicatorVisible = NO;
episodes = [[jsonDict valueForKey:#"latestLoans"]valueForKey:#"name"];
[tableView reloadData];
});
}
I have tried using:
episodes = [[jsonDict valueForKey:#"latestLoans"]objectForKey:#"name"];
I have used several tymes objectForKey and valueForkey but in the above code and in the
cell.textLabel.text = [[ episodes objectAtIndex:indexPath.row] objectForKey:#"names"];
line of code in cellForRowAtIndexPath: method
but the data is not displayed on tableview...indeed it is displayed on NSLog...
Why using dispatch_queues instead of using the dedicated async API of NSURLConnection?
As explained in Apple's Concurrency Programming Guide, when there already is a dedicated API to execute some task in a concurrent manner, avoid to make your own concurrent version by wrapping a synchronous call (like +[NSString stringWithContentOfURL:] in your case) inside asynchronous queues (like dispatch_async on a concurrent queue in your case).
Moreover, your latestLoans variable (which you then NSLog) extracts its content from a key called #"loans", but in the next line when you try to extract the episodes, you extract it from the dictionary assumed to be associated to a #"latestLoans" key. There is probably some discrepancy in your code there, isn't it? That's probably your main problem in your code.
And finally sometimes you use valueForKey: (which is a generic Key-Value-Coding method that works using introspection, not at all a dedicated NSDictionary-specific method) instead of objectForKey: (which is what you need to use when accessing objects from an NSDictionary).
Sure valueForKey: will work but objectForKey: being the dedicated method for NSDictionary it will be more efficient.
Personally I even prefer the new "Objective-C Modern notation" for Object Literals, namely dict[key] that the compiler will translate to objectForKey: automatically for you (this [] notation is only some syntaxic sugar to be more concise)
So here comes my code suggestion:
...
[NSURLConnection sendAsynchronousRequest:request queue:[NSOperationQueue mainQueue] completionHandler:^(NSURLResponse* resp, NSData* jsonData, NSError* err) {
...
NSDictionary* jsonDict = [NSJSONSerialization JSONObjectWithData:jsonData options:kNilOptions error:&error];
NSArray* latestLoans = jsonDict[#"loans"];
self.episodes = latestLoans[#"name"];
[self.tableView reloadData];
}];
It seems the problem is here:
episodes = [[jsonDict valueForKey:#"latestLoans"]valueForKey:#"name"];
The root of the JSON response has only two items - "paging" and "loans". You are asking for "latestLoans". Since there is no such item in the root level of the JSON, you will get nil in episodes.
I think you should use the call you performed for the logging:
NSArray* latestLoans = [jsonDict objectForKey:#"loans"];
Try with
episodes = [jsonDict objectForKey:#"loans"];
instead of
episodes = [[jsonDict valueForKey:#"latestLoans"]valueForKey:#"name"];
Related
How send data to tableview in iOS with objective-C? I am trying to solve my problem very long time but result is not correct. My tableview is still empty. What I'm doing wrong? Below is my implementation file.
import "PlacesViewController.h"
#implementation PlacesViewController
#synthesize places;
- (void)viewDidLoad
{
[super viewDidLoad];
// Set this view controller object as the delegate and data source for the table view
self.listTableView.delegate = self;
self.listTableView.dataSource = self;
}
-(void)queryGooglePlaces
{
//Build the url string to send to Google
NSString *url = [NSString stringWithFormat:#"https://maps.googleapis.com/maps/api/place/nearbysearch/json?location=51.503186,-0.126446&radius=5000&types=food#|restaurant|bar&keyword=vegetarian&key=myOwnKEY"];
url = [url stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding];
NSLog(#"%#", url);
//Formulate the string as a URL object.
NSURL *googleRequestURL=[NSURL URLWithString:url];
// Retrieve the results of the URL.
dispatch_async(kBgQueue, ^{
NSData* data = [NSData dataWithContentsOfURL: googleRequestURL];
dispatch_sync(dispatch_get_main_queue(), ^{
[self fetchedData:data];
});
});
}
-(void)fetchedData:(NSData *)responseData {
//parse out the json data
NSError* error;
NSDictionary* json = [NSJSONSerialization
JSONObjectWithData:responseData
options:kNilOptions
error:&error];
//The results from Google will be an array obtained from the NSDictionary object with the key "results".
self.places = [json objectForKey:#"results"];
//Write out the data to the console.
NSLog(#"Google Data: %#", json);
}
#pragma mark - Table view data source
- (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.places count];
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *CellIdentifier = #"Cell";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier forIndexPath:indexPath];
NSDictionary *tempDictionary= [self.places objectAtIndex:indexPath.row];
cell.textLabel.text = [tempDictionary objectForKey:#"name"];
return cell;
}
#end
Whenever you update the datasource, you need to call [self.tableView reloadData]
So it should be like this
-(void)fetchedData:(NSData *)responseData {
//parse out the json data
NSError* error;
NSDictionary* json = [NSJSONSerialization
JSONObjectWithData:responseData
options:kNilOptions
error:&error];
//The results from Google will be an array obtained from the NSDictionary object with the key "results".
self.places = [json objectForKey:#"results"];
// NOTE - ADDED RELOAD
[self.tableView reloadData];
//Write out the data to the console.
NSLog(#"Google Data: %#", json);
}
For more information about reloadData see the answer
After you update the data source you should reload the table view.
self.places = [json objectForKey:#"results"];
[self.tableView reloadData];
I'm trying to parse a json-file containing links to images. This is my code:
#import "Pictures.h"
#import "DEMONavigationController.h"
#import "PicturesObject.h"
#interface Pictures ()
{
NSInteger refreshIndex;
NSArray *images;
NSMutableArray *jsonIs;
NSArray *items;
IBOutlet UIImageView *imagesinsta;
}
#end
#implementation Pictures
- (void)viewDidLoad
{
[super viewDidLoad];
self.title = #"Pictures";
self.navigationItem.leftBarButtonItem = [[UIBarButtonItem alloc] initWithTitle:#"Menu"
style:UIBarButtonItemStylePlain
target:(DEMONavigationController *)self.navigationController
action:#selector(showMenu)];
// ***************** FETCHING DATA ******************* //
NSURL *URL = [NSURL URLWithString:[NSString stringWithFormat:#"link-to-json.php?image=Image"]];
NSData *data= [NSData dataWithContentsOfURL:URL];
if (data == nil) {
return;
}
NSError* error;
items = [NSJSONSerialization JSONObjectWithData:data
options:kNilOptions
error:&error];
NSLog(#"Json : %#",jsonIs);
if (jsonIs != nil) {
NSMutableDictionary* aDict = jsonIs[0];
NSString *item_media = [aDict objectForKey:#"link"];
}
// ***************** FETCHING DATA ******************* //
}
#pragma mark - Table view data source
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *simpleTableIdentifier = #"PicturesObject";
PicturesObject *cell = (PicturesObject *)[tableView dequeueReusableCellWithIdentifier:simpleTableIdentifier];
if (cell == nil)
{
NSArray *nib = [[NSBundle mainBundle] loadNibNamed:#"PicturesObject" owner:self options:nil];
cell = [nib objectAtIndex:0];
}
// The element in the array is going to be a dictionary. I JUST KNOW THIS. The key for the tweet is "text".
NSDictionary *item = [items objectAtIndex:indexPath.row];
NSString *item_media = [item objectForKey:#"link"];
return cell;
}
- (void)issueLoadRequest
{
// Dispatch this block asynchronosly. The block gets JSON data from the specified URL and performs the proper selector when done.
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
NSData* data = [NSData dataWithContentsOfURL:[NSURL URLWithString:#"link-to-json.php?image=Image"]];
[self performSelectorOnMainThread:#selector(receiveData:) withObject:data waitUntilDone:YES];
});
}
- (void)receiveData:(NSData *)data {
// When we have the data, we serialize it into native cocoa objects. (The outermost element from twitter is
// going to be an array. I JUST KNOW THIS. Reload the tableview once we have the data.
self.tweets = [NSJSONSerialization JSONObjectWithData:data options:kNilOptions error:nil];
[self.myTableView reloadData];
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
return self.tweets.count;
}
#end
This is how my custom PicturesObject table view look like:
But when I launch my app I only get a screen like this:
Json Data is similar to Dictionary and not an array. Instead of using
NSArray items
use
NSDictionary items
and then extract the the relevant data from dictionary.
I'm parsing JSON from my API which works fine but every time I select a row and get to the detail view he shows me the product with is supposed to be linked to the product shown at the very bottom. I don't understand why it isn't parsing the product id for the specific row because I have set it to parse for indexPath.row
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *neuheitenCell = #"neuheitenCell";
CJHomeTableCell *cell = [tableView dequeueReusableCellWithIdentifier:neuheitenCell];
wareID = [[arrayArtikelWareID objectAtIndex:indexPath.row] objectForKey:#"ware_id"];
NSLog(#"Waren ID: %#", wareID);
cell.artikelName.text = [[arrayArtikelName objectAtIndex:indexPath.row] objectForKey:#"name"];
cell.artikelHersteller.text = [[arrayArtikelHersteller objectAtIndex:indexPath.row] objectForKey:#"lieferant_name"];
return cell;
}
**EDIT:
-(void) parseJSONWithURL: (NSURL *) jsonURL {
dispatch_async(mainThreadQueue, ^{
NSError *error = nil;
[UIApplication sharedApplication].networkActivityIndicatorVisible = YES;
NSString *json =[NSString stringWithContentsOfURL:jsonURL encoding:NSUTF8StringEncoding error:&error];
if (error == nil) {
NSData *jsonData = [json dataUsingEncoding:NSUTF8StringEncoding];
dictionaryNewStuff = [NSJSONSerialization JSONObjectWithData:jsonData options:kNilOptions error:&error];
if (error == nil) {
dispatch_async(dispatch_get_main_queue(), ^{
arrayNeuheiten = [[dictionaryNewStuff valueForKey:#"newstuff"] valueForKey:#"Neuheiten"];
arrayArtikelBild = [[dictionaryNewStuff valueForKey:#"newstuff"] valueForKey:#"Neuheiten"];
[neuheitenTableView reloadData];
[UIApplication sharedApplication].networkActivityIndicatorVisible = NO;
});
}
}
});
}
JSON can be viewed at my API
Does anyone know how to fix this?
Thanks in advance!
Could you try using following code ?
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *neuheitenCell = #"neuheitenCell";
CJHomeTableCell *cell = [tableView dequeueReusableCellWithIdentifier:neuheitenCell];
NSDictionary *object = [arrayNeuheiten objectAtIndex:indexPath.row];
NSString *wareID = [object objectForKey:#"ware_id"];
NSLog(#"Waren ID: %#", wareID);
cell.artikelName.text = [object objectForKey:#"name"];
cell.artikelHersteller.text = [object objectForKey:#"lieferant_name"];
return cell;
}
Why do you need two arrays for holding the data ? I mean following lines " arrayNeuheiten = [[dictionaryNewStuff valueForKey:#"newstuff"] valueForKey:#"Neuheiten"]; arrayArtikelBild = [[dictionaryNewStuff valueForKey:#"newstuff"] valueForKey:#"Neuheiten"]; "
Also where do you assign anything to arrayArtikelWareID ,arrayArtikelName ,arrayArtikelHersteller
I have a JSON string (from PHP's json_encode() that looks like this:
[{"id": "1", "name":"Aaa"}, {"id": "2", "name":"Bbb"}]
I want to parse this into some sort of data structure for my iPhone app. I guess the best thing for me would be to have an array of dictionaries, so the 0th element in the array is a dictionary with keys "id" => "1" and "name" => "Aaa".
I do not understand how the NSJSONSerialization stores the data though. Here is my code so far:
NSError *e = nil;
NSDictionary *JSON = [NSJSONSerialization
JSONObjectWithData: data
options: NSJSONReadingMutableContainers
error: &e];
This is just something I saw as an example on another website. I have been trying to get a read on the JSON object by printing out the number of elements and things like that, but I am always getting EXC_BAD_ACCESS.
How do I use NSJSONSerialization to parse the JSON above, and turn it into the data structure I mentioned?
Your root json object is not a dictionary but an array:
[{"id": "1", "name":"Aaa"}, {"id": "2", "name":"Bbb"}]
This might give you a clear picture of how to handle it:
NSError *e = nil;
NSArray *jsonArray = [NSJSONSerialization JSONObjectWithData: data options: NSJSONReadingMutableContainers error: &e];
if (!jsonArray) {
NSLog(#"Error parsing JSON: %#", e);
} else {
for(NSDictionary *item in jsonArray) {
NSLog(#"Item: %#", item);
}
}
This is my code for checking if the received json is an array or dictionary:
NSError *jsonError = nil;
id jsonObject = [NSJSONSerialization JSONObjectWithData:jsonData options:kNilOptions error:&jsonError];
if ([jsonObject isKindOfClass:[NSArray class]]) {
NSLog(#"its an array!");
NSArray *jsonArray = (NSArray *)jsonObject;
NSLog(#"jsonArray - %#",jsonArray);
}
else {
NSLog(#"its probably a dictionary");
NSDictionary *jsonDictionary = (NSDictionary *)jsonObject;
NSLog(#"jsonDictionary - %#",jsonDictionary);
}
I have tried this for options:kNilOptions and NSJSONReadingMutableContainers and works correctly for both.
Obviously, the actual code cannot be this way where I create the NSArray or NSDictionary pointer within the if-else block.
It works for me. Your data object is probably nil and, as rckoenes noted, the root object should be a (mutable) array. See this code:
NSString *jsonString = #"[{\"id\": \"1\", \"name\":\"Aaa\"}, {\"id\": \"2\", \"name\":\"Bbb\"}]";
NSData *jsonData = [jsonString dataUsingEncoding:NSUTF8StringEncoding];
NSError *e = nil;
NSMutableArray *json = [NSJSONSerialization JSONObjectWithData:jsonData options:NSJSONReadingMutableContainers error:&e];
NSLog(#"%#", json);
(I had to escape the quotes in the JSON string with backslashes.)
Your code seems fine except the result is an NSArray, not an NSDictionary, here is an example:
The first two lines just creates a data object with the JSON, the same as you would get reading it from the net.
NSString *jsonString = #"[{\"id\": \"1\", \"name\":\"Aaa\"}, {\"id\": \"2\", \"name\":\"Bbb\"}]";
NSData *jsonData = [jsonString dataUsingEncoding:NSUTF8StringEncoding];
NSError *e;
NSMutableArray *jsonList = [NSJSONSerialization JSONObjectWithData:jsonData options:NSJSONReadingMutableContainers error:&e];
NSLog(#"jsonList: %#", jsonList);
NSLog contents (a list of dictionaries):
jsonList: (
{
id = 1;
name = Aaa;
},
{
id = 2;
name = Bbb;
}
)
[{"id": "1", "name":"Aaa"}, {"id": "2", "name":"Bbb"}]
In above JSON data, you are showing that we have an array contaning the number of dictionaries.
You need to use this code for parsing it:
NSError *e = nil;
NSArray *JSONarray = [NSJSONSerialization JSONObjectWithData: data options: NSJSONReadingMutableContainers error: &e];
for(int i=0;i<[JSONarray count];i++)
{
NSLog(#"%#",[[JSONarray objectAtIndex:i]objectForKey:#"id"]);
NSLog(#"%#",[[JSONarray objectAtIndex:i]objectForKey:#"name"]);
}
For swift 3/3+
//Pass The response data & get the Array
let jsonData = try JSONSerialization.jsonObject(with: data!, options: .allowFragments) as! [AnyObject]
print(jsonData)
// considering we are going to get array of dictionary from url
for item in jsonData {
let dictInfo = item as! [String:AnyObject]
print(dictInfo["id"])
print(dictInfo["name"])
}
The following code fetches a JSON object from a webserver, and parses it to an NSDictionary. I have used the openweathermap API that returns a simple JSON response for this example. For keeping it simple, this code uses synchronous requests.
NSString *urlString = #"http://api.openweathermap.org/data/2.5/weather?q=London,uk"; // The Openweathermap JSON responder
NSURL *url = [[NSURL alloc]initWithString:urlString];
NSURLRequest *request = [NSURLRequest requestWithURL:url];
NSURLResponse *response;
NSData *GETReply = [NSURLConnection sendSynchronousRequest:request returningResponse:&response error:nil];
NSDictionary *res = [NSJSONSerialization JSONObjectWithData:GETReply options:NSJSONReadingMutableLeaves|| NSJSONReadingMutableContainers error:nil];
Nslog(#"%#",res);
#rckoenes already showed you how to correctly get your data from the JSON string.
To the question you asked: EXC_BAD_ACCESS almost always comes when you try to access an object after it has been [auto-]released. This is not specific to JSON [de-]serialization but, rather, just has to do with you getting an object and then accessing it after it's been released. The fact that it came via JSON doesn't matter.
There are many-many pages describing how to debug this -- you want to Google (or SO) obj-c zombie objects and, in particular, NSZombieEnabled, which will prove invaluable to you in helping determine the source of your zombie objects. ("Zombie" is what it's called when you release an object but keep a pointer to it and try to reference it later.)
Swift 2.0 on Xcode 7 (Beta) with do/try/catch block:
// MARK: NSURLConnectionDataDelegate
func connectionDidFinishLoading(connection:NSURLConnection) {
do {
if let response:NSDictionary = try NSJSONSerialization.JSONObjectWithData(receivedData, options:NSJSONReadingOptions.MutableContainers) as? Dictionary<String, AnyObject> {
print(response)
} else {
print("Failed...")
}
} catch let serializationError as NSError {
print(serializationError)
}
}
NOTE: For Swift 3.
Your JSON String is returning Array instead of Dictionary. Please try out the following:
//Your JSON String to be parsed
let jsonString = "[{\"id\": \"1\", \"name\":\"Aaa\"}, {\"id\": \"2\", \"name\":\"Bbb\"}]";
//Converting Json String to NSData
let data = jsonString.data(using: .utf8)
do {
//Parsing data & get the Array
let jsonData = try JSONSerialization.jsonObject(with: data!, options: .allowFragments) as! [AnyObject]
//Print the whole array object
print(jsonData)
//Get the first object of the Array
let firstPerson = jsonData[0] as! [String:Any]
//Looping the (key,value) of first object
for (key, value) in firstPerson {
//Print the (key,value)
print("\(key) - \(value) ")
}
} catch let error as NSError {
//Print the error
print(error)
}
#import "homeViewController.h"
#import "detailViewController.h"
#interface homeViewController ()
#end
#implementation homeViewController
- (id)initWithStyle:(UITableViewStyle)style
{
self = [super initWithStyle:style];
if (self) {
// Custom initialization
}
return self;
}
- (void)viewDidLoad
{
[super viewDidLoad];
self.tableView.frame = CGRectMake(0, 20, 320, 548);
self.title=#"Jason Assignment";
// 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;
[self clientServerCommunication];
}
-(void)clientServerCommunication
{
NSURL *url = [NSURL URLWithString:#"http://182.72.122.106/iphonetest/getTheData.php"];
NSURLRequest *req = [NSURLRequest requestWithURL:url];
NSURLConnection *connection = [[NSURLConnection alloc]initWithRequest:req delegate:self];
if (connection)
{
webData = [[NSMutableData alloc]init];
}
}
- (void)connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)response
{
[webData setLength:0];
}
- (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data
{
[webData appendData:data];
}
- (void)connectionDidFinishLoading:(NSURLConnection *)connection
{
NSDictionary *responseDict = [NSJSONSerialization JSONObjectWithData:webData options:0 error:nil];
/*Third party API
NSString *respStr = [[NSString alloc]initWithData:webData encoding:NSUTF8StringEncoding];
SBJsonParser *objSBJson = [[SBJsonParser alloc]init];
NSDictionary *responseDict = [objSBJson objectWithString:respStr]; */
resultArray = [[NSArray alloc]initWithArray:[responseDict valueForKey:#"result"]];
NSLog(#"resultArray: %#",resultArray);
[self.tableView reloadData];
}
- (void)didReceiveMemoryWarning
{
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}
#pragma mark - Table view data source
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView
{
//#warning Potentially incomplete method implementation.
// Return the number of sections.
return 1;
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
//#warning Incomplete method implementation.
// Return the number of rows in the section.
return [resultArray count];
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *CellIdentifier = #"Cell";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil) {
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleSubtitle reuseIdentifier:CellIdentifier];
}
// Configure the cell...
cell.textLabel.text = [[resultArray objectAtIndex:indexPath.row] valueForKey:#"name"];
cell.detailTextLabel.text = [[resultArray objectAtIndex:indexPath.row] valueForKey:#"designation"];
NSData *imageData = [NSData dataWithContentsOfURL:[NSURL URLWithString:[[resultArray objectAtIndex:indexPath.row] valueForKey:#"image"]]];
cell.imageview.image = [UIImage imageWithData:imageData];
return cell;
}
/*
// Override to support conditional editing of the table view.
- (BOOL)tableView:(UITableView *)tableView canEditRowAtIndexPath:(NSIndexPath *)indexPath
{
// Return NO if you do not want the specified item to be editable.
return YES;
}
*/
/*
// Override to support editing the table view.
- (void)tableView:(UITableView *)tableView commitEditingStyle:(UITableViewCellEditingStyle)editingStyle forRowAtIndexPath:(NSIndexPath *)indexPath
{
if (editingStyle == UITableViewCellEditingStyleDelete) {
// Delete the row from the data source
[tableView deleteRowsAtIndexPaths:#[indexPath] withRowAnimation:UITableViewRowAnimationFade];
}
else if (editingStyle == UITableViewCellEditingStyleInsert) {
// Create a new instance of the appropriate class, insert it into the array, and add a new row to the table view
}
}
*/
/*
// Override to support rearranging the table view.
- (void)tableView:(UITableView *)tableView moveRowAtIndexPath:(NSIndexPath *)fromIndexPath toIndexPath:(NSIndexPath *)toIndexPath
{
}
*/
/*
// Override to support conditional rearranging of the table view.
- (BOOL)tableView:(UITableView *)tableView canMoveRowAtIndexPath:(NSIndexPath *)indexPath
{
// Return NO if you do not want the item to be re-orderable.
return YES;
}
*/
#pragma mark - Table view delegate
// In a xib-based application, navigation from a table can be handled in -tableView:didSelectRowAtIndexPath:
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
// Navigation logic may go here, for example:
//Create the next view controller.
detailViewController *detailViewController1 = [[detailViewController alloc]initWithNibName:#"detailViewController" bundle:nil];
//detailViewController *detailViewController = [[detailViewController alloc] initWithNibName:#"detailViewController" bundle:nil];
// Pass the selected object to the new view controller.
// Push the view controller.
detailViewController1.nextDict = [[NSDictionary alloc]initWithDictionary:[resultArray objectAtIndex:indexPath.row]];
[self.navigationController pushViewController:detailViewController1 animated:YES];
// Pass the selected object to the new view controller.
// Push the view controller.
// [self.navigationController pushViewController:detailViewController animated:YES];
}
#end
- (void)viewDidLoad
{
[super viewDidLoad];
// Do any additional setup after loading the view from its nib.
empName.text=[nextDict valueForKey:#"name"];
deptlbl.text=[nextDict valueForKey:#"department"];
designationLbl.text=[nextDict valueForKey:#"designation"];
idLbl.text=[nextDict valueForKey:#"id"];
salaryLbl.text=[nextDict valueForKey:#"salary"];
NSString *ImageURL = [nextDict valueForKey:#"image"];
NSData *imageData = [NSData dataWithContentsOfURL:[NSURL URLWithString:ImageURL]];
image.image = [UIImage imageWithData:imageData];
}
The issue seems to be with autorelease of objects. NSJSONSerialization JSONObjectWithData is obviously creating some autoreleased objects and passing it back to you. If you try to take that on to a different thread, it will not work since it cannot be deallocated on a different thread.
Trick might be to try doing a mutable copy of that dictionary or array and use it.
NSError *e = nil;
id jsonObject = [NSJSONSerialization
JSONObjectWithData: data
options: NSJSONReadingMutableContainers
error: &e] mutableCopy];
Treating a NSDictionary as NSArray will not result in Bad access exception but instead will probably crash when a method call is made.
Also, may be the options do not really matter here but it is better to give NSJSONReadingMutableContainers | NSJSONReadingMutableContainers |
NSJSONReadingAllowFragments but even if they are autoreleased objects it may not solve this issue.
bad example, should be something like this
{"id":1, "name":"something as name"}
number and string are mixed.
I have a JSON string (from PHP's json_encode() that looks like this:
[{"id": "1", "name":"Aaa"}, {"id": "2", "name":"Bbb"}]
I want to parse this into some sort of data structure for my iPhone app. I guess the best thing for me would be to have an array of dictionaries, so the 0th element in the array is a dictionary with keys "id" => "1" and "name" => "Aaa".
I do not understand how the NSJSONSerialization stores the data though. Here is my code so far:
NSError *e = nil;
NSDictionary *JSON = [NSJSONSerialization
JSONObjectWithData: data
options: NSJSONReadingMutableContainers
error: &e];
This is just something I saw as an example on another website. I have been trying to get a read on the JSON object by printing out the number of elements and things like that, but I am always getting EXC_BAD_ACCESS.
How do I use NSJSONSerialization to parse the JSON above, and turn it into the data structure I mentioned?
Your root json object is not a dictionary but an array:
[{"id": "1", "name":"Aaa"}, {"id": "2", "name":"Bbb"}]
This might give you a clear picture of how to handle it:
NSError *e = nil;
NSArray *jsonArray = [NSJSONSerialization JSONObjectWithData: data options: NSJSONReadingMutableContainers error: &e];
if (!jsonArray) {
NSLog(#"Error parsing JSON: %#", e);
} else {
for(NSDictionary *item in jsonArray) {
NSLog(#"Item: %#", item);
}
}
This is my code for checking if the received json is an array or dictionary:
NSError *jsonError = nil;
id jsonObject = [NSJSONSerialization JSONObjectWithData:jsonData options:kNilOptions error:&jsonError];
if ([jsonObject isKindOfClass:[NSArray class]]) {
NSLog(#"its an array!");
NSArray *jsonArray = (NSArray *)jsonObject;
NSLog(#"jsonArray - %#",jsonArray);
}
else {
NSLog(#"its probably a dictionary");
NSDictionary *jsonDictionary = (NSDictionary *)jsonObject;
NSLog(#"jsonDictionary - %#",jsonDictionary);
}
I have tried this for options:kNilOptions and NSJSONReadingMutableContainers and works correctly for both.
Obviously, the actual code cannot be this way where I create the NSArray or NSDictionary pointer within the if-else block.
It works for me. Your data object is probably nil and, as rckoenes noted, the root object should be a (mutable) array. See this code:
NSString *jsonString = #"[{\"id\": \"1\", \"name\":\"Aaa\"}, {\"id\": \"2\", \"name\":\"Bbb\"}]";
NSData *jsonData = [jsonString dataUsingEncoding:NSUTF8StringEncoding];
NSError *e = nil;
NSMutableArray *json = [NSJSONSerialization JSONObjectWithData:jsonData options:NSJSONReadingMutableContainers error:&e];
NSLog(#"%#", json);
(I had to escape the quotes in the JSON string with backslashes.)
Your code seems fine except the result is an NSArray, not an NSDictionary, here is an example:
The first two lines just creates a data object with the JSON, the same as you would get reading it from the net.
NSString *jsonString = #"[{\"id\": \"1\", \"name\":\"Aaa\"}, {\"id\": \"2\", \"name\":\"Bbb\"}]";
NSData *jsonData = [jsonString dataUsingEncoding:NSUTF8StringEncoding];
NSError *e;
NSMutableArray *jsonList = [NSJSONSerialization JSONObjectWithData:jsonData options:NSJSONReadingMutableContainers error:&e];
NSLog(#"jsonList: %#", jsonList);
NSLog contents (a list of dictionaries):
jsonList: (
{
id = 1;
name = Aaa;
},
{
id = 2;
name = Bbb;
}
)
[{"id": "1", "name":"Aaa"}, {"id": "2", "name":"Bbb"}]
In above JSON data, you are showing that we have an array contaning the number of dictionaries.
You need to use this code for parsing it:
NSError *e = nil;
NSArray *JSONarray = [NSJSONSerialization JSONObjectWithData: data options: NSJSONReadingMutableContainers error: &e];
for(int i=0;i<[JSONarray count];i++)
{
NSLog(#"%#",[[JSONarray objectAtIndex:i]objectForKey:#"id"]);
NSLog(#"%#",[[JSONarray objectAtIndex:i]objectForKey:#"name"]);
}
For swift 3/3+
//Pass The response data & get the Array
let jsonData = try JSONSerialization.jsonObject(with: data!, options: .allowFragments) as! [AnyObject]
print(jsonData)
// considering we are going to get array of dictionary from url
for item in jsonData {
let dictInfo = item as! [String:AnyObject]
print(dictInfo["id"])
print(dictInfo["name"])
}
The following code fetches a JSON object from a webserver, and parses it to an NSDictionary. I have used the openweathermap API that returns a simple JSON response for this example. For keeping it simple, this code uses synchronous requests.
NSString *urlString = #"http://api.openweathermap.org/data/2.5/weather?q=London,uk"; // The Openweathermap JSON responder
NSURL *url = [[NSURL alloc]initWithString:urlString];
NSURLRequest *request = [NSURLRequest requestWithURL:url];
NSURLResponse *response;
NSData *GETReply = [NSURLConnection sendSynchronousRequest:request returningResponse:&response error:nil];
NSDictionary *res = [NSJSONSerialization JSONObjectWithData:GETReply options:NSJSONReadingMutableLeaves|| NSJSONReadingMutableContainers error:nil];
Nslog(#"%#",res);
#rckoenes already showed you how to correctly get your data from the JSON string.
To the question you asked: EXC_BAD_ACCESS almost always comes when you try to access an object after it has been [auto-]released. This is not specific to JSON [de-]serialization but, rather, just has to do with you getting an object and then accessing it after it's been released. The fact that it came via JSON doesn't matter.
There are many-many pages describing how to debug this -- you want to Google (or SO) obj-c zombie objects and, in particular, NSZombieEnabled, which will prove invaluable to you in helping determine the source of your zombie objects. ("Zombie" is what it's called when you release an object but keep a pointer to it and try to reference it later.)
Swift 2.0 on Xcode 7 (Beta) with do/try/catch block:
// MARK: NSURLConnectionDataDelegate
func connectionDidFinishLoading(connection:NSURLConnection) {
do {
if let response:NSDictionary = try NSJSONSerialization.JSONObjectWithData(receivedData, options:NSJSONReadingOptions.MutableContainers) as? Dictionary<String, AnyObject> {
print(response)
} else {
print("Failed...")
}
} catch let serializationError as NSError {
print(serializationError)
}
}
NOTE: For Swift 3.
Your JSON String is returning Array instead of Dictionary. Please try out the following:
//Your JSON String to be parsed
let jsonString = "[{\"id\": \"1\", \"name\":\"Aaa\"}, {\"id\": \"2\", \"name\":\"Bbb\"}]";
//Converting Json String to NSData
let data = jsonString.data(using: .utf8)
do {
//Parsing data & get the Array
let jsonData = try JSONSerialization.jsonObject(with: data!, options: .allowFragments) as! [AnyObject]
//Print the whole array object
print(jsonData)
//Get the first object of the Array
let firstPerson = jsonData[0] as! [String:Any]
//Looping the (key,value) of first object
for (key, value) in firstPerson {
//Print the (key,value)
print("\(key) - \(value) ")
}
} catch let error as NSError {
//Print the error
print(error)
}
#import "homeViewController.h"
#import "detailViewController.h"
#interface homeViewController ()
#end
#implementation homeViewController
- (id)initWithStyle:(UITableViewStyle)style
{
self = [super initWithStyle:style];
if (self) {
// Custom initialization
}
return self;
}
- (void)viewDidLoad
{
[super viewDidLoad];
self.tableView.frame = CGRectMake(0, 20, 320, 548);
self.title=#"Jason Assignment";
// 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;
[self clientServerCommunication];
}
-(void)clientServerCommunication
{
NSURL *url = [NSURL URLWithString:#"http://182.72.122.106/iphonetest/getTheData.php"];
NSURLRequest *req = [NSURLRequest requestWithURL:url];
NSURLConnection *connection = [[NSURLConnection alloc]initWithRequest:req delegate:self];
if (connection)
{
webData = [[NSMutableData alloc]init];
}
}
- (void)connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)response
{
[webData setLength:0];
}
- (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data
{
[webData appendData:data];
}
- (void)connectionDidFinishLoading:(NSURLConnection *)connection
{
NSDictionary *responseDict = [NSJSONSerialization JSONObjectWithData:webData options:0 error:nil];
/*Third party API
NSString *respStr = [[NSString alloc]initWithData:webData encoding:NSUTF8StringEncoding];
SBJsonParser *objSBJson = [[SBJsonParser alloc]init];
NSDictionary *responseDict = [objSBJson objectWithString:respStr]; */
resultArray = [[NSArray alloc]initWithArray:[responseDict valueForKey:#"result"]];
NSLog(#"resultArray: %#",resultArray);
[self.tableView reloadData];
}
- (void)didReceiveMemoryWarning
{
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}
#pragma mark - Table view data source
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView
{
//#warning Potentially incomplete method implementation.
// Return the number of sections.
return 1;
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
//#warning Incomplete method implementation.
// Return the number of rows in the section.
return [resultArray count];
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *CellIdentifier = #"Cell";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil) {
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleSubtitle reuseIdentifier:CellIdentifier];
}
// Configure the cell...
cell.textLabel.text = [[resultArray objectAtIndex:indexPath.row] valueForKey:#"name"];
cell.detailTextLabel.text = [[resultArray objectAtIndex:indexPath.row] valueForKey:#"designation"];
NSData *imageData = [NSData dataWithContentsOfURL:[NSURL URLWithString:[[resultArray objectAtIndex:indexPath.row] valueForKey:#"image"]]];
cell.imageview.image = [UIImage imageWithData:imageData];
return cell;
}
/*
// Override to support conditional editing of the table view.
- (BOOL)tableView:(UITableView *)tableView canEditRowAtIndexPath:(NSIndexPath *)indexPath
{
// Return NO if you do not want the specified item to be editable.
return YES;
}
*/
/*
// Override to support editing the table view.
- (void)tableView:(UITableView *)tableView commitEditingStyle:(UITableViewCellEditingStyle)editingStyle forRowAtIndexPath:(NSIndexPath *)indexPath
{
if (editingStyle == UITableViewCellEditingStyleDelete) {
// Delete the row from the data source
[tableView deleteRowsAtIndexPaths:#[indexPath] withRowAnimation:UITableViewRowAnimationFade];
}
else if (editingStyle == UITableViewCellEditingStyleInsert) {
// Create a new instance of the appropriate class, insert it into the array, and add a new row to the table view
}
}
*/
/*
// Override to support rearranging the table view.
- (void)tableView:(UITableView *)tableView moveRowAtIndexPath:(NSIndexPath *)fromIndexPath toIndexPath:(NSIndexPath *)toIndexPath
{
}
*/
/*
// Override to support conditional rearranging of the table view.
- (BOOL)tableView:(UITableView *)tableView canMoveRowAtIndexPath:(NSIndexPath *)indexPath
{
// Return NO if you do not want the item to be re-orderable.
return YES;
}
*/
#pragma mark - Table view delegate
// In a xib-based application, navigation from a table can be handled in -tableView:didSelectRowAtIndexPath:
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
// Navigation logic may go here, for example:
//Create the next view controller.
detailViewController *detailViewController1 = [[detailViewController alloc]initWithNibName:#"detailViewController" bundle:nil];
//detailViewController *detailViewController = [[detailViewController alloc] initWithNibName:#"detailViewController" bundle:nil];
// Pass the selected object to the new view controller.
// Push the view controller.
detailViewController1.nextDict = [[NSDictionary alloc]initWithDictionary:[resultArray objectAtIndex:indexPath.row]];
[self.navigationController pushViewController:detailViewController1 animated:YES];
// Pass the selected object to the new view controller.
// Push the view controller.
// [self.navigationController pushViewController:detailViewController animated:YES];
}
#end
- (void)viewDidLoad
{
[super viewDidLoad];
// Do any additional setup after loading the view from its nib.
empName.text=[nextDict valueForKey:#"name"];
deptlbl.text=[nextDict valueForKey:#"department"];
designationLbl.text=[nextDict valueForKey:#"designation"];
idLbl.text=[nextDict valueForKey:#"id"];
salaryLbl.text=[nextDict valueForKey:#"salary"];
NSString *ImageURL = [nextDict valueForKey:#"image"];
NSData *imageData = [NSData dataWithContentsOfURL:[NSURL URLWithString:ImageURL]];
image.image = [UIImage imageWithData:imageData];
}
The issue seems to be with autorelease of objects. NSJSONSerialization JSONObjectWithData is obviously creating some autoreleased objects and passing it back to you. If you try to take that on to a different thread, it will not work since it cannot be deallocated on a different thread.
Trick might be to try doing a mutable copy of that dictionary or array and use it.
NSError *e = nil;
id jsonObject = [NSJSONSerialization
JSONObjectWithData: data
options: NSJSONReadingMutableContainers
error: &e] mutableCopy];
Treating a NSDictionary as NSArray will not result in Bad access exception but instead will probably crash when a method call is made.
Also, may be the options do not really matter here but it is better to give NSJSONReadingMutableContainers | NSJSONReadingMutableContainers |
NSJSONReadingAllowFragments but even if they are autoreleased objects it may not solve this issue.
bad example, should be something like this
{"id":1, "name":"something as name"}
number and string are mixed.