I am very new to Objective-C so please bear with me.
I have been working on a UIPickerView that uses JSON data and can change the values of the second component based on which option is selected on the first component.
I have used two examples I found online to try and create this, one for using JSON with a UIPickerView and one for a UIPickerView that changes the value of the second component based on the selection in the first.
I have got both of these examples working separately but can't combine them to get them to work.
I have a feeling I might be going about this the wrong way so I came here to get some help and advice.
Heres my code:
#import "ViewController.h"
#interface ViewController ()
{
NSMutableArray *Nations;
NSMutableArray *England;
NSMutableArray *Espana;
NSMutableArray *Netherlands;
NSMutableArray *Germany;
NSMutableArray *Italy;
NSDictionary *Engdict;
NSDictionary *Espdict;
NSDictionary *Netdict;
NSDictionary *Gerdict;
NSDictionary *Itadict;
// Define keys
NSString *footballclub;
NSString *team;
}
#end
#implementation ViewController
- (void)viewDidLoad
{
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.
Nations = [[NSMutableArray alloc]initWithObjects:#"England",#"Espana",#"Netherlands",#"Germany",#"Italy", nil];
footballclub = #"FootballClub";
England = [[NSMutableArray alloc] init];
Espana = [[NSMutableArray alloc] init];
Netherlands = [[NSMutableArray alloc] init];
Germany = [[NSMutableArray alloc] init];
Italy = [[NSMutableArray alloc] init];
NSData *EngjsonData = [NSData dataWithContentsOfURL:
[NSURL URLWithString:#"http://url./England.php"]];
NSData *EspjsonData = [NSData dataWithContentsOfURL:
[NSURL URLWithString:#"http://url.com/Espana.php"]];
NSData *NetjsonData = [NSData dataWithContentsOfURL:
[NSURL URLWithString:#"http://url.com/Netherlands.php"]];
NSData *GerjsonData = [NSData dataWithContentsOfURL:
[NSURL URLWithString:#"http://url.com/Germany.php"]];
NSData *ItajsonData = [NSData dataWithContentsOfURL:
[NSURL URLWithString:#"http://url.com/Italy.php
id EngjsonObjects = [NSJSONSerialization JSONObjectWithData:EngjsonData options:NSJSONReadingMutableContainers error:nil];
id EspjsonObjects = [NSJSONSerialization JSONObjectWithData:EspjsonData options:NSJSONReadingMutableContainers error:nil];
id NetjsonObjects = [NSJSONSerialization JSONObjectWithData:NetjsonData options:NSJSONReadingMutableContainers error:nil];
id GerjsonObjects = [NSJSONSerialization JSONObjectWithData:GerjsonData options:NSJSONReadingMutableContainers error:nil];
id ItajsonObjects = [NSJSONSerialization JSONObjectWithData:ItajsonData options:NSJSONReadingMutableContainers error:nil];
for (NSDictionary *EngdataDict in EngjsonObjects) {
NSString *strFootballClub = [EngdataDict objectForKey:#"FootballClub"];
Engdict = [NSDictionary dictionaryWithObjectsAndKeys:
strFootballClub, footballclub,
nil];
[England addObject:Engdict];
}
for (NSDictionary *EspdataDict in EspjsonObjects) {
NSString *strFootballClub = [EspdataDict objectForKey:#"FootballClub"];
Espdict = [NSDictionary dictionaryWithObjectsAndKeys:
strFootballClub, footballclub,
nil];
[Espana addObject:Espdict];
}
for (NSDictionary *NetdataDict in NetjsonObjects) {
NSString *strFootballClub = [NetdataDict objectForKey:#"FootballClub"];
Netdict = [NSDictionary dictionaryWithObjectsAndKeys:
strFootballClub, footballclub,
nil];
[Netherlands addObject:Netdict];
}
for (NSDictionary *GerdataDict in GerjsonObjects) {
NSString *strFootballClub = [GerdataDict objectForKey:#"FootballClub"];
Gerdict = [NSDictionary dictionaryWithObjectsAndKeys:
strFootballClub, footballclub,
nil];
[Germany addObject:Gerdict];
}
for (NSDictionary *ItadataDict in ItajsonObjects) {
NSString *strFootballClub = [ItadataDict objectForKey:#"FootballClub"];
Itadict = [NSDictionary dictionaryWithObjectsAndKeys:
strFootballClub, footballclub,
nil];
[Italy addObject:Itadict];
}
}
- (NSInteger)numberOfComponentsInPickerView:(UIPickerView *)pickerView
{
return 2;
}
- (NSInteger)pickerView:(UIPickerView *)pickerView numberOfRowsInComponent: (NSInteger)component
{
if(component ==0)
{
return [Nations count];
}
else {
if ([team isEqualToString:#"England"]) {
return [England count];
}
if ([team isEqualToString:#"Espana"]) {
return [Espana count];
}
if ([team isEqualToString:#"Netherlands"]) {
return [Netherlands count];
}
if ([team isEqualToString:#"Germany"]) {
return [Germany count];
}
else {
return [Italy count];
}
}
return 0;
}
- (NSString *)pickerView:(UIPickerView *)pickerView titleForRow:(NSInteger)row forComponent:(NSInteger)component
{
NSDictionary *EngtmpDict = [England objectAtIndex:row];
NSDictionary *EsptmpDict = [Espana objectAtIndex:row];
NSDictionary *NettmpDict = [Netherlands objectAtIndex:row];
NSDictionary *GertmpDict = [Germany objectAtIndex:row];
NSDictionary *ItatmpDict = [Italy objectAtIndex:row];
if(component ==0)
{
return [Nations objectAtIndex:row];
}
else {
if ([team isEqualToString:#"England"]) {
return [EngtmpDict objectForKey:footballclub];
}
if ([team isEqualToString:#"Espana"]) {
return [EsptmpDict objectForKey:footballclub];
}
if ([team isEqualToString:#"Netherlands"]) {
return [NettmpDict objectForKey:footballclub];
}
if ([team isEqualToString:#"Germany"]) {
return [GertmpDict objectForKey:footballclub];
}
else {
return [ItatmpDict objectForKey:footballclub];
}
}
return 0;
}
-(void)pickerView:(UIPickerView *)pickerView didSelectRow:(NSInteger)row inComponent:(NSInteger)component
{
if (component == 0) {
team=[[NSString alloc] initWithFormat:#"%#" , [Nations objectAtIndex:row]];
[pickerView reloadComponent:1];
}
}
- (void)didReceiveMemoryWarning
{
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}
- (void)dealloc {
}
#end
This comes up in the debug screen when I try to run it.
Terminating app due to uncaught exception 'NSRangeException',
reason: '*** -[__NSArrayM objectAtIndex:]: index 1 beyond bounds [0 .. 0]'
Related
I want to use multiple sections feature UITableView. I have a single NSMutableArray which consists of data in dictionary format. In that dictionary there is a key which has values as '0' and '1'.
I want to create two separate NSMutableArray out of that so that I can assign them accordingly in different sections.
For example :
if (indexPath.section==0) {
NSDictionary *Data = [roomList1 objectAtIndex:indexPath.row];
} else {
NSDictionary *Data = [roomList2 objectAtIndex:indexPath.row];
}
Assuming the value in your dictionary is always set you could do something like:
NSMutableArray *firstArray = [NSMutableArray new];
NSMutableArray *secondArray = [NSMutableArray new];
for (NSDictionary *dictionary in yourArray) {
if ([dictionary objectForKey:#"yourValue"] isEqualToString: #"0") {
[firstArray addObject:dictionary];
} else if ([dictionary objectForKey:#"yourValue"]isEqualToString: #"1") {
[secondArray addObject:dictionary];
}
}
You can use this
- (NSDictionary *)groupObjectsInArray:(NSArray *)array byKey:(id <NSCopying> (^)(id item))keyForItemBlock
{
NSMutableDictionary *groupedItems = [NSMutableDictionary new];
for (id item in array) {
id <NSCopying> key = keyForItemBlock(item);
NSParameterAssert(key);
NSMutableArray *arrayForKey = groupedItems[key];
if (arrayForKey == nil) {
arrayForKey = [NSMutableArray new];
groupedItems[key] = arrayForKey;
}
[arrayForKey addObject:item];
}
return groupedItems;
}
Ref: Split NSArray into sub-arrays based on NSDictionary key values
Use like this
NSMutableArray * roomList1 = [[NSMutableArray alloc] init];
NSMutableArray * roomList2 = [[NSMutableArray alloc] init];
for(int i = 0;i< YourWholeArray.count;i++)
{
if([[[YourWholeArray objectAtIndex:i] valueForKey:#"YourKeyValueFor0or1"] isEqualToString "0"])
{
[roomList1 addObject:[YourWholeArray objectAtIndex:i]];
}
else if([[YourWholeArray objectAtIndex:i] valueForKey:#"YourKeyValueFor0or1"] isEqualToString "1"])
{
[roomList2 addObject:[YourWholeArray objectAtIndex:i]];
}
}
NSMutableArray *roomList1 = [[NSMutableArray alloc] init];
NSMutableArray *roomList2 = [[NSMutableArray alloc] init];
for (NSString *key in [myDictionary allKeys]) {
if (myDictionary[key] isEqualToString: #"0") {
[roomList1 addObject: myDictionary[key]];
} else {
[roomList2 addObject: myDictionary[key]];
}
}
try this way :-
NSMutableArray *getdata=[[NSMutableArray alloc]init];
getdata=[results objectForKey:#"0"];
NSMutableArray *getdata2=[[NSMutableArray alloc]init];
getdata2=[results objectForKey:#"1"];
use this two array and populate the section with now of rows at indexpathrow
define number of section 2
if (section==0){
getdata
}
else{
getdata2
}
i'm trying to fill a tableview with a Json response this is the Json that i'm reading from the callback
{
"Categories": {
"Beer": [
{
"id": "3",
"nombre": "Bud ligth",
"precio": "10",
"categoriaid": "3",
"url": "false"
}
],
"Whiskey": [
{
"id": "2",
"nombre": "Red label",
"precio": "100",
"categoriaid": "2",
"url": "false"
}
]
}
}
and this is my code but it breaks the app any ideas on how can i make change my code in order to make it fill the tableview with its correspondent section and rows in each sections
NSDictionary *json = [NSJSONSerialization JSONObjectWithData:self.menuInfo options:NSJSONReadingMutableContainers error:nil];
NSDictionary *cat = [json objectForKey:#"Categories"];
for(NSString *categories in cat){
Categorias *categorias = [[Categorias alloc]initWithNombre:categories];
NSDictionary *listTagDict = [cat objectForKey:categories];
for (NSString *prod in listTagDict) {
NSArray *rows = [listTagDict objectForKey:prod];
for (NSDictionary *rowDict in rows) {
NSString* pID = [rowDict objectForKey:#"id"];
NSString* pNombre = [rowDict objectForKey:#"nombre"];
NSString* pPrecio = [rowDict objectForKey:#"precio"];
NSString* pUrl = [rowDict objectForKey:#"url"];
Productos* productos = [[Productos alloc]initWithNombre:pNombre productoID:pID precio:pPrecio iconUrl:pUrl];
[categorias addRow:productos];
}
}
}
here are my two object clases the .m part
#implementation Productos
-(id)initWithNombre:(NSString *)name productoID:(NSString *)pId precio:(NSString*)prec iconUrl:(NSString*)url{
self = [super init];
if (self) {
self.nombre = name;
self.productoID = pId;
self.precio = prec;
self.iconUrl = url;
}
return self;
}
#end
#interface Categorias(){
NSMutableArray *rows;
}
#end
#implementation Categorias
-(id)initWithNombre:(NSString *)name{
self = [super init];
if (self) {
self.nombre = name;
}
return self;
}
-(void)addRow:(Productos *)row {
[rows addObject: row];
}
-(NSArray *)rowData {
return [NSArray arrayWithArray: rows];
}
#end
you are parsing the json response in wrong way,
try this,
NSDictionary *json = [NSJSONSerialization JSONObjectWithData:self.menuInfo options:NSJSONReadingMutableContainers error:nil];
NSDictionary *cat = [json objectForKey:#"Categories"];
NSMutableArray *categoryArray = [NSMutableArray new];
for(NSString *key in [cat allKeys]){
Categorias *category = [[Categorias alloc]initWithNombre:key];
NSArray *listTagDict = [cat objectForKey:key];
for (NSDictionary *prod in listTagDict) {
NSString* pID = [prod objectForKey:#"id"];
NSString* pNombre = [prod objectForKey:#"nombre"];
NSString* pPrecio = [prod objectForKey:#"precio"];
NSString* pUrl = [prod objectForKey:#"url"];
Productos* productos = [[Productos alloc]initWithNombre:pNombre productoID:pID precio:pPrecio iconUrl:pUrl];
[category addRow:productos];
}
[categoryArray addObject:category];
}
use categoryArray to populate tableview.
in this, categoryArray count will be section count, and each section contains rows with rowData array of each category.
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView {
return [categoryArray count];
}
-(NSString *) tableView:(UITableView *)tableView titleForHeaderInSection:(NSInteger)section {
Category *category = [categoryArray objectAtIndex:section];
return category.nombre;
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
Category *category = [categoryArray objectAtIndex:section];
NSArray *rows = [category rowData];
return [rows count];
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:#"cell" forIndexPath:indexPath]; return cell;
Category *category = [categoryArray objectAtIndex:indexPath.section];
NSArray *rows = [category rowData];
Product *product = [rows objectAtIndex:indexPath.row];
//populate cell with product
}
I am trying to figure out how I can get data from a JSON array that is in another array.
Here is the JSON. I'm wanting to get one of the image URLs from photos.
[
{
"id":6901439,
"name":"INDTTIN CD",
"description":"Full-length released June 2013 via Little Heart Records. \r\n\r\nTrack Listing:\r\n\r\n1. Tired\r\n2. Time to Heal\r\n3. Gypsy Summer\r\n4. Sketchbooks\r\n5. I Never Deserve the Things I Need\r\n6. Say it With the \"I\"\r\n7. A Negative Mind\r\n8. Rafters\r\n9. Indrid Cold\r\n10. Present Tense ",
"short_url":"http://onmyhonor.storenvy.com/products/6901439-indttin-cd",
"status":"active",
"labels":null,
"preorder":false,
"on_sale":true,
"store_id":373949,
"price":"7.00",
"marketplace_category":"music-cds",
"marketplace_category_id":345,
"photos":[
{
"photo":{
"original":"//d111vui60acwyt.cloudfront.net/product_photos/15878486/inddthin_20vinyl_20image_201_original.jpg",
"large":"//d111vui60acwyt.cloudfront.net/product_photos/15878486/inddthin_20vinyl_20image_201_large.jpg",
"homepage":"//d111vui60acwyt.cloudfront.net/product_photos/15878486/inddthin_20vinyl_20image_201_homepage.jpg",
"medium":"//d111vui60acwyt.cloudfront.net/product_photos/15878486/inddthin_20vinyl_20image_201_medium.jpg",
"small":"//d111vui60acwyt.cloudfront.net/product_photos/15878486/inddthin_20vinyl_20image_201_small.jpg",
"64w":"//d111vui60acwyt.cloudfront.net/product_photos/15878486/inddthin_20vinyl_20image_201_64w.jpg",
"200w":"//d111vui60acwyt.cloudfront.net/product_photos/15878486/inddthin_20vinyl_20image_201_200w.jpg",
"400w":"//d111vui60acwyt.cloudfront.net/product_photos/15878486/inddthin_20vinyl_20image_201_400w.jpg",
"600w":"//d111vui60acwyt.cloudfront.net/product_photos/15878486/inddthin_20vinyl_20image_201_600w.jpg",
"1000w":"//d111vui60acwyt.cloudfront.net/product_photos/15878486/inddthin_20vinyl_20image_201_1000w.jpg",
"64sq":"//d111vui60acwyt.cloudfront.net/product_photos/15878486/inddthin_20vinyl_20image_201_64sq.jpg",
"200sq":"//d111vui60acwyt.cloudfront.net/product_photos/15878486/inddthin_20vinyl_20image_201_200sq.jpg",
"400sq":"//d111vui60acwyt.cloudfront.net/product_photos/15878486/inddthin_20vinyl_20image_201_400sq.jpg"
}
}
],
"variants":[
{
"variant":{
"id":14382188,
"name":"INDTTIN CD",
"position":1,
"sku":"",
"full_quantity":300,
"in_stock":300,
"percent_available":100,
"is_default_variant?":false,
"price":7.0,
"sold_out":false,
"status":"active"
}
}
],
"collections":[
],
"store":{
"id":373949,
"name":"On My Honor",
"marketplace_url":"http://www.storenvy.com/stores/373949-on-my-honor"
}
}
]
Here is my code:
#define kBgQueue dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0)
#define storeURL [NSURL URLWithString: #"http://onmyhonor.storenvy.com/products.json"]
#import "GRSStoreViewController.h"
#import "GRSStoreDetailViewController.h"
#interface GRSStoreViewController ()
#end
#implementation GRSStoreViewController
#synthesize name, description, short_url, price, productImage, nameArray, descriptionArray, urlArray, priceArray, imageArray, url;
- (id)initWithStyle:(UITableViewStyle)style
{
self = [super initWithStyle:style];
if (self) {
}
return self;
}
- (void)viewDidLoad
{
[super viewDidLoad];
self.title = #"Store";
self.tableView = [[UITableView alloc]initWithFrame:CGRectZero style:UITableViewStyleGrouped];
url = [NSURL URLWithString:#"http://onmyhonor.storenvy.com/products.json"];
dispatch_async(kBgQueue, ^{
NSData *data = [NSData dataWithContentsOfURL:url];
[self performSelectorOnMainThread:#selector(fetchedData:) withObject:data waitUntilDone:YES];
});
}
- (void)fetchedData:(NSData *)responseData
{
NSError *error;
nameArray = [[NSMutableArray alloc]init];
descriptionArray = [[NSMutableArray alloc]init];
urlArray = [[NSMutableArray alloc]init];
priceArray = [[NSMutableArray alloc]init];
imageArray = [[NSMutableArray alloc]init];
NSArray *json = [NSJSONSerialization JSONObjectWithData:responseData options:kNilOptions error:&error];
for (NSDictionary *item in json)
{
name = [item objectForKey:#"name"];
description = [item objectForKey:#"description"];
short_url = [item objectForKey:#"short_url"];
price = [item objectForKey:#"price"];
[nameArray addObject:name];
[descriptionArray addObject:description];
[urlArray addObject:short_url];
[priceArray addObject:price];
}
[self.tableView reloadData];
}
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView
{
return 1;
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
return [nameArray count];
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:#"Cell"];
if (cell == nil)
{
cell = [[UITableViewCell alloc]initWithStyle:UITableViewCellStyleSubtitle reuseIdentifier:#"Cell"];
cell.textLabel.font = [UIFont systemFontOfSize:16.0];
}
if (cell)
{
cell.textLabel.text = [nameArray objectAtIndex:indexPath.row];
cell.textLabel.textColor = [UIColor darkGrayColor];
}
return cell;
}
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
GRSStoreDetailViewController *itemDetail = [[GRSStoreDetailViewController alloc]initWithNibName:#"GRSStoreDetailViewController" bundle:nil];
itemDetail.priceString = [priceArray objectAtIndex:indexPath.row];
itemDetail.descriptionString = [descriptionArray objectAtIndex:indexPath.row];
itemDetail.itemURL = [urlArray objectAtIndex:indexPath.row];
[self.navigationController pushViewController:itemDetail animated:YES];
[tableView deselectRowAtIndexPath:indexPath animated:YES];
}
- (void)didReceiveMemoryWarning
{
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}
#end
Change loop to
for (NSDictionary *item in json)
{
NSArray *photos = item[#"photos"];
NSDictionary *dict = [photos[0] valueForKeyPath:"photo"];
NSLog(#"original = %#", dict[#"original"]);
name = [item objectForKey:#"name"];
description = [item objectForKey:#"description"];
short_url = [item objectForKey:#"short_url"];
price = [item objectForKey:#"price"];
[nameArray addObject:name];
[descriptionArray addObject:description];
[urlArray addObject:short_url];
[priceArray addObject:price];
}
Change this
NSArray *json = [NSJSONSerialization JSONObjectWithData:responseData options:kNilOptions error:&error];
for (NSDictionary *item in json)
{
name = [item objectForKey:#"name"];
description = [item objectForKey:#"description"];
short_url = [item objectForKey:#"short_url"];
price = [item objectForKey:#"price"];
[nameArray addObject:name];
[descriptionArray addObject:description];
[urlArray addObject:short_url];
[priceArray addObject:price];
}
[self.tableView reloadData];
To this
NSdictionary *json = [NSJSONSerialization JSONObjectWithData:responseData options:kNilOptions error:&error];
name = [json objectForKey:#"name"];
description = [json objectForKey:#"description"];
short_url = [json objectForKey:#"short_url"];
price = [json objectForKey:#"price"];
// this is your photos array
NSArray *photos = [josn objectForKey:#"photos"];
// every object in this array is a dictionary. In your case this array has only one dictionary so
NSDictionary *photosDict = [photos firstObject];
// from here you can access all keys of photosDict
All available keys in your photosDict:
original
large
homepage
medium
small
64w
200w
400w
600w
1000w
64sq
200sq
400sq
i am getting data from service which i successfully parse like this
-(NSMutableArray *)clubTypes
{
NSMutableArray *dataArray = [[NSMutableArray alloc] init];
NSString *url = [NSString stringWithFormat:#"%#category",KWSURLList];
NSLog(#"%#",url);
NSDictionary * returnDict = (NSDictionary *) [self callWebService:url];
if([returnDict objectForKey:#"result"])
{
// NSDictionary * returnDictResult = (NSDictionary *) [returnDict objectForKey:#"result"];
NSArray *returnedArray = [returnDict objectForKey:#"data"];
for(NSDictionary *clubDict in returnedArray)
{
ClubTypeDC *clubDC = [[ClubTypeDC alloc] init];
clubDC.clubTypeID = [[clubDict objectForKey:#"Id"] intValue];
clubDC.clubTypeTitle = [clubDict objectForKey:#"tittle"];
clubDC.clubTypeImage = [clubDict objectForKey:#"icons"];
[dataArray addObject:clubDC];
// NSLog(#"%#",proEvents.proEventImage);
}
}
}
but i always get an error bad access in numberOfRowsInSection. can any one tell me why?
Remember always return your array like this
return dataArray;
i edited your code, please check it
-(NSMutableArray *)clubTypes
{
NSMutableArray *dataArray = [[NSMutableArray alloc] init];
NSString *url = [NSString stringWithFormat:#"%#category",KWSURLList];
NSLog(#"%#",url);
NSDictionary * returnDict = (NSDictionary *) [self callWebService:url];
if([returnDict objectForKey:#"result"])
{
// NSDictionary * returnDictResult = (NSDictionary *) [returnDict objectForKey:#"result"];
NSArray *returnedArray = [returnDict objectForKey:#"data"];
for(NSDictionary *clubDict in returnedArray)
{
ClubTypeDC *clubDC = [[ClubTypeDC alloc] init];
clubDC.clubTypeID = [[clubDict objectForKey:#"Id"] intValue];
clubDC.clubTypeTitle = [clubDict objectForKey:#"tittle"];
clubDC.clubTypeImage = [clubDict objectForKey:#"icons"];
[dataArray addObject:clubDC];
// NSLog(#"%#",proEvents.proEventImage);
}
}
return dataArray;
}
return dataArray
in your function at last
and
use this in numberOfRowsInSection...
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
if ([dataArray count] > 0) {
return dataArray.count;
}
else {
return 0;
}
}
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.