iPhone JSON Flickr Tutorial – Part 2

Tue, Sep 8

Welcome to the second tutorial in this three part series on using the iPhone SDK, JSON and Flickr. In the first tutorial we covered the basics for setting up the JSON framework within your projects, registering to get Flickr API key, and writing some code to asynchronously fetch images and their accompanying titles from Flickr.

The application created in Part 1 had no UI to speak of, the only output was debug information written directly to the console as a test case to verify we could successfully request and download images using JSON and Flickr.

The video shown in Part 1 that highlights the finished application, developed over the course of these three tutorials, is displayed below if you need a refresher:




Download iPhone JSON Xcode Project File

You may find it helpful to download the Xcode project and walk through the source code as you read this tutorial.

  1. Download iPhone, JSON and Flickr – Part 2 Xcode project
Flickr JSON Data

Let’s begin by taking a closer look at the data returned from Flickr and the resulting dictionary that the JSON framework creates for us.

The method that we wrote in Part 1 for receiving date from Flickr, didReceiveData, begins with the following two lines:

1
2
3
4
5
// Store incoming data into a string
NSString *jsonString = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding];
 
// Create a dictionary from the JSON string
NSDictionary *results = [jsonString JSONValue];

Below is a screenshot of the jsonString (line 2 from above) as shown in the Xcode console. Everything you need is here, however, it would be a mess to parse this ourselves.

With one line of code (on line 5 above) the JSON framework will convert the string format into a dictionary. The image below shows a formatted output of the JSON, which better describes the dictionary – photos is a dictionary, and looking down to the fifth entry in the list, the key-value pair, with the key photo is an array of each photo returned from Flickr based on the search query we request. Each entry in the photo array is a dictionary as well, with keys “id”, “owner”, “secret”, “server”, etc.

With the help of the JSON framework and conversion to a dictionary, accessing the contents from Flickr becomes as easy as requesting key-value pairs. For example, to loop through the titles for each image we begin by creating a reference to the array of photos as listed here:

NSArray *photos = [[results objectForKey:@"photos"] objectForKey:@"photo"];

With the array in hand, we now access each entry in the array and create an NSString object from the key-value pair with the key “title”:

for (NSDictionary *photo in photos)
{
  // Get title of the image
  NSString *title = [photo objectForKey:@"title"];
  ...
}
Building Paths to the Images

Let’s review how to build the path to images hosted on Flickr, based on values in the JSON return data. To access an image, the path looks like this: http://farmX.static.flickr.com/server/id_secret.jpg, where each of the bold entries in the path is mapped to one of the entries in the photos array (see the image above).

For example, in the return results shown below, note the values for farm, server, id and secret.

Using the values above, we can now build a path to each image. For example, the path for the image data shown above looks as follows (screenshot of the Xcode console):

Define Table for Images and Titles

Let’s add code to the previous project to store the images and title returned from Flickr into a table. The result will look as shown below:

Begin by adding a tableview to JSONFlickrViewController to the implementation file JSONFlickrViewController.m as follows:

 
@interface JSONFlickrViewController : UIViewController <UITableViewDelegate, UITableViewDataSource>
{
  UITableView     *theTableView;
  NSMutableArray  *photoTitles;         // Titles of images
  NSMutableArray  *photoSmallImageData; // Image data (thumbnail)
  NSMutableArray  *photoURLsLargeImage; // URL to larger image
}

Now, inside the same file, we need to add code to initialize the table. In the init method, we’ll create the table as follows:

...
// Create table view
theTableView = [[UITableView alloc] initWithFrame:CGRectMake(0, 240, 320, 220)];
[theTableView setDelegate:self];
[theTableView setDataSource:self];
[theTableView setRowHeight:80];
[theTableView setBackgroundColor:[UIColor grayColor]];
[theTableView setSeparatorStyle:UITableViewCellSeparatorStyleNone];
[self.view addSubview:theTableView];
...

Notice the row height set to 80, as the Flickr thumbnail images are 75 x 75 pixels. The extra space allows some room around the image so it fits snuggly within the table row, yet has some visual padding.

Populate Table with Flickr Content

Next up is adding content to the table – we want an image on the left and its title text on the right. The code follows:

- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView 
{
  return 1;
}
 
- (NSInteger)tableView:(UITableView *)tableView 
      numberOfRowsInSection:(NSInteger)section 
{
  return [photoTitles count];
}
 
- (UITableViewCell *)tableView:(UITableView *)tableView 
      cellForRowAtIndexPath:(NSIndexPath *)indexPath 
{
  UITableViewCell *cell = 
      [tableView dequeueReusableCellWithIdentifier:@"cachedCell"];
 
if (cell == nil)
    cell = [[[UITableViewCell alloc] 
      initWithFrame:CGRectZero reuseIdentifier:@"cachedCell"] autorelease];
 
#if __IPHONE_3_0
  cell.textLabel.text = [photoTitles objectAtIndex:indexPath.row];
  cell.textLabel.font = [UIFont systemFontOfSize:13.0];
#else
  cell.text = [photoTitles objectAtIndex:indexPath.row];
  cell.font = [UIFont systemFontOfSize:13.0];
#endif
 
  NSData *imageData = [photoSmallImageData objectAtIndex:indexPath.row];
 
#if __IPHONE_3_0
  cell.imageView.image = [UIImage imageWithData:imageData];
#else
  cell.image = [UIImage imageWithData:imageData];
#endif
 
  return cell;
}

Most of this code is straightforward – there is one section in the table, the number of rows in that sole section is equal to the number of entries in the photoTitles array (which we built from the Flickr return data).

For each row in the table, we set the cell text by accessing the desired row from the photoTitles array. For the cell image we create a UIImage object from the photoSmallImageData array (again, from data we retreived from Flickr).

The last step is to make a call to populate the tableview once the data has been downloaded. Inside the method didReceiveData, at the bottom, once we’ve built all the internal arrays to hold the Flikr results, we make a request to reload the table data (see the project source code for the big picture of where this line of code lives):

// Update the table with data
[theTableView reloadData];
Looking Ahead to Part 3

At this point we can request data from Flickr, store the results into internal variables and populate and display a table of images and descriptions.

There are two things we need to finish before we can call this application complete. First, we need to add a textfield so users can input their desired search strings. Second, when a user taps on a row in the table, we want to display a larger version of the image above the table. We’ll do this by sliding a view onto the screen from right.

Summary of Links
  1. iPhone, JSON and Flickr Tutorial – Part 1
  2. iPhone, JSON and Flickr Tutorial – Part 3
  3. Download iPhone, JSON and Flickr – Part 2 Xcode Project
  4. Get Flickr API key
  5. Flickr API documentation
  6. Download JSON framework
  7. Stig Brautaset’s Blog

Stay tuned for Part 3 of the iPhone, JSON and Flickr tutorial…

19 comments

Hi,

This is definitely one of the most useful tutorials on the net. Very practical. You answered a question I had about building a path to an image.

I look forward to the next installment of this series.

by Craig on Sep 14, 2009. #

You rock this is an awesome post!

by Daniel Monroe on Sep 14, 2009. #

Hi,

How would you recommend handling the loading of 50 images, rather than 15? I can see that this could become slow. In the console it loads the path for all 50 images before displaying them to the table. Could it display the text as it loads then display the images later?

Thanks for your time.

by Craig on Sep 14, 2009. #

Craig,

The method connection: is called as the application is notified of more incoming information. The code as written assumes this method will be called just once with the data for the 15 images.

To manage a larger number of images, you can add smarts to that same method to append data to the arrays that hold the image, titles and url information.

Since the loading is done asynchronously, it shouldn’t affect the performance/responsiveness of the application.

John

by John Muchow on Sep 17, 2009. #

Excellent John,

Thanks again for the great tutorial and thanks for commenting the code so well.

Regards,
Craig

by Craig on Sep 18, 2009. #

Really nice tutorial. Can you expand to show how to get the photo description? I am unable to find it in the API or XML parser examples I’ve seen. Thanks.

by DenVog on Nov 13, 2009. #

DenVog,

Are you referring to the Title, which is shown above in the JSON output?

john

by John Muchow on Nov 13, 2009. #

Hi John. Thanks for the quick response. I’m referring to the field called “description”, that is displayed (in addition to “title”) when users upload a photo. “title” appears to be a short single line text field. “Description” is a longer multi-line text box.

by DenVog on Nov 13, 2009. #

There looks to be a different API that might be of help, it shows a description in the example response:

http://www.flickr.com/services/api/flickr.photos.getInfo.html

by John Muchow on Nov 13, 2009. #

Thanks John. I will have to see if I can integrate this getInfo API, with the way I am currently pulling the images.

by DenVog on Nov 13, 2009. #

John,.. you are a dev god! Thank-you for this well-structured and generous tutorial. My next is to present those thumbnails in a app-‘photo’–like grid structure, and replace the slide-in with a new view etc. etc. But your tut has the merit of being sober and to the point ! (and with a bit of humour!) :-)

by Shawn Koppenhoefer (@DrKdev) on Jan 9, 2010. #

Hi John,
This is indeed a great post, very helpful.
I do have a question regarding the method connection:didRecieve…
As you wrote, this function can be called multiple times (it will grab chuncks of data each time), so, how can i work with the data if it is not completed?
for example, the first chunk will have the data abot 15.5 photos (and not 16…), the next chunk will have the rest of the 16th photo, and it won’t have all the beginning of the JSON string…
Any suggestions on how to keep working with partial data in the meantime?

Thanks,
Roy.

by RoyZ on Feb 10, 2010. #

Hi Roy,

You will need to store the data in some type of internal data structure, appending the content each time more data arrives. For example, you could keep an array of objects, an add a new entry each time more data arrives.

John

by John Muchow on Feb 10, 2010. #

Hi John,

I’ve learnt quite a lot from your tutorial, thank you. One thing that confused me at first was at the very beginning of the tutorial where you add a tableview to JSONFlickrViewController.m I think you mean JSONFLickrViewController.h

Thanks for the great tutorial,

Les.

by Les Morton on Mar 21, 2011. #

Hi John,

This is a great tutorial. I needed to learn JSON and this really put everything together for me. Everything seems to be working and I have a good understanding of how to use it. Thank you so much.

I am having one slight problem with accessing flickr tho. I’m using my api key but am not getting my images from my flickr account. I’m getting someone else’s (which is kinda funny). Any ideas?
Thanks again and I hope you’re still responding to comments.
I’m off to conquer part 3.

by Gina on May 25, 2011. #

Hi Gina, if I recall correctly, the demo app I wrote does a simple search of flickr photos, not specifically photos for your flickr account. I imagine there is a flickr API to do this, so the code for searching would need to be updated to query your account. Hope that helps.

by John Muchow on May 25, 2011. #

Thank you John for your quick response. I looked into Flickr API docs and yes, you could update your query to include your user_id and this will download your own photos as specified. Flickr will provide the method for you. It’s hard-coded and I haven’t played with it too much but it seems pretty easy to use.
thanks again.

by Gina on May 25, 2011. #

hi, somehow I’m getting this error when trying to NSLog the titles:

2011-07-23 14:23:26.834 letsTestFlickrAPI[4318:b303] -JSONValue failed. Error trace is: (
“Error Domain=org.brautaset.JSON.ErrorDomain Code=8 \”Illegal escape sequence ‘0x0’\” UserInfo=0x4e62230 {NSLocalizedDescription=Illegal escape sequence ‘0x0′}”,
“Error Domain=org.brautaset.JSON.ErrorDomain Code=3 \”Object value expected for key: title\” UserInfo=0x4e62330 {NSUnderlyingError=0x4e62290 \”Illegal escape sequence ‘0x0’\”, NSLocalizedDescription=Object value expected for key: title}”,
“Error Domain=org.brautaset.JSON.ErrorDomain Code=3 \”Expected value while parsing array\” UserInfo=0x4e62380 {NSUnderlyingError=0x4e62310 \”Object value expected for key: title\”, NSLocalizedDescription=Expected value while parsing array}”,
“Error Domain=org.brautaset.JSON.ErrorDomain Code=3 \”Object value expected for key: photo\” UserInfo=0x4e62440 {NSUnderlyingError=0x4e62400 \”Expected value while parsing array\”, NSLocalizedDescription=Object value expected for key: photo}”,
“Error Domain=org.brautaset.JSON.ErrorDomain Code=3 \”Object value expected for key: photos\” UserInfo=0x4e624f0 {NSUnderlyingError=0x4e62420 \”Object value expected for key: photo\”, NSLocalizedDescription=Object value expected for key: photos}”
)
2011-07-23 14:23:26.836 letsTestFlickrAPI[4318:b303] -JSONValue failed. Error trace is: (
“Error Domain=org.brautaset.JSON.ErrorDomain Code=10 \”Garbage after JSON\” UserInfo=0x4b48870 {NSLocalizedDescription=Garbage after JSON}”
)
2011-07-23 14:23:27.009 letsTestFlickrAPI[4318:b303] -JSONValue failed. Error trace is: (
“Error Domain=org.brautaset.JSON.ErrorDomain Code=3 \”Unrecognised leading character\” UserInfo=0x4e5bdb0 {NSLocalizedDescription=Unrecognised leading character}”
)
2011-07-23 14:23:27.013 letsTestFlickrAPI[4318:b303] -JSONValue failed. Error trace is: (
“Error Domain=org.brautaset.JSON.ErrorDomain Code=10 \”Garbage after JSON\” UserInfo=0x4e5a6e0 {NSLocalizedDescription=Garbage after JSON}”
)

any idea why? I exactly followed your steps!

by John on Jul 23, 2011. #

awsome tutorial !!thnxxx

by riaz on Nov 18, 2011. #