iPhone JSON Flickr Tutorial – Part 1

August 26, 2009

Two consistently popular posts on are iPhone 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.

Setting up JSON Framework

The library used in this application is one written by Stig Brautaset and hosted at github. 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.

  1. 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.

  1. Open the dmg file from the download.
  2. Drag the JSON folder and drop it on the ‘Classes’ group in the ‘Groups & Files’ in your Xcode project.
  3. Check the “Copy items into destination group’s folder” option.
  4. 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.

  1. 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.

  1. Get Flickr API key.

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:

  1. api_key: your specific developer key
  2. tags: the search text
  3. per_page: the number of images to return
  4. 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.

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

30 comments

Great information, thanks! I also appreciate the code examples included with the posts.

by MarkZ on Aug 26, 2009. Reply #

Thanks for this, you totally rock.

by Kb on Aug 26, 2009. Reply #

This is a great tutorial. Really looking forward to Part 2. Any idea when we can expect the latest on this topic? Thanks!

by illstr8or on Sep 7, 2009. Reply #

Thanks for the comments. Part 2 will be posted this week.

by John Muchow on Sep 7, 2009. Reply #

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

by Daniel Monroe on Sep 14, 2009. Reply #

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

by John Muchow on Sep 14, 2009. Reply #

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!

by Michael Will on Nov 19, 2009. Reply #

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

by Ben on Jan 7, 2010. Reply #

Good call Ben, jsonString does need to be released as it is created with a call to alloc.

by John Muchow on Jan 8, 2010. Reply #

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

by ishaywei on Jan 14, 2010. Reply #

Any object that is created with alloc/copy does need to be released.

by John Muchow on Jan 14, 2010. Reply #

@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]

by Ben on Jan 14, 2010. Reply #

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!

by Brad on Mar 8, 2010. Reply #

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

by John Muchow on Mar 8, 2010. Reply #

Thanks John! I got that to work!

by Brad on Mar 8, 2010. Reply #

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!

by Dan Goodwin on Mar 12, 2010. Reply #

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

by Yuan Chen on Aug 17, 2010. Reply #

Hello

in the bellow snippet profileType is defined as a NSString while year is defined as an int.
When setting the values of my class with the json object the first line works but the second fail as year expect an int but not a NSString nor a Dictionary Object. What is the good/clean approach to cast my json year entry into an int?

profileType = [NSString stringWithFormat:@"%@", [jsonEntry objectForKey:@"profileType"]]
year = [jsonEntry objectForKey:@"year"]

Thanks :-)

by Deve on Nov 27, 2010. Reply #

If ‘year’ is an NSString, you can use the method integerValue to convert the string to an integer.

by John Muchow on Nov 27, 2010. Reply #

Hi,

Your JSON parser code doesn’t seem to be available anymore, would you mind providing it?

by Chris on Apr 27, 2011. Reply #

Which link no longer seems to work?

by John Muchow on Apr 27, 2011. Reply #

Hey John,

Thanks for the tutorial !

Just one suggestion : wouldn’t it be nicer to create a Photo class with attributes title, smallImageData and largeImageUrl and then having an NSMutableArray of those instead of storing everything in three separate NSMutableArrays ?

by Johanisma on Jul 14, 2011. Reply #

Yes, there are probably a number of ways to extend the code and make things more robust if you were to use this within a more complete project. Often times I write short examples with the primary intention to demonstrate a concept and keep things simple…

by John Muchow on Jul 14, 2011. Reply #

Hi, first off, great tutorial!! I must say I am new to iOS development so please bear with me:)

I do not really understand the function of the -(id)init method. I mean, could you not initialize the arrays in the viewWillLoad method? And whats the need to alloc memory for the view? And after that you call the [self searchFlickrPhotos:@"iPhone"]; function. Why do you do so? The user didn’t want to do so or am I missing something?

by Rajat Agarwal on Jul 19, 2011. Reply #

Here is a post that discusses initialization, it may provide some insight.

by John Muchow on Jul 19, 2011. Reply #

Hi John this tutorial is great! I downloaded the source files, and see that (for me at least) the following are missing:
under Products folder: JSONFlickr.app
under Frameworks folder: Foundation.framework , CoreGraphics.framework, and UIKit.framework

It also show “Base SDK Missing” in the Xcode dropdown top left.

I’m new to Xcode and may be overlooking something.

Thx!

by Adam on Aug 29, 2011. Reply #

When using Xcode, you will always need to check the build set in the project with the version(s) you have installed. In other words, the project was created some time ago, with an older SDK. Chances are you are using a much new SDK, so you will need to change that in the build settings to match you SDK version.

by John Muchow on Aug 29, 2011. Reply #

I’ve tried to run your projects many times, but it does not work out properly. All I changed is replacing the flicker api key and commenting out ‘#error’. Though it does build without any issue, it just blacked out when I run it on the iPhone 5.0 simulator.

Is there any change in the project? or Do I have to do something else?

by Kyoung on Apr 23, 2012. Reply #

Is there an updated version of this? I am getting a TON of errors when trying to get this working with XCode 4.

by Brian on May 3, 2012. Reply #

Ok, I created a new application from scratch using XCode 4; I made sure to NOT select a storyboard and I also unchecked automatic reference counting. Next I added a GROUP or folder to my project called ‘Classes’ and I copied the JSON folder from the download into the Classes folder. I compiled the project which popped up the #error and I entered my code from Flickr. I also copied my secret into the associated place. I ran the project and saw in the console window several image URLs. I copied a couple of these into the browser and it appears this is working…..NOW, how to I convert this library to work in XCode 4 while using the latest features of XCode like automatic reference counting and the storyboard.

Thanks all!

by Brian on May 3, 2012. Reply #

Leave a Comment