We use excellent Three20 framework in most of our iPhone projects. One of the great things provided by it is convenient framework for making HTTP requests to web services. There are plugins available to parse both JSON and XML responses. However for one of the projects I needed to parse JSONP response and I haven’t found any ready to use solution.

So I decided to make my own implementation of JSONP response parser, based on available TTURLJSONResponse class. Here it goes:

@implementation JSONPResponse

// This method is based on the same-named method in TTURLJSONResponse
// Note, that only SBJSON suport is left
- (NSError *) request: (TTURLRequest *) request processResponse: (NSHTTPURLResponse *) response
                 data: (id) data {

    // This response is designed for NSData objects, so if we get anything else it's probably a
    // mistake.
    TTDASSERT([data isKindOfClass: [NSData class]]);
    TTDASSERT(nil == _rootObject);
    NSError *err = nil;
    if ([data isKindOfClass: [NSData class]]) {
        NSString *json = [[[NSString alloc] initWithData: data encoding: NSUTF8StringEncoding] autorelease];

        // Remove JSONP wrapper

        json = [json stringByTrimmingCharactersInSet: [NSCharacterSet whitespaceAndNewlineCharacterSet]];
        NSRange openingBracket = [json rangeOfString: @"("];
        NSRange closingBracket = [json rangeOfString: @")" options: NSBackwardsSearch];

        if (openingBracket.location != NSNotFound && closingBracket.location != NSNotFound) {
            json = [json substringWithRange:
                    NSMakeRange(openingBracket.location + 1, closingBracket.location - openingBracket.location - 1)];

            // Parse JSON
            _rootObject = [[json JSONValue] retain];
        }

        // Report error if failed to parse
        if (!_rootObject) {
            err = [NSError errorWithDomain: kTTExtJSONErrorDomain
                                      code: kTTExtJSONErrorCodeInvalidJSON
                                  userInfo: nil];
        }
    }

    return err;
}

@end

Full source code is available as Gist: https://gist.github.com/1052330

As you can see, the implementation above is quite dumb – it just extracts JSON content string between parentheses and parses it in a same way as regular JSON response handler.

Response can be done as usual in Three20:

TTURLRequest *request = [TTURLRequest requestWithURL: "http://example.com"
                                            delegate: self];
request.response = [[JSONPResponse new] autorelease];
[request send];

Of course actual delegate methods should also be implemented:

- (void) requestDidFinishLoad: (TTURLRequest *) request {
    TTURLJSONResponse *response = request.response;

    TTDINFO(@"request succeeded: %@, data = %@", request.response, response.rootObject);
    
    // response.rootObject contains unwrapped JSON data
}

- (void) request: (TTURLRequest *) request didFailLoadWithError: (NSError *) error {
    TTDINFO(@"request failed: %@", error);
}

Good luck hacking on your iPhone apps!

Shameless plug

We develop mobile and web apps, you can hire us to work on your next project.