iPhone JSON Flickr Tutorial – Part 1
Two consistently popular posts on iPhone Dev Tips are JSON Frameworks for iPhone Part 1 and Part 2. Seems a good time to revisit the combination of the iPhone and JSON, this time creating a complete working application.
This is part one of a three part series in which I’ll build a Flickr photo viewer, a pretty simple application, however, we’ll cover some interesting stuff with the primary goal of understanding the nuts and bolts of working with JSON to build a complete working application accessing web-services.
Let’s start by looking at the finished application – the video below shows the end result we’re after – it starts with a search box (UITextField) and an empty table. Once you enter a search string, the application will construct a URL to call a photo search method at Flickr. The return data will be used to populate a table with thumbnails images and any title associated with the photo.
As you browse the table, you can touch an image and a second request will be submitted to Flickr for a larger variation of the same image. The larger image will be displayed above the table. As you select additional rows in the table, the current image will slide off the screen and a new image will slide into place. Watch the video below, it’ll make more sense once you see everything pulled together.
Setting up JSON Framework
The library used in this application is one written by Stig Brautaset and hosted at code.google.com. I’ve used this library in two applications that are in the App Store, Today’s Horoscope, free and paid. This is an excellent library, you can’t go wrong.
- Download the iPhone JSON framework
There are a few variations on the install process, the easiest and least prone to errors is to copy the source files directly into your project. In the project that is attached to this post, this is done for you, however, it’s shown here if you need to add the framework to an existing or future project.
- Open the dmg file from the download.
- Drag the JSON folder and drop it on the ‘Classes’ group in the ‘Groups & Files’ in your Xcode project.
- Check the “Copy items into destination group’s folder” option.
- You can now use #import “JSON.h” in your source files.
Xcode Project Download – Part 1
To keep this post to a manageable length, I’ll show only the code that is most pertinent to JSON, Flickr and the overall application flow, the code on the periphery you’ll be familiar with and you can browse through at any time in the project source code.
- Download iPhone, JSON and Flickr – Part 1 Xcode project
Get Flickr API Key
If you don’t already have a Flickr API key, that’s the next step.
The key will be an alpha-numeric value that you pass into all requests when calling a Flickr API. You’ll need to replace the key in the project for this post with your own Flickr key. Once you have the key, you’ll need to copy/paste the same into the FlickrAPIKey variable in the file JSONFlickrViewController.m.
NSString *const FlickrAPIKey = @"your-key-here";
Flickr APIs
Flickr has an impressive list of APIs you can access, take a look here Flickr API’s.
We’ll be using REST request format for calling Flickr, here is basic layout for how calls will look:
http://api.flickr.com/services/rest/?method=flickr.test.echo&name=value
For our interests, we want to search for photos, so the method we are after is: flickr.photos.search. The Flickr API page for this method is here.
The full URL we’ll pass to Flickr will look as follows:
NSString *urlString = [NSString stringWithFormat: @"http://api.flickr.com/services/rest/?method=flickr.photos.search&api_key=%@&tags=%@&per_page=25&format=json&nojsoncallback=1", FlickrAPIKey, text];
The parameters to the query are:
- api_key: your specific developer key
- tags: the search text
- per_page: the number of images to return
- format=json&nojsoncallback: request the results to be returned as JSON
Earth to Flickr, Come in Flickr
With our URL complete, our next step is to make sure we can talk to the appropriate Flickr API and retrieve images based on a search string.
We have a very basic application framework at this point, an App Delegate class and a bare bones view controller. The interface definition for the view controller follows:
@interface JSONFlickrViewController : UIViewController { NSMutableArray *photoTitles; // Titles of images NSMutableArray *photoSmallImageData; // Image data (thumbnail) NSMutableArray *photoURLsLargeImage; // URL to larger image }
At this point all we are concerned about is capturing the names of the images from Flickr, the image data for our thumbnail image and also, the URL’s for the larger images. The initialization code consists of allocating a view, initializing the arrays, and calling the method to search Flickr for photos matching a search string:
- (id)init { if (self = [super init]) { self.view = [[[UIView alloc] initWithFrame:[[UIScreen mainScreen] applicationFrame]] autorelease]; // Initialize our arrays photoTitles = [[NSMutableArray alloc] init]; photoSmallImageData = [[NSMutableArray alloc] init]; photoURLsLargeImage = [[NSMutableArray alloc] init]; // Notice that I am hard-coding the search tag at this point (@"iPhone") [self searchFlickrPhotos:@"iPhone"]; } return self; }
The call to [self searchFlickrPhotos:@"iPhone"]; begins the process of creating the URL to call Flickr, which in turn will kick off an asynchronous request to download the image data.
-(void)searchFlickrPhotos:(NSString *)text { // Build the string to call the Flickr API NSString *urlString = [NSString stringWithFormat: @"http://api.flickr.com/services/rest/?method=flickr.photos.search&api_key=%@&tags=%@&per_page=15&format=json&nojsoncallback=1", FlickrAPIKey, text]; // Create NSURL string from formatted string NSURL *url = [NSURL URLWithString:urlString]; // Setup and start async download NSURLRequest *request = [[NSURLRequest alloc] initWithURL: url]; NSURLConnection *connection = [[NSURLConnection alloc] initWithRequest:request delegate:self]; [connection release]; [request release]; }
At this point we’ve made our request to Flickr to search for photos that are tagged with the text “iPhone” From here, we need to process the incoming data. The method didReceiveData: is the callback that will deal with the incoming data. Within this method we’ll save the title of the image, the image data for the thumbnail as well as the URL for the large image. Here’s what that code looks like:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 | - (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data { // 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]; // Build an array from the dictionary for easy access to each entry NSArray *photos = [[results objectForKey:@"photos"] objectForKey:@"photo"]; // Loop through each entry in the dictionary... for (NSDictionary *photo in photos) { // Get title of the image NSString *title = [photo objectForKey:@"title"]; // Save the title to the photo titles array [photoTitles addObject:(title.length > 0 ? title : @"Untitled")]; // Build the URL to where the image is stored (see the Flickr API) // In the format http://farmX.static.flickr.com/server/id_secret.jpg // Notice the "_s" which requests a "small" image 75 x 75 pixels NSString *photoURLString = [NSString stringWithFormat:@"http://farm%@.static.flickr.com/%@/%@_%@_s.jpg", [photo objectForKey:@"farm"], [photo objectForKey:@"server"], [photo objectForKey:@"id"], [photo objectForKey:@"secret"]]; NSLog(@"photoURLString: %@", photoURLString); // The performance (scrolling) of the table will be much better if we // build an array of the image data here, and then add this data as // the cell.image value (see cellForRowAtIndexPath:) [photoSmallImageData addObject:[NSData dataWithContentsOfURL:[NSURL URLWithString:photoURLString]]]; // Build and save the URL to the large image so we can zoom // in on the image if requested photoURLString = [NSString stringWithFormat:@"http://farm%@.static.flickr.com/%@/%@_%@_m.jpg", [photo objectForKey:@"farm"], [photo objectForKey:@"server"], [photo objectForKey:@"id"], [photo objectForKey:@"secret"]]; [photoURLsLargeImage addObject:[NSURL URLWithString:photoURLString]]; NSLog(@"photoURLsLareImage: %@\n\n", photoURLString); } } |
Look closely at line 7: NSDictionary *results = [jsonString JSONValue]; which is where the magic occurs, converting the incoming JSON data into a dictionary, which makes our parsing the Flickr data as simple as working with key-value pairs. We’ll look closer at the JSON return values from Flickr, converting to a dictionary and building the arrays of image data in Part 2 of this tutorial.
Flickr Image Data
Notice in the code above how I added to NSLog statements to verify the return results. A partial listing of the output is shown below:

As one last sanity check, you can copy/paste a few of the links shown in the Xcode console into a browser window to see what photos Flickr is returning.
Looking Ahead to Part 2
In Part 2 of this tutorial we’ll start by looking at the raw data returned from Flickr and walk through how to map that information into dictionaries and arrays. We will also extend the current application to store the results from our query into a table. We’ll also add a textfield to the interface so we can search for photos matching any tag.





















Great information, thanks! I also appreciate the code examples included with the posts.
Thanks for this, you totally rock.
This is a great tutorial. Really looking forward to Part 2. Any idea when we can expect the latest on this topic? Thanks!
Thanks for the comments. Part 2 will be posted this week.
I am trying to run your sample (I got an API Key & inserted it correctly)… and I get this error no matter what I do. The error is “error: #error” reported on the line 22 of the JSONFlickrViewController.m file. What am I doing wrong here?
If you could let me know I would really appreciate it!
-Daniel Monroe
danielm@logictwilight.com
Daniel,
Sorry for the confusion – #error is a way to force a compiler error. I did this so it would be clear that you need to add your own API key value.
Once you add you API key, remove the line #error.
John
Thanks for going as far as posting your xcode flickr project dmg. I am new to xcode and iphone development in general, so my first obstacle was getting it to build. My first obstacle was an error around signing:
Code Sign error: The identity ‘iPhone Developer’ doesn’t match any valid certificate/private key pair in the default keychain
I dorked around a bit, removing your key and setting it to ‘don’t create keys’ but that did not fix it. Somebody on #iphonedev gave me the noob hint that I had to change the target from ‘base api’ to ‘iphone simulator’ in the top left dropdown.
Once I got there, I quickly found your #error and removed it and put my key in place. Now when I do build and run, I find the output on the gdb console of the debugger. Thats a great start, thank you!
Thanks for the great tutorial, I’ve got a strong JavaScript background and really prefer to use JSON over XML whenever possible, and this is great help.
I’m also new to iPhone development and Objective-C. As I’ve been reading and building many samples, one thing that has been emphasized repeatedly is memory management. In general I’ve been following the principles outlined in the Memory Management Rules posted here: http://developer.apple.com/iphone/library/documentation/Cocoa/Conceptual/MemoryMgmt/Articles/mmRules.html#//apple_ref/doc/uid/20000994
Here is the question I have. You allocate memory for jsonString within the connection method, but I can’t see that jsonString is ever released. Is this because the JSON framework takes care of it? Is it unnecessary because the NSURLConnection connection pointer itself is released?
NSString *jsonString = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding];
Thanks for any feedback.
- Ben
Good call Ben, jsonString does need to be released as it is created with a call to alloc.
First of all THX it’s a great lib,
i just have one question do i need to release any object that the JSONValue return? (NSDictionary/NSArray/NSString etc`) or are all auto-released objects?
Ishay
Any object that is created with alloc/copy does need to be released.
@ishaywei: The convention is that you need to release objects that are created with alloc / init / copy / new. However, that is a convention and not a requirement so if you are unsure the best way to find out is to take a look.
I just looked through the source for the JSON library and it looks like it DOES follow the convention.
The object returned from JSONValue is created using objectWithString, not initWithString ([jsonParser objectWithString:self];)
Looking back through the code chain I don’t see anything that makes me question it.
So you should not have to release the object you get back from [jsonString JSONValue]
Thanks for this great tutorial! I’ve used this tutorial to create an app that implements JSON from my server, but I’ve ran into one interesting issue. The didReceiveData method gets called multiple times, rather than just once, and each time it gets called, the data received is only part of the data that should be received. For example, when first called, it will return the first half of the data, and then the method is called a second time and returns the second half of the data. Other times when I run it, it will return the data in 3 parts rather than two.
Any idea what my be wrong? I’d like it to just call the didReceiveData method once and return all the data.
Thanks so much!
Hey Brad,
My example had very minimal data so a call to didReceiveData occurs just once. The default implementation is that this method will be called each time there is more data, which allows it to handle data downloads of varying sizing. In such cases, you would need to create your own internal buffer to append each successive chunk of data.
John
Thanks John! I got that to work!
That was THE best iPhone tutorial I have found yet. I am on a strict deadline for a school project and this has taught me exactly what I needed to know. Very clean code made for easy learning. Thanks, John!
Hey John, great article!
Just a very tiny comment: there may be a potential memory leak in following sample code
…
- (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data
{
// Store incoming data into a string
NSString *jsonString = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding];
…
jsonString is not released after being used
just my thought, may doesn’t make sense