NSNull length error when parsing JSON to Table View - ios

I'm trying to parse a JSON file to my iOS app table view.
When I launch the app I see that it parses the data, but when I begin to scroll the app instantly crashes and gives me this error: Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: '-[NSNull length]: unrecognized selector sent to instance 0x38094a60'
My code:
#import "FirstViewController.h"
#import "YoutubePost.h"
#interface FirstViewController ()
{
NSInteger refreshIndex;
NSArray *title;
NSArray *about;
NSArray *views;
NSArray *rating;
NSArray *votes;
NSArray *content;
}
#end
#implementation FirstViewController
#synthesize tweets;
- (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil
{
self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil];
if (self) {
self.title = NSLocalizedString(#"Videos", #"Videos");
self.tabBarItem.image = [UIImage imageNamed:#"newtab1"];
}
return self;
}
- (void)viewDidLoad
{
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.
self.myTableView.separatorColor = [UIColor clearColor];
[self issueLoadRequest];
[self setNeedsStatusBarAppearanceUpdate];
}
-(UIStatusBarStyle)preferredStatusBarStyle{
return UIStatusBarStyleLightContent;
}
#pragma mark - Table view data source
- (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:#"my-site.php/file.json"]];
[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;
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *simpleTableIdentifier = #"YoutubePost";
YoutubePost *cell = (YoutubePost *)[tableView dequeueReusableCellWithIdentifier:simpleTableIdentifier];
if (cell == nil)
{
NSArray *nib = [[NSBundle mainBundle] loadNibNamed:#"YoutubePost" 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 *temp = [self.tweets objectAtIndex:indexPath.row];
NSDictionary *tweet = [self nullFreeDictionaryWithDictionary:temp];
cell.title.text = [tweet objectForKey:#"title"];
cell.views.text = [tweet objectForKey:#"views"];
return cell;
}
- (NSDictionary *)nullFreeDictionaryWithDictionary:(NSDictionary *)dictionary
{
NSMutableDictionary *replaced = [NSMutableDictionary dictionaryWithDictionary:dictionary];
// Iterate through each key-object pair.
[dictionary enumerateKeysAndObjectsUsingBlock:^(id key, id object, BOOL *stop) {
// If object is a dictionary, recursively remove NSNull from dictionary.
if ([object isKindOfClass:[NSDictionary class]]) {
NSDictionary *innerDict = object;
replaced[key] = [NSDictionary nullFreeDictionaryWithDictionary:innerDict];
}
// If object is an array, enumerate through array.
else if ([object isKindOfClass:[NSArray class]]) {
NSMutableArray *nullFreeRecords = [NSMutableArray array];
for (id record in object) {
// If object is a dictionary, recursively remove NSNull from dictionary.
if ([record isKindOfClass:[NSDictionary class]]) {
NSDictionary *nullFreeRecord = [NSDictionary nullFreeDictionaryWithDictionary:record];
[nullFreeRecords addObject:nullFreeRecord];
}
else {
if (object == [NSNull null]) {
[nullFreeRecords addObject:#""];
}
else {
[nullFreeRecords addObject:record];
}
}
}
replaced[key] = nullFreeRecords;
}
else {
// Replace [NSNull null] with nil string "" to avoid having to perform null comparisons while parsing.
if (object == [NSNull null]) {
replaced[key] = #"";
}
}
}];
return [NSDictionary dictionaryWithDictionary:replaced];
}
- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath
{
return 397;
}
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
int storyIndex = [indexPath indexAtPosition: [indexPath length] - 1];
NSString * storyLink = [[tweets objectAtIndex: storyIndex] objectForKey:#"link"];
[[UIApplication sharedApplication] openURL:[NSURL URLWithString:storyLink]];
// Spit out some pretty JSON for the tweet that was tapped. Neato.
NSString *formattedJSON = [[NSString alloc] initWithData:[NSJSONSerialization dataWithJSONObject:[self.tweets objectAtIndex:indexPath.row] options:NSJSONWritingPrettyPrinted error:nil] encoding:NSUTF8StringEncoding];
NSLog(#"tweet:\n%#", formattedJSON);
}
#end
Before I installed the new Xcode 5 I didn't get this error. Can someone help me?
Thanks.

What I've been doing myself to avoid this when parsing JSON results is replacing each instance of NSNull with a null string (#""), using the following method:
+ (NSDictionary *)nullFreeDictionaryWithDictionary:(NSDictionary *)dictionary
{
NSMutableDictionary *replaced = [NSMutableDictionary dictionaryWithDictionary:dictionary];
// Iterate through each key-object pair.
[dictionary enumerateKeysAndObjectsUsingBlock:^(id key, id object, BOOL *stop) {
// If object is a dictionary, recursively remove NSNull from dictionary.
if ([object isKindOfClass:[NSDictionary class]]) {
NSDictionary *innerDict = object;
replaced[key] = [NSDictionary nullFreeDictionaryWithDictionary:innerDict];
}
// If object is an array, enumerate through array.
else if ([object isKindOfClass:[NSArray class]]) {
NSMutableArray *nullFreeRecords = [NSMutableArray array];
for (id record in object) {
// If object is a dictionary, recursively remove NSNull from dictionary.
if ([record isKindOfClass:[NSDictionary class]]) {
NSDictionary *nullFreeRecord = [NSDictionary nullFreeDictionaryWithDictionary:record];
[nullFreeRecords addObject:nullFreeRecord];
}
else {
if (object == [NSNull null]) {
[nullFreeRecords addObject:#""];
}
else {
[nullFreeRecords addObject:record];
}
}
}
replaced[key] = nullFreeRecords;
}
else {
// Replace [NSNull null] with nil string "" to avoid having to perform null comparisons while parsing.
if (object == [NSNull null]) {
replaced[key] = #"";
}
}
}];
return [NSDictionary dictionaryWithDictionary:replaced];
}
Of course, this relies on the JSON return format being a dictionary, but you could easily modify it to accommodate other data types if you replaced all the parameter types with id and performed class checks.
--Edit--
If this is the only place you'll be using JSON, then change
NSDictionary *tweet = [self.tweets objectAtIndex:indexPath.row];
to
NSDictionary *temp = [self.tweets objectAtIndex:indexPath.row];
NSDictionary *tweet = [NSDictionary nullFreeDictionaryWithDictionary:temp];
Note that I have nullFreeDictionaryWithDictionary as an Objective-C category for the NSDictionary class. You could probably just add that to your view controller's implementation file if you weren't going to use this method anywhere else.

Related

Outputting read in NSMutable Array

Problem: I have read in values from Windows Azure. Through NSLogs I am able to see that my application does indeed read in from the table on the Azure Server. However Displaying the values has become a problem.
Situation: So far I have an NSMutableArray object in the ViewController.m file. I have accessed the array and been able to assign the values from the results of the read from the table (in windows azure) to the mutableArray. My problem is that I am trying to display it through a tableview however nothing displays, and when I move down the table view, the application crashes.
I believe the main problem is this line:
cell.textLabel.text = [clubs objectAtIndex:indexPath.row];
Here is the ViewController.m code:
#import "ViewController.h"
#interface ViewController ()
#end
#implementation ViewController{
NSDictionary *courseDetails;
NSArray *justCourseNames;
NSDictionary *webcourseDetails;
NSArray *webjustCourseNames;
NSDictionary *clubNames;
NSArray *location;
NSMutableArray *clubs;
NSInteger amount;
}
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView{
return 2;
}
-(NSString *)tableView:(UITableView *)tableView titleForHeaderInSection:(NSInteger)section{
if (section == 0)
{
return #"Milton Keynes";
}
else{
return #"Stafford";
}
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section{
if (section == 0)
{
return clubs.count;
}
else{
return webcourseDetails.count;
}
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath{
UITableViewCell *cell = [[UITableViewCell alloc]initWithStyle:UITableViewCellStyleDefault reuseIdentifier:#"cell"];
UIImage *image = [UIImage imageNamed:#"ClubCellImage"];
[cell.imageView setImage:image];
//removing the line of code below seems to fix the crash. this is the line of code to display the details
cell.textLabel.text = [clubs objectAtIndex:indexPath.row];
/*if (indexPath.section == 0)
{
//cell.textLabel.text = justCourseNames[indexPath.row];
//cell.detailTextLabel.text = courseDetails[justCourseNames[indexPath.row]];
}
else
{
cell.textLabel.text = clubs[indexPath.row];
//cell.textLabel.text = webjustCourseNames[indexPath.row];
//cell.detailTextLabel.text = webcourseDetails[webjustCourseNames[indexPath.row]];
}*/
return cell;
}
- (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil
{
self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil];
if (self) {
// Custom initialization
}
return self;
}
- (void)viewDidLoad
{
[super viewDidLoad];
self.client = [MSClient clientWithApplicationURLString:#"https://clublocatortimogunmakin.azure-mobile.net/"
applicationKey:#"ecxnaXEfNpeOvwYYgcViJJoumZlZng45"];
// Do any additional setup after loading the view.
NSURL *url = [[NSBundle mainBundle] URLForResource:#"courses" withExtension:#"plist"];
MSTable *itemTable = [_client tableWithName:#"Item"];
courseDetails = [NSDictionary dictionaryWithContentsOfURL:url];
justCourseNames = courseDetails.allKeys;
NSURL *weburl = [[NSBundle mainBundle] URLForResource:#"courses_web" withExtension:#"plist"];
webcourseDetails = [NSDictionary dictionaryWithContentsOfURL:weburl];
webjustCourseNames = courseDetails.allKeys;
[itemTable readWithCompletion:^(NSArray *results, NSInteger totalCount, NSError *error) {
clubs = [results mutableCopy];
amount = totalCount;
if (error) {
NSLog(#"Error: %#", error);
} else {
//NSLog(#"Item read, id: %#", [results objectAtIndex:1]);
for (int i = 0; i < results.count; i++)
{
NSLog(#"Item read, id: %#", [results objectAtIndex:i]);
}
}
}];
}
- (void)didReceiveMemoryWarning
{
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}
#end
You are contradicting yourself when you implement the required tableview datasource methods (i.e. the numberOfRowsInSection and the cellForRowAtIndexPath methods)
You provide the count of cells here:
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section{
if (section == 0)
{
return clubs.count;
}
else{
return webcourseDetails.count;
}
}
So, your cellForRowAtIndexPath method should look something like this:
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath{
// cell init/dequeuing
if (indexPath.section == 0) {
cell.textLabel.text = clubs[indexPath.row]; //Assuming that is an NSString instance
} else {
cell.textLabel.text = webcourseDetails[indexPath.row]; //Assuming that is an NSString instance
}
return cell;
}
Please, try to do the following (I think you're not re-loading table after all items are read):
[itemTable readWithCompletion:^(NSArray *results, NSInteger totalCount, NSError *error) {
clubs = [results mutableCopy];
amount = totalCount;
if (error) {
NSLog(#"Error: %#", error);
} else {
//NSLog(#"Item read, id: %#", [results objectAtIndex:1]);
for (int i = 0; i < results.count; i++)
{
NSLog(#"Item read, id: %#", [results objectAtIndex:i]);
}
[YOURTABLENAMEHERE reloadData];
}
}];
Replace YOURTABLENAMEHERE with a reference to your table.

Multi level categories with items on all levels in UITableView

I have to create a UITableView using the JSON response below ( Array ). I have no code for this yet but would love some direction to how i would split this array to accommodate categories and items on all levels.
{
"result":{
"products":[
{
"id":"4",
"product_code":"PR04",
"title":"Product1",
"franchisee_id":"118"
}
],
"categories":[
{
"id":"8",
"name":"Category1"
},
{
"id":"20",
"name":"Category2",
"products":[
{
"id":"9",
"product_code":"PR07",
"title":Product2,
"franchisee_id":"118"
}
]
}
]
}
}
I want to achieve the following result:
items
Category1 > items
Category2 > items
When a category is clicked it would slide to the products in that category. Would really love some direction on this. Some products will not be in categories. Like the example above.
Well....
You need to parse the JSON file. You can easily google for some tutorials but here is a decent one.
Next you are going to need to setup a UITableView to load the items. another good tutorial on UITableViews
Then you are going to need to learn how to pass data between UIViewControllers. Tutorial.
So your steps in the code will be to:
Parse the JSON to separate all the elements.
Setup a UITableView to display the top level elements.
Create a second UITableViewController to push to after a top level item has been selected.
Setup a custom initializer for the second UITableViewController so you can pass it relevant data from the first view controller where you parsed the JSON.
I'm assuming you were looking for a bunch of code on how to do this, but that's no fun :)
Let me know if you run into any troubles and I will be glad to help.
EDIT:
I know I said I wasn't going to dump code but I have some extra time.
Create an NSObject subclass called ProductObject and make the .h look like this:
#import <Foundation/Foundation.h>
#interface ProductObject : NSObject
#property NSString *productCode, *productTitle, *franchiseId, *productId;
#end
Don't do any thing to the .m
Create another NSObject subclass called CategoryObject and make the .h look like this:
#import <Foundation/Foundation.h>
#interface CategoryObject : NSObject
#property NSString *categoryName, *categoryId;
#property NSArray *products;
#end
Again, don't need to do anything to the .m.
Now, in the class that you want to display the UITableView will the Products and Categories (this is all in the .m, the .h is empty):
#import "ViewController.h"
#import "CategoryObject.h"
#import "ProductObject.h"
#interface ViewController ()
//Hooked in from IB
#property (weak, nonatomic) IBOutlet UITableView *table;
//Our UITableView data source
#property NSMutableDictionary *tableObjects;
#end
#implementation ViewController
/**
Parses a the local JSON file
*/
- (void)parseJSON {
NSString *filePath = [[NSBundle mainBundle] pathForResource:#"test" ofType:#"json"];
//création d'un string avec le contenu du JSON
NSString *myJSON = [[NSString alloc] initWithContentsOfFile:filePath encoding:NSUTF8StringEncoding error:NULL];
NSError *error;
NSDictionary *topLevleJSON = [NSJSONSerialization JSONObjectWithData:[myJSON dataUsingEncoding:NSUTF8StringEncoding] options:kNilOptions error:&error];
if (error) {
NSLog(#"Error serializing JSON: %#", error.localizedDescription);
return;
}
NSArray *products = topLevleJSON[#"products"];
NSArray *categories = topLevleJSON[#"categories"];
//Use a NSDictonary so that it contains an NSArray of ProductObjects for the "Products" key, and an array of CategoryObjects for the "Category" key.
self.tableObjects = [NSMutableDictionary new];
//Parse all the products
NSMutableArray *productsInJSON = [NSMutableArray new];
[products enumerateObjectsUsingBlock:^(NSDictionary *productObject, NSUInteger idx, BOOL *stop) {
ProductObject *product = [self createProductObjectFromDictionary:productObject];
[productsInJSON addObject:product];
}];
//Set the array of ProductObjects for the key #"Products"
[self.tableObjects setObject:productsInJSON forKey:#"Products"];
//Parse all the categories
NSMutableArray *categoriesInJSON = [NSMutableArray new];
[categories enumerateObjectsUsingBlock:^(NSDictionary *categoryObject, NSUInteger idx, BOOL *stop) {
CategoryObject *category = [self createCategoryObjectFromDictionary:categoryObject];
[categoriesInJSON addObject:category];
}];
//Set the array of CategoryObjects for key #"Categories"
[self.tableObjects setObject:categoriesInJSON forKey:#"Categories"];
[self.table reloadData];
}
/**
Creates a ProductObject from an NSDictonary.
#param dictionary The dictonary describing the Product parsed from JSON
#return A pretty formatted ProductObject
*/
- (ProductObject*)createProductObjectFromDictionary:(NSDictionary*)dictionary {
ProductObject *product = [ProductObject new];
product.productTitle = dictionary[#"title"];
product.productCode = dictionary[#"product_code"];
product.franchiseId = dictionary[#"franchisee_id"];
product.productId = dictionary[#"id"];
return product;
}
/**
Creates a Category from an NSDictionary
#param dictionary The dictonary describing the Category parsed from JSON
#return A pretty formatted CategoryObject
*/
- (CategoryObject*)createCategoryObjectFromDictionary:(NSDictionary*)dictionary {
CategoryObject *category = [CategoryObject new];
category.categoryId = dictionary[#"id"];
category.categoryName = dictionary[#"name"];
//Check to see if the "products" key exist for the category, if we don't check and just look for it, we will get a crash if it doesn't exist.
if ([[dictionary allKeys] containsObject:#"products"]) {
NSArray *categoryProducts = dictionary[#"products"];
//Parse all the Products for the Category.
NSMutableArray *categoryProductsFormatted = [NSMutableArray new];
[categoryProducts enumerateObjectsUsingBlock:^(NSDictionary *productObject, NSUInteger idx, BOOL *stop) {
ProductObject *product = [self createProductObjectFromDictionary:productObject];
[categoryProductsFormatted addObject:product];
}];
category.products = [NSArray arrayWithArray:categoryProductsFormatted];
}
else {
category.products = nil;
}
return category;
}
#pragma mark -
#pragma mark - UITableView delegate
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView {
return [[self.tableObjects allKeys] count];
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
//Get the key for this section
NSString *key = [[self.tableObjects allKeys] objectAtIndex:section];
//Return the number of objects for this key.
return [(NSArray*)[self.tableObjects objectForKey:key] count];
}
- (NSString *)tableView:(UITableView *)tableView titleForHeaderInSection:(NSInteger)section {
return [[self.tableObjects allKeys] objectAtIndex:section];
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:#"CellIdentifier"];
if (!cell) {
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:#"CellIdentifier"];
}
//Get all the NSArray associated with this section, which will be an array of ProductObjects or an array of CategoryObjects
NSString *key = [[self.tableObjects allKeys] objectAtIndex:indexPath.section];
NSArray *sectionobjects = (NSArray*)[self.tableObjects objectForKey:key];
id object = [sectionobjects objectAtIndex:indexPath.row];
//Set the cell text based on what kind of object is returned
if ([object isKindOfClass:[ProductObject class]]) {
cell.textLabel.text = [(ProductObject*)object productTitle];
}
else if ([object isKindOfClass:[CategoryObject class]]) {
cell.textLabel.text = [(CategoryObject*)object categoryName];
}
return cell;
}
#pragma mark -
#pragma mark - UITableView delegate
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
[tableView deselectRowAtIndexPath:indexPath animated:YES];
NSString *key = [[self.tableObjects allKeys] objectAtIndex:indexPath.section];
NSArray *sectionobjects = (NSArray*)[self.tableObjects objectForKey:key];
id object = [sectionobjects objectAtIndex:indexPath.row];
//They selected a product
if ([object isKindOfClass:[ProductObject class]]) {
ProductObject *product = (ProductObject*)object;
NSLog(#"%#", product.productTitle);
NSLog(#"%#", product.productCode);
NSLog(#"%#", product.productId);
}
//They selected a Category
else if ([object isKindOfClass:[CategoryObject class]]) {
//Check to see if the CategoryObject has any ProductObjects associated with it
if ([(CategoryObject*)object products]) {
//Now you will need to pass array of ProductObjects this along to your next view controller.
NSArray *cateogryProducts = [(CategoryObject*)object products];
//For demonstration purposes, i'll run through and print out all the Products for this Category
[cateogryProducts enumerateObjectsUsingBlock:^(ProductObject *product, NSUInteger idx, BOOL *stop) {
NSLog(#"%#", product.productTitle);
NSLog(#"%#", product.productCode);
NSLog(#"%#", product.productId);
}];
}
}
}
- (void)viewDidLoad
{
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.
//Start parsing the JSON
[self parseJSON];
}
- (void)didReceiveMemoryWarning
{
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}
#end
EDIT:
If you are wanting to open and close parts of the table like an accordion, take a look at Apple's same code: Table View Animations and Gestures.

Parsing JSON url pictures to iOS not working

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.

Populate UITableView Cells from JSON dates

I am having a hard time populating the tableview cells with json feed dates field. I think it has do to with the way I am getting the dates
NSMutableArray *datesArray = [[NSMutableArray alloc] init];
for (NSDictionary *tempDict in json){
[datesArray addObject:[tempDict objectForKey:#"date"]];
}
Please assist if you can. I have gone through everything I can think of (still learning).
.h file
#import <UIKit/UIKit.h>
#interface AvailabilityViewController : UIViewController <UITextFieldDelegate, UITableViewDelegate, UITableViewDataSource>
{
NSDate *appointmentdate;
UIActionSheet *dateSheet;
UITextField *mydatetextfield;
UILabel *pastDateLabel;
NSArray *json;
}
//-(IBAction)getDataFromJson:(id)sender;
#property (strong, nonatomic) IBOutlet UITextField *mydatetextfield;
#property (nonatomic, retain) NSDate *appointmentdate;
#property (strong, nonatomic) IBOutlet UILabel *pastDateLabel;
#property (strong, nonatomic) IBOutlet UITableView *_tableView;
#property (nonatomic, retain) NSArray *json;
//-(void)setDate;
-(void)dismissDateSet;
-(void)cancelDateSet;
#end
.m file
- (void)fetchedData:(NSData *)responseData {
//parse out the json data
NSError* error;
//NSLog(#"string is %#", responseData);
self.json = [NSJSONSerialization JSONObjectWithData:responseData options:kNilOptions error:&error];
NSLog(#"string is %#", responseData);
if ([json isKindOfClass:[NSArray class]]) {
NSLog(#"its an array");
} else if ([json isKindOfClass:[NSDictionary class]]) {
NSLog(#"its a dictionary");
} else if ([json isKindOfClass:[NSString class]]) {
NSLog(#"its a string");
} else if ([json isKindOfClass:[NSNumber class]]) {
NSLog(#"its a number");
} else if ([json isKindOfClass:[NSNull class]]) {
NSLog(#"its a null");
} else if (json == nil){
NSLog(#"nil");
}
//NSArray* latestLoans = [json objectForKey:#"date"]; //2
NSMutableArray *datesArray = [[NSMutableArray alloc] init];
for (NSDictionary *tempDict in json){
[datesArray addObject:[tempDict objectForKey:#"date"]];
}
NSLog(#"this is your datesArray %#", datesArray);
}
- (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.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];
}
cell.textLabel.text = [[self.json objectAtIndex:indexPath.row] objectForKey:#"date"];
return cell;
//[_tableView reloadData];
}
Here is my NSLog of datesArray
2012-08-21 10:09:39.303 GBSB[1409:15b03] this is your datesArray (
"2012-08-13 12:00:00",
"2012-08-13 10:00:00",
"2012-08-13 13:00:00"
Here is what the viewDidLoad looks like
- (void)viewDidLoad
{
[super viewDidLoad];
dispatch_async(kBgQueue, ^{
NSData* data = [NSData dataWithContentsOfURL:
kLatestKivaLoansURL];
[self performSelectorOnMainThread:#selector(fetchedData:)
withObject:data waitUntilDone:YES];
});
}
- (void)fetchedData:(NSData *)responseData {
//parse out the json data
NSError* error;
//NSLog(#"string is %#", responseData);
self.json = [NSJSONSerialization JSONObjectWithData:responseData options:kNilOptions error:&error];
NSLog(#"string is %#", responseData);
if ([json isKindOfClass:[NSArray class]]) {
NSLog(#"its an array");
} else if ([json isKindOfClass:[NSDictionary class]]) {
NSLog(#"its a dictionary");
} else if ([json isKindOfClass:[NSString class]]) {
NSLog(#"its a string");
} else if ([json isKindOfClass:[NSNumber class]]) {
NSLog(#"its a number");
} else if ([json isKindOfClass:[NSNull class]]) {
NSLog(#"its a null");
} else if (json == nil){
NSLog(#"nil");
}
//NSArray* latestLoans = [json objectForKey:#"date"]; //2
NSMutableArray *datesArray = [[NSMutableArray alloc] init];
for (NSDictionary *tempDict in json){
[datesArray addObject:[tempDict objectForKey:#"date"]];
}
NSLog(#"this is your datesArray %#", datesArray);
NSLog(#"this is the json %#", self.json);
// 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;
}
You are creating a local variable called json in your fetchedData method and putting the parsed response in there. However, because this is a local variable it ceases to exist once your exit from the method.
Instead, what you should do is to put the parsed response data into your viewController's json #property which you declare in the .h file. To do this, make this change to your fetchedData: method:
self.json = [NSJSONSerialization JSONObjectWithData:responseData options:kNilOptions error:&error];
Then in your cellForRowAtIndexPath: you can pull out the data like this:
cell.textLabel.text = [[self.json objectAtIndex:indexPath.row] objectForKey:#"date"]
Also your numberOfRowsInSection: should return:
return self.json.count;
EDIT:
More explanation about local variables vs. ivars vs. properties...
When you declare this in your view controller's #interface in the .h file:
NSArray *json;
you are creating in instance variable (ivar) for your class. Whenever an instance of your class is instantiated it will have a member variable named json that you can access within the methods of your class.
When you declare a property like this:
#property (nonatomic, retain) NSArray *json;
and a matching #synthesize in the implementation file:
#synthesize json;
the compiler auto generates a setter and getter method for you, so you can then use these methods:
NSArray *theArray = [self json]; // getter
[self setJson:newArray]; // setter
You can do the same in modern Objective-C using dot notation:
NSArray *theArray = self.json; // getter
self.json = newArray; // setter
Your property ends up being backed by an ivar, which is by default named the same of the property and will be autogenerated for you if it doesn't exist. (You can also specify the name of the backing ivar in the #synthesize statement, and you'll often see people using ivar names that start with an underscore to help keep straight what is the ivar name and what is the property name, but I won't go into that further here)
Your object's properties can be accessed from other classes, whereas your object's ivars cannot.
But back to your question. In addition to your ivar and property, you have created a local variable, also named json in your fetchedData: method. This variable, because you declare it within the body of the method, will only exist until the method finishes, at which time it will be deallocated and the data contained will be lost if not retained elsewhere. Because you have given your local variable the same name as your ivar, the local variable effective hides the ivar.
Apple does not recommend using ivars directly anyway, but instead doing all access through your class properties (getter and setter methods). That's why I suggested using self.json. It should also fix your problem, since values saved to your property will persist beyond the execution of the method.
Hope that helps some.
It looks like 'json' is an array of NSDictionary's.
You can't do this:
cell.textLabel.text = [json objectAtIndex:indexPath.row];
(i.e. assign an NSDictionary to a UILabel's text field)
but you could do something like:
cell.textLabel.text = [[json objectAtIndex:indexPath.row] objectForKey:#"date"];

Problem in loading a NSMutablearray value into table view?

I am new to iphone development, I am trying to load a NSMutableArray values into a table view, I am using code below for this which generates error as specified. Can some one help me in rectifying this error.
code :-
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
// Return the number of rows in the section.
return [ShowList count];
}
// Customize the appearance of table view cells.
- (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] autorelease];
}
// Configure the cell...
NSString *cellValue = [ShowList objectAtIndex:indexPath.row];
cell.textLabel.text = cellValue;
return cell;
}
error :-
2011-02-25 07:22:24.470 iPhone[1032:207] -[__NSArrayM isEqualToString:]: unrecognized selector sent to instance 0xab15d30
2011-02-25 07:22:24.471 iPhone[1032:207] *** Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: '-[__NSArrayM isEqualToString:]: unrecognized selector sent to instance 0xab15d30'
*** Call stack at first throw:
Print Description of My NSMutableArray :-
2011-02-25 07:21:54.806[1032:207] (
(
"viral_tweeter",
default1571546,
default1570056,
twilightsaga,
"wp-monetizer",
viraltweetbuild,
"building_a_list",
yourtwittertips,
"twitter_profit",
mikesbi,
mikesbizz,
default1164341,
incbizztest,
default1164319,
iprotv,
iwantafreecopy1,
tweeterbuilder,
trafficlists,
myadsensenews,
mysafelistnews,
myviralnews,
safelistology,
slmembers,
slpmembers,
twonderlandlist,
noseospider,
yseospider,
digitallockdown,
alistblueprint,
classifiedtips,
incbizzblog,
"xit-trafficbeta",
twwidget,
jvtrafficfunnel,
instantmlmspage,
listbuldingmax,
"incbizz_tips"
)
)
code for parsing HTTP Get response :-
- (void)requestDataFetcher:(GTMHTTPFetcher *)fetcher finishedWithData:(NSData *)data error:(NSError *)error {
// this is only for testing whether the data is coming or not
// NSDictionary *tempDict = [GTMOAuthAuthentication dictionaryWithResponseData:data];
if (error)
{
NSLog(#"Error: in getting data after authentication : %#",[error description]);
}
else
{
// NSLog(#"Succcess: in getting data after authentication \n data: %#",[tempDict description]);
NSString* aStr;
aStr = [[NSString alloc] initWithData:data encoding:NSASCIIStringEncoding];
NSDictionary *dictionary = [aStr JSONValue];
NSArray *keys = [dictionary allKeys];
Names = [[NSMutableArray alloc]init];
int i = 0;
// values in foreach loop
for (NSString *key in keys)
{
i++;
NSArray *items = (NSArray *) [dictionary objectForKey:key];
// NSLog(#" test %#", items);
if (i==3)
{
for (NSString *item in items)
{
NSString* aStrs= item;
// NSLog(#" test %#", aStrs);
NSDictionary *dict = aStrs;
NSArray *k = [dict allKeys];
for (id *it in k)
{
// NSLog(#"the child item: %#", [NSString stringWithFormat:#"Child Item -> %# value %#", (NSDictionary *) it,[dict objectForKey:it]]);
NSString *value = [it description];
if ( [value isEqualToString:#"name"])
{
NSString * value = (NSString*)[[dict objectForKey:it] description];
NSLog(value);
[Names addObject:value];
[[MySingletonClass sharedMySingleton] SetAweberList: value];
}
}
}
}
}
mShowList.hidden = FALSE;
}
}
#Ravi your array is array of array that is the ShowList is an array which has first object as array and that array is that
"viral_tweeter",
default1571546,
default1570056,
twilightsaga,
"wp-monetizer",
viraltweetbuild,
"building_a_list",
yourtwittertips,
"twitter_profit",
mikesbi,
mikesbizz,
default1164341,
incbizztest,
default1164319,
iprotv,
iwantafreecopy1,
tweeterbuilder,
trafficlists,
myadsensenews,
mysafelistnews,
myviralnews,
safelistology,
slmembers,
slpmembers,
twonderlandlist,
noseospider,
yseospider,
digitallockdown,
alistblueprint,
classifiedtips,
incbizzblog,
"xit-trafficbeta",
twwidget,
jvtrafficfunnel,
instantmlmspage,
listbuldingmax,
"incbizz_tips"
so you can do something like this.
NSString *cellValue = [[ShowList objectAtIndex:0] objectAtIndex:indexPath.row];
I assume UILabel's setText calls isEqualToString for some reason, so it crashes for the very first object in your array which is not a NSString.

Resources