JSON Error

If you want to show an error if the host is down it is pretty easy. This thinks you have read the other JSON posts. So look at the viewDidLoad function.

This is a continuation of Custom UITableViewCell

We are just adding a if {} else {}  and if the data is nil, show an error.

- (void)viewDidLoad {
	// Add the following line if you want the list to be editable
	// self.navigationItem.leftBarButtonItem = self.editButtonItem;

	// init the url
	NSURL *jsonURL = [NSURL URLWithString:@"http://iphone.zcentric.com/test-json2.php"];

	NSString *jsonData = [[NSString alloc] initWithContentsOfURL:jsonURL];

	if (jsonData == nil) {
		UIAlertView *alert = [[UIAlertView alloc] initWithTitle:@"Webservice Down" message:@"The webservice you are accessing is down. Please try again later."  delegate:self cancelButtonTitle:@"OK" otherButtonTitles: nil];
		[alert show];
		[alert release];
	}
	else {
		// converting the json data into an array
		self.jsonArray = [jsonData JSONValue];
	}

	// releasing the vars now
	[jsonURL release];
	[jsonData release];
}

You can test this by simply changing the hostname to like iphone2.zcentric.com and it will show an error like the following.

Custom UITableViewCell

Make sure you have JSON Framework installed first.

This is a continuation of More Complex JSON endpoint

The reason why OOP is so powerful is that you can subclass everything to make it work or look just how you want it. The default look of a UITableViewCell is just one line of text. If you want to add some text below it you need to subclass that. So that is what we will do right now.

The first thing we want to do is create a new subclass in your project. You should know how to do this already. File -> New File and select a UITableViewCell subclass. I have named mine ImageCell

Now you can see we have the title in nice big letters and the URL to the image under in in smaller letters and also in grey

I am just going to paste all the code and I have made some comments to show you what I added

First your ImageCell.h should look like

#import 

@interface ImageCell : UITableViewCell {
	// adding the 2 labels we want to show in the cell
	UILabel *titleLabel;
	UILabel *urlLabel;
}

// these are the functions we will create in the .m file

// gets the data from another class
-(void)setData:(NSDictionary *)dict;

// internal function to ease setting up label text
-(UILabel *)newLabelWithPrimaryColor:(UIColor *)primaryColor selectedColor:(UIColor *)selectedColor fontSize:(CGFloat)fontSize bold:(BOOL)bold;

// you should know what this is for by know
@property (nonatomic, retain) UILabel *titleLabel;
@property (nonatomic, retain) UILabel *urlLabel;

@end

There really isn’t any magic going on in the header. So now open up the ImageCell.m file and my contents are as follows.

#import "ImageCell.h"

@implementation ImageCell

// we need to synthesize the two labels
@synthesize titleLabel, urlLabel;

- (id)initWithFrame:(CGRect)frame reuseIdentifier:(NSString *)reuseIdentifier {
	if (self = [super initWithFrame:frame reuseIdentifier:reuseIdentifier]) {
		// Initialization code

		// we need a view to place our labels on.
		UIView *myContentView = self.contentView;

		/*
		 init the title label.
		 set the text alignment to align on the left
		 add the label to the subview
		 release the memory
		*/
		self.titleLabel = [self newLabelWithPrimaryColor:[UIColor blackColor] selectedColor:[UIColor whiteColor] fontSize:14.0 bold:YES];
		self.titleLabel.textAlignment = UITextAlignmentLeft; // default
		[myContentView addSubview:self.titleLabel];
		[self.titleLabel release];

		/*
		 init the url label. (you will see a difference in the font color and size here!
		 set the text alignment to align on the left
		 add the label to the subview
		 release the memory
		 */
        self.urlLabel = [self newLabelWithPrimaryColor:[UIColor blackColor] selectedColor:[UIColor lightGrayColor] fontSize:10.0 bold:NO];
		self.urlLabel.textAlignment = UITextAlignmentLeft; // default
		[myContentView addSubview:self.urlLabel];
		[self.urlLabel release];
	}

	return self;
}

- (void)setSelected:(BOOL)selected animated:(BOOL)animated {

	[super setSelected:selected animated:animated];

	// Configure the view for the selected state
}

/*
 this function gets in data from another area in the code
 you can see it takes a NSDictionary object
 it then will set the label text
*/
-(void)setData:(NSDictionary *)dict {
	self.titleLabel.text = [dict objectForKey:@"title"];
	self.urlLabel.text = [dict objectForKey:@"img"];
}

/*
 this function will layout the subviews for the cell
 if the cell is not in editing mode we want to position them
*/
- (void)layoutSubviews {

    [super layoutSubviews];

	// getting the cell size
    CGRect contentRect = self.contentView.bounds;

	// In this example we will never be editing, but this illustrates the appropriate pattern
    if (!self.editing) {

		// get the X pixel spot
        CGFloat boundsX = contentRect.origin.x;
		CGRect frame;

        /*
		 Place the title label.
		 place the label whatever the current X is plus 10 pixels from the left
		 place the label 4 pixels from the top
		 make the label 200 pixels wide
		 make the label 20 pixels high
		*/
		frame = CGRectMake(boundsX + 10, 4, 200, 20);
		self.titleLabel.frame = frame;

		// place the url label
		frame = CGRectMake(boundsX + 10, 28, 200, 14);
		self.urlLabel.frame = frame;
	}
}

/*
 this function was taken from an XML example
 provided by Apple

 I can take no credit in this
*/
- (UILabel *)newLabelWithPrimaryColor:(UIColor *)primaryColor selectedColor:(UIColor *)selectedColor fontSize:(CGFloat)fontSize bold:(BOOL)bold
{
	/*
	 Create and configure a label.
	 */

    UIFont *font;
    if (bold) {
        font = [UIFont boldSystemFontOfSize:fontSize];
    } else {
        font = [UIFont systemFontOfSize:fontSize];
    }

    /*
	 Views are drawn most efficiently when they are opaque and do not have a clear background, so set these defaults.  To show selection properly, however, the views need to be transparent (so that the selection color shows through).  This is handled in setSelected:animated:.
	 */
	UILabel *newLabel = [[UILabel alloc] initWithFrame:CGRectZero];
	newLabel.backgroundColor = [UIColor whiteColor];
	newLabel.opaque = YES;
	newLabel.textColor = primaryColor;
	newLabel.highlightedTextColor = selectedColor;
	newLabel.font = font;

	return newLabel;
}

- (void)dealloc {
	// make sure you free the memory
	[titleLabel dealloc];
	[urlLabel dealloc];
	[super dealloc];
}

@end

So now we can save that file and open up RootViewController.m

We need to add in a import

#import "ImageCell.h"

Now go down to the cellForRowAtIndexPath function. Right now we have a UITableViewCell *cell call to init the cell, we need to change that since we are using our new ImageCell class. So replace that line with

ImageCell *cell = (ImageCell *)[tableView dequeueReusableCellWithIdentifier:MyIdentifier];

Also we need to replace the cell = line in the if (cell == nil) if statement

cell = [[[ImageCell alloc] initWithFrame:CGRectZero reuseIdentifier:MyIdentifier] autorelease];

Now remove the cell.text line and replace it with the following

NSDictionary *itemAtIndex = (NSDictionary *)[self.jsonArray objectAtIndex:indexPath.row];
[cell setData:itemAtIndex];

Now you can build and go and you should get the following

As you can see you now have the title in nice big letters and the URL in smaller gray letters below the title. As always you can grabt he source code here.

More Complex JSON endpoint

Make sure you have JSON Framework installed first.

This is a continuation from First JSON Iphone application

So the last entry went into describing how we do very basic JSON. Now lets get into something a bit more complex. Here is what my array looks like in PHP.

$array = array(
        array(
                'title' => 'Brown Dog',
                'img'   => 'http://iphone.zcentric.com/files/1.jpg',
        ),
        array(
                'title' => 'Black Dog',
                'img'   => 'http://iphone.zcentric.com/files/2.jpg',
        ),
        array(
                'title' => 'Two Dogs',
                'img'   => 'http://iphone.zcentric.com/files/3.jpg',
        ),
        array(
                'title' => 'Girl',
                'img'   => 'http://iphone.zcentric.com/files/4.jpg',
        ),
);

So you can see it is a multi-dimensional array in PHP. In the main array are 4 sub-arrays that each have a title and a img key. The title is the title of the image and the img is a URL to the image. So we can pretty much use our example almost line for line to do this more complex array now

So first open up RootViewController.m and find the line where we inited the JSON URL. All you are doing is changing the endpoint

NSURL *jsonURL = [NSURL URLWithString:@"http://iphone.zcentric.com/test-json2.php"];

Before it was using test-json.php and now we are using test-json2.php.

Now go down to cellForRowAtIndexPath and remove the cell.text line that is there now. You want the cell text to look like this now

	NSDictionary *itemAtIndex = (NSDictionary *)[self.jsonArray objectAtIndex:indexPath.row];
	cell.text = [itemAtIndex objectForKey:@"title"];

So you can see what we are doing is grabbing a dictionary off the array at a certain index and then in that dictionary we are putting the title in for the cell text.

If you build and go now you should see this.

As always you can download the source here.

First JSON Iphone application

So now that we have the basics of installing JSON to our project, lets make a test application.

I started by creating a new project and used the Navigation Controller Template.

The first thing you want to do is setup your JSON environment as explained in the previous post and again you can visit the link above for instructions on how to do that.

Once it is setup you are ready to code. So the first thing is open up RootViewController.m and we need to import the JSON header so we can access it.

#import <JSON/JSON.h>

You need to synthesize the jsonArray

@synthesize jsonArray;

Now you want to edit the viewDidLoad function and make it look like this

- (void)viewDidLoad {
	// Add the following line if you want the list to be editable
	// self.navigationItem.leftBarButtonItem = self.editButtonItem;

	// init the url
	NSURL *jsonURL = [NSURL URLWithString:@"http://iphone.zcentric.com/test-json.php"];
	/*
	 loading the contents of the URL into a string. At this point we should hit
	 the json endpoint and put the contents of it in jsonData.
	*/
	NSString *jsonData = [[NSString alloc] initWithContentsOfURL:jsonURL];

	/*
	 we can verify the contents by looking at a NSLog if we open the console
	*/
	//NSLog(jsonData);

	// converting the json data into an array
	self.jsonArray = [jsonData JSONValue]; 

	// this will return the array.. in the console it will still look a bit like json
	// but that is a array object.
	//NSLog(@"%@", jsonArray);

	// this will give the current count
	//NSLog(@"count is: %i", [self.jsonArray count]);

	// releasing the vars now
	[jsonURL release];
	[jsonData release];
}

I hope I gave good comments in the code. Basically what we are doing is loading a url and placing the contents into a string and then using json to turn it into an array object that we can use later.

Now my array in PHP looks like this

$array = array(
        0       => 'http://iphone.zcentric.com/files/1.jpg',
        1       => 'http://iphone.zcentric.com/files/2.jpg',
        2       => 'http://iphone.zcentric.com/files/3.jpg',
        3       => 'http://iphone.zcentric.com/files/4.jpg',
);

I use 0-3 as keys to make it easy to load the values into a new row.

Now we need to tell the application how many rows we have

- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
	return [jsonArray count];
}

Then in the cellForRowAtIndexPath function above the return cell we want to set the text.

cell.text = (NSString *)[self.jsonArray objectAtIndex:indexPath.row];

Now we want to free the memory

- (void)dealloc {
	[jsonArray dealloc];
	[super dealloc];
}

Now we are done with this file so save it and close it and open up RootViewController.h. You want it to look like this

#import 

@interface RootViewController : UITableViewController {
	NSMutableArray *jsonArray; // added this
}

@property (nonatomic, retain) NSMutableArray *jsonArray;  // added this

@end

You should know to do this by now.

Once you are done you should be able to compile your application and see 4 rows. Also feel free to use my test-json.php endpoint since I know it works.

The source code is right here. Have fun!

Install json-framework

If you are attempting to write a Iphone application to talk to a webservice, JSON is one of the best ways to go. Now unfortunately there is no native support in the Iphone SDK for JSON. There is a project called json-framework that provides a framework called json-framework.

There is little documentation on how to install it though. Recently I found a news group posting on how to do it along with a dmg! I have mirrored the dmg file just in case it gets removed from that posting.

So here are some instructions on how to install it.

  • Download the dmg file
  • Make a directory called SDKs in your ~/Library/ directory. So if you username is bob. it would be /Users/bob/Library/SDKs
  • Copy the JSON directory to SDKs so you should have a directory called /Users/bob/Library/SDKs/JSON which has iphoneos.sdk and iphonesimulator.sdk directories inside it.
  • In Xcode load up your project and go to Project -> Edit Project Settings. You will see a box like this

  • At the top you will see Additional SDKs. Double click that and you will see

  • Now click the + sign and it will prompt you to add a directory. You will enter the following line
$HOME/Library/SDKs/JSON/$(PLATFORM_NAME).sdk
  • Now you need to add in the linker options. So now look for Other Linker Flags and add in the following
-ObjC -ljson
  • Now you can import the <JSON/JSON.h> header file and begin coding.

I have also mirrored their test application they built in the news group post. So if you download that and make the project changes there, you should be able to compile touchJSON into your application.