iPhone JSON Flickr Tutorial – Part 3

Thu, Sep 17

Welcome to the third and final post on working with the iPhone SDK, JSON and the Flickr API. In I Part 1 we setup a JSON library, created a Flickr API key and wrote code to retrieve images and the titles from Flickr.

In Part 2 of the series we dove into the how to quickly parse information returned from a JSON service using a JSON framework. We also created a table to display images returned from Flickr.

In this tutorial we wrap up by adding a simple image viewer that will display a larger variation of the image when the user taps on a row in the table. We’ll add some code to slide the larger images on/off screen, which is a nice visual effect for this application. You can review the finished application by watching the video below:




Download iPhone JSON Xcode Project

You can download the Xcode project and walk through the source code:

  1. Download iPhone, JSON and Flickr – Part 3 Xcode project
Zoomed Image Viewer

Let’s begin by creating a simple class for viewing the larger variation of the image in the table. The class definition follows:

@interface ZoomedImageView : UIView
{
  UIImageView *fullsizeImage;
}
 
- (id)initWithURL:(NSURL *)url;
 
@end

Not much here – just a UIView with an UIImageView to hold the large image downloaded from Flickr. Let’s look at the implementation of this class.

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
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
#import "ZoomedImageView.h"
 
/*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
* Private interface definitions
*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/
@interface ZoomedImageView(private)
- (void)slideViewOffScreen;
@end
 
@implementation ZoomedImageView
 
/**************************************************************************
*
* Private implementation section
*
**************************************************************************/
 
#pragma mark -
#pragma mark Private Methods
 
- (void)slideViewOffScreen
{
  // Get the frame of this view
  CGRect frame = self.frame;
 
  [UIView beginAnimations:nil context:NULL];
  [UIView setAnimationDuration:.45];
 
  // Set view to this offscreen location
  frame.origin.x = -320;
  self.frame = frame;
 
  // Slide view
  [UIView commitAnimations];
}
 
/**************************************************************************
*
* Class implementation section
*
**************************************************************************/
 
#pragma mark -
#pragma mark Initialization
 
- (id)initWithURL:(NSURL *)url
{
  if (self = [super init])
  {
    // Create the view offscreen (to the right)
    self.frame = CGRectMake(320, 0, 320, 240);
 
    // Setup image
    NSData *imageData = [NSData dataWithContentsOfURL:url];
    fullsizeImage = [[UIImageView alloc] initWithImage:[UIImage imageWithData:imageData]];
 
    // Center the image...
    int width = fullsizeImage.frame.size.width;
    int height = fullsizeImage.frame.size.height; 
 
    int x = (320 - width) / 2;
    int y = (240 - height) / 2;
 
    [fullsizeImage setFrame:CGRectMake(x, y, width, height)];     
    fullsizeImage.userInteractionEnabled = YES;                                      
    [self addSubview:fullsizeImage];
 
  }
 
  return self;  
}
 
#pragma mark -
#pragma mark Event Mgmt
 
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event 
{
  [self slideViewOffScreen];
 
  // We now send the same event up to the next responder
  // (the JSONFlickrViewController) so we can show enable
  // the search textfield again
  [self.nextResponder touchesBegan:touches withEvent:event];
 
}
 
#pragma mark -
#pragma mark Cleanup
 
- (void)dealloc 
{
  [fullsizeImage release];
  [super dealloc];
}
 
@end

It starts the the initialization code on line 46 – we pass in the URL of the larger image that we saved previously when making the original request for images matching a search string (more on that below).

Notice the x coordinate on line 51 has a value of 320, which sets this view off-screen to the right. As we’ll see momentarily, to accomplish the slide-in effect we’ll move the x coordinate to an on-screen location, which will slide the image into place. We wrap up the initialization code by creating the image, centering the image in the view, and adding the image as a subview.

Once an image is shown on screen, when a new image is requested by the user (tapping on a table row) we need to slide the current view off the screen to the left. The code beginning on line 21 shows how we accomplish this – setup the animation, move the frame for the current image off-screen to the left (-320) and commit the animation.

Search TextField and Activity Indicator

Our next task is to create the search textfield. Head over to the JSONFlickrViewController implementation file, where we need to add a UITextField, also add an activity indicator as shown here:

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

The code for initializing the textfield and activity indicator are inside the init method of the JSONFlickrViewController class:

- (id)init
 
   ...
 
    // Create textfield for the search text
    searchTextField = [[[UITextField alloc] initWithFrame:CGRectMake(110, 100, 100, 40)] retain];
    [searchTextField setBorderStyle:UITextBorderStyleRoundedRect];
    searchTextField.placeholder = @"search";
    searchTextField.returnKeyType = UIReturnKeyDone;
    searchTextField.clearButtonMode = UITextFieldViewModeWhileEditing;
    searchTextField.delegate = self;
    [self.view addSubview:searchTextField];
    [searchTextField release];
 
    // Create activity indicator
    activityIndicator = [[UIActivityIndicatorView alloc] initWithFrame:CGRectMake(220, 110, 15, 15)];
    activityIndicator.activityIndicatorViewStyle = UIActivityIndicatorViewStyleWhite;
    activityIndicator.autoresizingMask = (UIViewAutoresizingFlexibleLeftMargin |
                      UIViewAutoresizingFlexibleRightMargin |
                      UIViewAutoresizingFlexibleTopMargin |
                      UIViewAutoresizingFlexibleBottomMargin);
    [	activityIndicator sizeToFit];
    activityIndicator.hidesWhenStopped = YES; 
    [self.view addSubview:activityIndicator];
 
  ...
}

When new search text is entered, the code in the method textFieldShouldReturn will be run. Inside this method we remove all the objects from each of the arrays we created previously when calling Flickr (if we’ve called Flickr previously) and start the activity indicator.

Next, call the method searchFlickrPhotos which will kick-off the process for calling the Flickr API and rebuilding the arrays of content based on the JSON returned from the service – See Part 1 for more information. Once the query is complete, the activity indicator will be stopped (you can see this code in the downloaded Xcode project).

- (BOOL)textFieldShouldReturn:(UITextField *)textField
{
  [textField resignFirstResponder];
 
  // Remove any content from a previous search
  [photoTitles removeAllObjects];
  [photoSmallImageData removeAllObjects];
  [photoURLsLargeImage removeAllObjects];
 
  // Begin the call to Flickr
  [self searchFlickrPhotos:searchTextField.text];
 
  // Start the busy indicator
  [activityIndicator startAnimating];
 
  return YES;
}
Wrapping all the Pieces Together

At this point all that’s left is to recognize when a new table row has been selected and begin the process for downloading the larger image view and sliding the image into place on the display. It all begins in the code for managing the table with the method shown below:

- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath 
{
  searchTextField.hidden = YES;
 
  // If we've created this VC before...
  if (fullImageViewController != nil)
  {
    // Slide this view off screen
    CGRect frame = fullImageViewController.frame;
 
    [UIView beginAnimations:nil context:NULL];
    [UIView setAnimationDuration:.45];
 
    // Off screen location
    frame.origin.x = -320;
    fullImageViewController.frame = frame;
 
    [UIView commitAnimations];
  }
 
  [self performSelector:@selector(showZoomedImage:) withObject:indexPath afterDelay:0.1];
}

We begin by hiding the search textfield, then, if a zoomed image is visible, we need to slide it off-screen – we do this by setting up the animation, move the image location (frame) to an off-screen location (-320) and committing the animation. Once that is complete, we call showZoomedImage to display the image the user requested.

The last chunk of code is for displaying the requested image:

- (void)showZoomedImage:(NSIndexPath *)indexPath
{
  // Remove from view (and release)
  if ([fullImageViewController superview])
  	[fullImageViewController removeFromSuperview];
 
  fullImageViewController = [[ZoomedImageView alloc] initWithURL:[photoURLsLargeImage objectAtIndex:indexPath.row]];
 
  [self.view addSubview:fullImageViewController];
 
    // Slide this view off screen
  CGRect frame = fullImageViewController.frame;
 
  [UIView beginAnimations:nil context:NULL];
  [UIView setAnimationDuration:.45];
 
  // Slide the image to its new location (onscreen)
  frame.origin.x = 0;
  fullImageViewController.frame = frame;
 
  [UIView commitAnimations];
}

We start by releasing the current zoomed view (if there is one). Next, we create a new ZoomedImageView, passing in the URL of the larger image that was saved previously when we parsed the JSON data (see the previous posts). We setup the animation, move x coordinate of the image into place and commit the animation.

Final Notes

With that, we have a complete working application that will call Flickr based on a user requested search string, display a table of images and titles and allow zooming in on each image by selecting a row in the table.

It’s hard to get the big picture when looking at code snippets as shown in this tutorial. Once you download and tinker with the Xcode project, it’ll all make sense.

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

19 comments

Thanks for this, great work as usual.

I have one question:

How would I go about allowing the user to search for multiple tag words. For example, in HTTP Client or directly in the Safari browser I can use the urlString (provided in the xCode project) with tags: ‘hot dog’ (without the quotes) and have a result set returned.

If you enter the search term: hot dog into the search textField then the debug(@”%@”, url); returns null. So no search would reach flickr.

I had the same outcome when I implemented a UISearchBar to perform the search. Any ideas?

Regards,
Craig

by Craig on Sep 18, 2009. #

Craig,

If you look at the Flickr API for the search method: http://www.flickr.com/services/api/flickr.photos.search.html you’ll see notes about working with tags (which is the way we pass in search parameters to the method). For example:

tags (Optional)
A comma-delimited list of tags. Photos with one or more of the tags listed will be returned.
tag_mode (Optional)
Either ‘any’ for an OR combination of tags, or ‘all’ for an AND combination. Defaults to ‘any’ if not specified.

If I enter search text as “hot,dog” it will return results from Flickr. If you want to search for “hot dog” chances are you will need to update the application to add the tag_mode as shown above so you can use the AND and OR functionality.

Hope that helps.

John

by John Muchow on Sep 18, 2009. #

Got it. Thanks.

So, as well as adding the tag_mode=all to the search I also have to make sure that the search terms are separated by commas. Searching for “hot dog” still returned a null url on my end but if I force a comma between the search terms everything works fine.

Thanks.

by Craig on Sep 18, 2009. #

Hi John,

My previous solution to handling the user entering text such as “beer glass” was not very clean. Here is a cleaner solution:

// Place the code into the searchFlickrPhotos method after the NSString *urlString line.

// Add this so the user can enter spaces between search terms

NSString *searchURLString = 
  NSMakeCollectable(CFURLCreateStringByAddingPercentEscapes(
      NULL,     
      (CFStringRef)urlString,
      (CFStringRef)@"%+#",  // do not want these chars escaped
      NULL,
      kCFStringEncodingUTF8));
 
debug(@"escapedURL: %@", searchURLString);
NSURL *url = [NSURL URLWithString:searchURLString];
debug(@"URL: %@", url);

Regards,
Craig

by Craig on Sep 18, 2009. #

Just found this site. It’s fantastic. Thanks for all of your work. You’re amazing. Now on with the JSON…;)

by jordan on Sep 21, 2009. #

John, you are a good person for posting all of this. I’m applying this to making an app that uses google local search. Everything is good except I get this error:

Error Domain=org.brautaset.JSON.ErrorDomain Code=5 UserInfo=0x125fe30 “Unescaped control character ‘0x0′”,
Error Domain=org.brautaset.JSON.ErrorDomain Code=3 UserInfo=0x1260200 “Object key string expected”,
Error Domain=org.brautaset.JSON.ErrorDomain Code=3 UserInfo=0x12602a0 “Expected value while parsing array”,
Error Domain=org.brautaset.JSON.ErrorDomain Code=3 UserInfo=0x1260320 “Object value expected for key: results”,

I’m requesting the JSON file from http://ajax.googleapis.com/ajax/services/search/local?q=pizza&v=1.0

When I print the JSON string in the log it does have weird characters in it, but I would assume to JSON framework would be smart enough to handle it.

I’ve tried to google to problem but unsuccessful. Can anyone offer help?

by Jared on Oct 1, 2009. #

Jared,

That error signifies that the input you’re parsing is improperly escaped. The JSON on that page loads just fine for me. Unless the url returns different content for you and me, it looks–I’m sorry to say–like you’re doing something wrong… I cannot say what that is without seeing more of your code.

by Stig Brautaset on Oct 2, 2009. #

@Stig

You are right. I was doing something wrong. I figured it out by taking what I thought was the JSON string and running it through a JSON validator. It failed. I looked to see where and I saw that the string was being broken up by time stamps from the console, which meant that the string was being segmented somehow. After looking at my code I saw that I was calling

NSDictionary *results = [responseString JSONValue];

from within the

– (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data

method instead of the

– (void)connectionDidFinishLoading:(NSURLConnection *)connection

method. As soon as I switched to the latter method, the whole thing worked like a charm. Hopefully this helps someone else out with what they are doing. Thanks again.

by Jared on Oct 2, 2009. #

I’m banging my head against the wall with this one.

I’m getting info from Google’s local search. I’m putting all data into their own arrays. I can get everything except for the phone number because it is another “section” deep in the JSON data.

The string I’m able to get for the phone number is this:

(
{
number = “(415) 436-9380″;
type = “”;
},
{
number = “(415) 436-9535″;
type = “”;
}
)

My .h file is:

#import

@interface JSONViewController : UIViewController {

NSMutableData *responseData; // the data that holds what was returned by the request
NSMutableArray *urlName; // Name of URL Request
NSMutableArray *urlRequestLat; // Latitude of URL Request Locations
NSMutableArray *urlRequestLon; // Longitude of URL Request Locations
NSMutableArray *urlStreetAddress; // Street Address of URL Request Locations
NSMutableArray *urlCity; //City of URL Request Locations
NSMutableArray *urlRegion; // Region/State Address of URL Request Locations
NSMutableArray *urlPhoneNumber; // Phone number of URL Request Locations

}

@property (nonatomic, retain) NSMutableArray *urlName;
@property (nonatomic, retain) NSMutableArray *urlRequestLat;
@property (nonatomic, retain) NSMutableArray *urlRequestLon;
@property (nonatomic, retain) NSMutableArray *urlStreetAddress;
@property (nonatomic, retain) NSMutableArray *urlCity;
@property (nonatomic, retain) NSMutableArray *urlRegion;
@property (nonatomic, retain) NSMutableArray *urlPhoneNumber;

@end

My .m file is:

#import “JSONViewController.h”
#import “JSON.h”

@implementation JSONViewController

@synthesize
urlName,
urlRequestLat,
urlRequestLon,
urlStreetAddress,
urlCity,
urlRegion,
urlPhoneNumber;

/*
// The designated initializer. Override to perform setup that is required before the view is loaded.
– (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil {
if (self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil]) {
// Custom initialization
}
return self;
}
*/

/*
// Implement loadView to create a view hierarchy programmatically, without using a nib.
– (void)loadView {
}
*/

// Implement viewDidLoad to do additional setup after loading the view, typically from a nib.
– (void)viewDidLoad {
[super viewDidLoad];

// Initialize the arrays
urlName = [[NSMutableArray alloc] init];
urlRequestLat = [[NSMutableArray alloc] init];
urlRequestLon = [[NSMutableArray alloc] init];
urlStreetAddress = [[NSMutableArray alloc] init];
urlCity = [[NSMutableArray alloc] init];
urlRegion = [[NSMutableArray alloc] init];
urlPhoneNumber = [[NSMutableArray alloc] init];

responseData = [[NSMutableData data] retain];
NSURLRequest *request = [NSURLRequest requestWithURL:[NSURL URLWithString:@”http://ajax.googleapis.com/ajax/services/search/local?v=1.0&q=pizza&rsz=large&start=0″]];
[[NSURLConnection alloc] initWithRequest:request delegate:self];
}

– (void)connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)response {
[responseData setLength:0];
}
– (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data {
[responseData appendData:data];
}
– (void)connection:(NSURLConnection *)connection didFailWithError:(NSError *)error {
NSLog(@”Connection failed: are you connected to the Internet?”);
}
– (void)connectionDidFinishLoading:(NSURLConnection *)connection {

NSLog(@”connectionDidFinishLoading”);

[connection release];

NSString *responseString = [[NSString alloc] initWithData:responseData encoding:NSUTF8StringEncoding];
[responseData release];

//NSLog(@”%@”,responseString);

// Create a dictionary from the JSON string
NSDictionary *results = [responseString JSONValue];

//NSArray *numbersArray = [[[results objectForKey:@”responseData”] objectForKey:@”results”] objectForKey:@”phoneNumbers”];
//NSArray *numbersArray = [[[results objectForKey:@”responseData”] objectForKey:@”results”] objectAtIndex:];
//NSArray *numbersArray = [[[results objectForKey:@”responseData”] objectForKey:@”results”] objectAtIndex:0]; // works
//NSArray *numbersArray = [[[[results objectForKey:@”responseData”] objectForKey:@”results”] objectAtIndex:0] objectAtIndex:0]; // does not work

// Build an array from the dictionary for easy access to each entry
//NSArray *photos = [[results objectForKey:@”photos”] objectForKey:@”photo”];
NSArray *values = [[results objectForKey:@”responseData”] objectForKey:@”results”];
NSLog(@”%d objects in the values array”,[values count]);

//NSArray *numbersArray = [[[results objectForKey:@”responseData”] objectForKey:@”results”] objectForKey:@”phoneNumbers”];

[responseString release];

NSError *error;
if (values == nil)
//label.text = [NSString stringWithFormat:@”JSON parsing failed: %@”, [error localizedDescription]];
//NSLog([NSString stringWithFormat:@”JSON parsing failed: %@”, [error localizedDescription]]);
NSLog(@”JSON parsing failed: %@”, [error localizedDescription]);
else {

// Loop through each entry in the dictionary…
NSString *thisText;
for (NSDictionary *value in values){
// Get title
thisText = [value objectForKey:@”titleNoFormatting”];
NSLog(@”%@”,thisText);
[urlName addObject:(thisText.length > 0 ? thisText : @”Untitled”)];

// Get latitude
thisText = [value objectForKey:@”lat”];
NSLog(@”%@”,thisText);
[urlRequestLat addObject:thisText];

// Get longitude
thisText = [value objectForKey:@”lng”];
NSLog(@”%@”,thisText);
[urlRequestLon addObject:thisText];

// Get street address
thisText = [value objectForKey:@”streetAddress”];
NSLog(@”%@”,thisText);
[urlStreetAddress addObject:thisText];

// Get city
thisText = [value objectForKey:@”city”];
NSLog(@”%@”,thisText);
[urlCity addObject:thisText];

// Get region
thisText = [value objectForKey:@”region”];
NSLog(@”%@”,thisText);
[urlRegion addObject:thisText];

// Get phone number
//for (NSDictionary *photo in numbersArray){

thisText = [value objectForKey:@”phoneNumbers”];
NSLog(@”%@”,thisText);

//thisText = [[photo objectForKey:@”phoneNumbers”] objectAtIndex:1];
//NSLog(@”%@”,thisText);
//NSString *newString = [thisText stringByReplacingOccurrencesOfString:@”\(” withString:@””];

//NSString *newString = [thisText substringWithRange:NSMakeRange(3, 5)];

//NSLog(@”%@”,newString);

//NSDictionary *results2 = [thisText JSONValue];
//NSArray *numbersArray = [thisText objectForKey:@”number”];

[urlPhoneNumber addObject:thisText];

//}

}

}

}

/*
// Override to allow orientations other than the default portrait orientation.
– (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation {
// Return YES for supported orientations
return (interfaceOrientation == UIInterfaceOrientationPortrait);
}
*/

– (void)didReceiveMemoryWarning {
// Releases the view if it doesn’t have a superview.
[super didReceiveMemoryWarning];

// Release any cached data, images, etc that aren’t in use.
}

– (void)viewDidUnload {
// Release any retained subviews of the main view.
// e.g. self.myOutlet = nil;
}

– (void)dealloc {

[urlName release];
[urlRequestLat release];
[urlRequestLon release];
[urlStreetAddress release];
[urlCity release];
[urlRegion release];
[urlPhoneNumber release];

[super dealloc];
}

@end

You can see some of the things I tried (which are commented out in the .m file). What can I do to just get the phone number ((415) 436-9380 in the above example). Perhaps some regex or another way of implementing the JSON method? Perhaps another way of setting up the array?

Thanks in advance.

by Jared on Oct 18, 2009. #

Hi, John, your tutorial is awesome!
I have a question for JSON parser: how can we parse the field which are NON String? for example, integer, boolean, or float type. It seems in your flicker example, everything is treated as NSString, like farm, in the returned son data, it’s type is integer, but in your code you treated it as NSString.
Can we get the value of integer directly using JSON API? like:

int farm = JsonResult.getInteger(@”farm”);

Thanks

by steve on Nov 18, 2009. #

Steve,

Your question is more about Objective-C than the JSON.framework itself. The parser will translate JSON types into Objective-C types in the way described here: http://json-framework.googlecode.com/svn/branches/2.2/documentation/interfaceSBJsonParser.html#_details

You will get a dictionary (or, in some cases, an array) back from the parser. If something is an integer in the JSON text, then you can get its value back like this:

NSNumber tmp = [jsonResult objectForKey:@”farm”];
NSInteger farm = [tmp integerValue];

Or in one line:

NSInteger farm = [[jsonResult objectForKey:@”farm”] integerValue];

by Stig Brautaset on Nov 19, 2009. #

Thank you so much for doing this tutorial!

My group’s semester project is to create a iphone travel app. My part is getting yelp api to work. I downloaded part 1 & just replaced only the parts I needed with the yelp api. http://www.yelp.com/developers/documentation/search_api#searchHood

However I’m getting the same error as Jared did but I don’t understand his solution.

The site I’m calling is http://api.yelp.com/business_review_search?term=pizza&ywsid=9udh9Zd8kh_kqoXmAPaY0g&location=San2ose庌A&num_biz_requested=3

The errors I get is:
[Session started at 2009-11-30 00:33:39 -0600.]
2009-11-30 00:33:40.848 JSONFlickr[2518:20b] -JSONValue failed. Error trace is: (
Error Domain=org.brautaset.JSON.ErrorDomain Code=3 UserInfo=0x571980 “Object key string expected”,
Error Domain=org.brautaset.JSON.ErrorDomain Code=3 UserInfo=0x571cf0 “Expected value while parsing array”,
Error Domain=org.brautaset.JSON.ErrorDomain Code=3 UserInfo=0x571da0 “Object value expected for key: reviews”,
Error Domain=org.brautaset.JSON.ErrorDomain Code=3 UserInfo=0x571e60 “Expected value while parsing array”,
Error Domain=org.brautaset.JSON.ErrorDomain Code=3 UserInfo=0x571ee0 “Object value expected for key: businesses”
)
2009-11-30 00:33:40.851 JSONFlickr[2518:20b] -JSONValue failed. Error trace is: (
Error Domain=org.brautaset.JSON.ErrorDomain Code=3 UserInfo=0x56b830 “Unrecognised leading character”
)

Here is the method where I know I’m not calling something right. If I NSLog(jsonString), it prints the JSON output but it also puts the errors above in the outputs. Please help.

– (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data

//- (void)connectionDidFinishLoading:(NSURLConnection *)connection;
{
// 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 *categories_info = [[results objectForKey:@”businesses”] objectForKey:@”categories”];

// Loop through each entry in the dictionary…
for (NSDictionary *cate in categories_info)
{
// Get title of the image
NSString *name = [cate objectForKey:@”name”];

debug(@”name: %@”, name);

}

}

Thanks in advance!

-Amy

by Amy Zou on Nov 30, 2009. #

After I posted my 1st comment I realized that even though the site I was calling had some funky characters, yelp still returned fine if I enter it in firefox. Do you think it’s my “%20″ between San Jose CA down at the bottom method that is messing with everything??

The site really should have this URL: http://api.yelp.com /business_review_search?term=pizza&ywsid=9udh9Zd8kh_kqoXmAPaY0g& location=San%20Jose%20CA&num_biz_requested=3

-(void)searchFlickrPhotos:(NSString *)term_param loc:(NSString *)location_param
{
// Build the string to call the Flickr API
NSString *urlString = [NSString stringWithFormat:@”http://api.yelp.com/business_review_search?term=%@&ywsid=%@&location=%@&num_biz_requested=3″, term_param, YelpAPIKey, location_param];
NSLog(urlString);
// 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];

}

/**************************************************************************
*
* Class implementation section
*
**************************************************************************/

#pragma mark –
#pragma mark Initialization

/*————————————————————-
*
*————————————————————*/
– (id)init
{
if (self = [super init])
{
self.view = [[[UIView alloc] initWithFrame:[[UIScreen mainScreen] applicationFrame]] autorelease];

// Initialize our arrays
info = [[NSMutableArray alloc] init];
reviews = [[NSMutableArray alloc] init];
reviews1 = [[NSMutableArray alloc] init];

// Notice that I am hard-coding the search tag at this point (@”iPhone”)
[self searchFlickrPhotos:@”pizza” loc:@”San%20Jose%20CA”];

}
return self;

}

by Amy Zou on Nov 30, 2009. #

Hello,

I’m having trouble figuring out what is causing an “Object value expected for key: venues” error when I attempt to parse a JSON file[1] in my iPhone app. I have created a schema[2] for my JSON file and it passes a check with kwalify[3] so I know the JSON itself is OK (I have found this validator is very picky). One thing that I do notice is that the JSON file in question here is by far the largest one I’ve tried so far at 1.4 megs. Do you think there may be a constraint on the size of the data?

[1]: http://www.sekaino.com/skedu/demodata/sxsw.json
[2]: http://skeduapp.com/files/skedu_schema_v1.yml
[3]: http://www.kuwata-lab.com/kwalify/

by Dav on Feb 13, 2010. #

Wow. It’s amazing how many times after banging your head against a problem for hours, as soon as you post a question to a forum, you find the answer yourself. In fact my validator was not as picky as the Obj-C JSON parser. Some tab characters in the data file were causing the problem, I converted those to spaces and the data loads fine now!

by Dav on Feb 13, 2010. #

thanks ,,,this post is good

by twister on Oct 12, 2010. #

Awesome!!. I really appreciate your effort!!

You are authentic guru guy!!

by Mr.black on Nov 3, 2010. #

Hi John – I got it going. My Flickr key was commented out.

Is there a way to automatically put in a tag in the box or call on a specific RSS stream.

I am trying to do an App for my Cousin, where anyone can just pull in the latest photos from the wedding.

Iain

by Iain Munro on Nov 6, 2010. #

Iain, I would look into the Flickt API and see if there is a means to insert a specific tag in the query…

by John Muchow on Nov 6, 2010. #