After pounding my head all day long, I am down to StackOverflow to pull me through.
I am making a NSURLRequest in my iPhone App ...
NSURL* url = [[self serviceUrl] URLByAppendingPathComponent:[NSString stringWithFormat:#"Json"]];
NSString* json = [NSString stringWithFormat:#"{\"id\":\"%#\"}", id];
NSMutableURLRequest* urlRequest = [NSMutableURLRequest requestWithURL:url];
[urlRequest setHTTPMethod:#"POST"];
[urlRequest setValue:#"application/json" forHTTPHeaderField:#"Content-Type"];
[urlRequest setHTTPBody:[json dataUsingEncoding:NSUTF8StringEncoding]];
this is returning to me a JSON string, which comes back to me but it is broken in the since that the string will not parse correctly.
However if I make a normal request to the same url in the Safari browser then JSON is returned correctly. I am validating this JSON here.
So whats the deal? Is there a limit to the length of data in a NSString* that a 32Kb json file would not be stored in memory correctly? Sometimes the JSON can be parsed, which leads me to believe that I am not clearing my JSON string correctly after each request.
_json = [[NSString alloc] initWithData:_dataResponse encoding:NSUTF8StringEncoding];
NSArray* retrievedData = (NSArray*)[_json JSONValue];
// removed for brevity
_json = #"";
Other information, I am using ASP.NET MVC 3 to provide the web services for this app.
EDIT
- (NSURLRequest *)connection:(NSURLConnection *)connection willSendRequest:(NSURLRequest *)request redirectResponse:(NSURLResponse *)redirectResponse
{
_loader.hidden = NO;
[_loadingIndicator startAnimating];
return request;
}
- (void)connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)response
{
_dataResponse = [[NSMutableData alloc] init];
[_dataResponse setLength:0];
}
- (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data
{
[_dataResponse appendData:data];
}
- (void)connection:(NSURLConnection *)connection didFailWithError:(NSError *)error
{
NSLog(#"Error receiving response: %#", error);
_loader.hidden = YES;
[_loadingIndicator stopAnimating];
}
- (void)connectionDidFinishLoading:(NSURLConnection*)connection
{
_json = [[NSString alloc] initWithData:_dataResponse encoding:NSUTF8StringEncoding];
// Removed for brevity
_json = #"";
_loader.hidden = YES;
[_dataResponse release];
_dataResponse = nil;
[_loadingIndicator stopAnimating];
}
FINAL SOLUTION
I was making multiple calls to have data already stored for views in order to switch views in a tab bar controller. I wasn't checking the connection during the appending of the data, I was checking the connection when it was finished in order to store the data correctly. My final solution was to make each call sychronously after the previous one during the finished method call.
Unfortunately, nothing blatant is jumping out at me.
Some things to try that will hopefully help:
double check your NSUrlConnection pattern against this doc. The only diff I see is they're doing [[NSMutableData data] retain] instead of alloc, init. They also create NSMutableData with the connection request (not in response) and only length=0 in response. Not sure why it would matter though ...
http://developer.apple.com/library/mac/#documentation/Cocoa/Conceptual/URLLoadingSystem/Tasks/UsingNSURLConnection.html
try something like Charles Proxy. It allows you to sniff the wire. You can see what's different between browser and simulator/phone access. At a minimum, you'll see what's coming over the wire in the bad cases.
add lots of logging. everytime you append data, log some details. After you convert to string and before the json call, log. Log sizes, etc... Something may offer you a hint.
the use of _json string seems a bit off. You're reallocating and then setting to empty string. You should either have an iVar that you alloc and release (not set to "") or create a #property with retain, copy.
You should definitely include charset=utf8 in you content-type header. And how do you know that the response you get is encoded as utf8? You should also set content-size header.
Related
I would like to obtain the following: I have two NSOperations in a NSOperationQueue. The firs is a download from a website (gets some json data) the next is parsing that data. This are dependent operations.
I don't understand how to link them together. If they are both allocated and in the queue, how do I transfer the json string to the operation that parses it? Is it a problem if this queue is inside another NSOperationQueue that executes an NSOperation that consists of the two mentioned previously?
All I could find is transfers of data to a delegate on the main thread (performSelectorOnMainThread), but I need all this operations to execute in the background.
Thanks.
Code:
NSDownload : NSOperation
- (instancetype)initWithURLString:(NSString *)urlString andDelegate:(id<JSONDataDelegate>)delegate
{
self = [super init];
if (self) {
_urlStr = urlString;
_delegate = delegate; /// this needs to be a NSOPeration
_receivedData = [NSMutableData dataWithCapacity:256];
}
return self;
}
#pragma mark - OVERRIDE
- (void)main
{
#autoreleasepool {
if (self.isCancelled) {
return;
}
NSURL *url = [NSURL URLWithString:self.urlStr];
NSURLRequest *request = [NSURLRequest requestWithURL:url];
self.urlConnection = [[NSURLConnection alloc] initWithRequest:request delegate:self startImmediately:YES];
}
}
#pragma mark - NSURLConnectionDataDelegate
- (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data
{
if (self.isCancelled) {
[connection cancel];
self.receivedData = nil;
return;
}
[self.receivedData appendData:data];
}
- (void)connectionDidFinishLoading:(NSURLConnection *)connection
{
if (self.isCancelled) {
self.receivedData = nil;
return;
}
// return data to the delegate
NSDictionary *responseDict = #{JSON_REQUESTED_URL : self.urlStr,
JSON_RECEIVED_RESPONSE : self.receivedData};
[(NSObject *)self.delegate performSelectorOnMainThread:#selector(didReceiveJSONResponse:) withObject:responseDict waitUntilDone:NO]; // ok to uses performSelector as this data is not for use on the main thread ???
}
- (void)connection:(NSURLConnection *)connection didFailWithError:(NSError *)error
{
// return error to the delegate
[(NSObject *)self.delegate performSelectorOnMainThread:#selector(didFailToReceiveDataWithError:) withObject:error waitUntilDone:NO];
}
#user1028028:
Use the following approach.
1) Maintain the operation queue reference that you are using to add DownloadOperation.
2) In connectionDidFinishLoading method, create ParseOperation instance, set the json data and add it to operation queue. Maintain ParseOperation strong reference variable in DownloadOperation and handling of cancelling of parsing operation through DownloadOperation interface.
3) After completed parsing call the UI functionality in main thread.
I hope this helps.
As lucianomarisi notes, it would usually be best to just have the first operation generate the second operation. This is usually simpler to manage. Operation dependencies aren't really that common in my experience.
That said, it's of course possible to pass data between operations. For instance, you could create a datasource property on the second operation. That would be the object to ask for its data; that object would be the first operation. This approach may require locking, though.
You can also create a nextOp property on the first operation. When it completes, it would call setData: on the second operation before exiting. You probably wouldn't need locking for this, but you might. In most cases it would be better for the first operation to just schedule the nextOp at this point (which again looks like lucianomarisi's answer).
The point is that an operation is just an object. It can have any methods and properties you want on it. And you can pass one operation to another.
Keep in mind that since an operation runs in the background, there's no reason you need to use the asynchronous interface to NSURLConnection. The synchronous API (sendSynchronousRequest:returningResponse:error: is fine for this, and much simpler to code. You could even use a trivial NSBlockOperation. Alternately, you can use the asynchronous NSURLConnection interface, but then you really don't need an NSOperation.
I also notice:
_receivedData = [NSMutableData dataWithCapacity:256];
Is it really such a small piece of JSON data? It's hard to believe that this complexity is worth it to move such a small parsing operation to the background.
(As a side note, unless you know precisely the size of the memory, there's not usually much benefit to specifying a capacity manually. Even then it's not always clear that it's a benefit. I believe NSURLConnection is using dispatch data under the covers now, so you're actually requesting a memory block that will never be used. Of course Cocoa also won't allocate it because it optimizes that out... the point is that you might as well just use [NSMutableData data]. Cocoa is quite smart about these kinds of things; you generally can only get in the way of its optimizations.)
As Rob said, unless you have any particular reason to use operations use the synchronized call. Then perform the selector on MainThread or on any other thread you need. Unless you want to separate the retrieval and parsing in separate operations or thread (explicitly).
Here is the code I was using for json retrieval and parsing:
-(BOOL) loadWithURL:(NSString*) url params: (NSDictionary*) params andOutElements:(NSDictionary*) jElements
{
NSError *reqError = nil;
NSString* urlStr = #"";//#"http://";
urlStr = [urlStr stringByAppendingString:url];
NSURL* nsURL = [NSURL URLWithString:urlStr];
//Private API to bypass certificate ERROR Use only for DEBUG
//[NSURLRequest setAllowsAnyHTTPSCertificate:YES forHost:[nsURL host]];
NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL: nsURL
cachePolicy:NSURLRequestUseProtocolCachePolicy
timeoutInterval:60.0];
[request setHTTPMethod:#"POST"];
NSString *postString = #"";
if(params!=nil) {
NSEnumerator* enumerator = params.keyEnumerator;
NSString* aKey = nil;
while ( (aKey = [enumerator nextObject]) != nil) {
NSString* value = [params objectForKey:aKey];
//Use our own encoded implementation instead of above Apple one due to failing to encode '&'
NSString* escapedUrlString =[value stringByAddingPercentEscapesUsingEncoding:NSASCIIStringEncoding];
//Required to Fix Apple bug with not encoding the '&' to %26
escapedUrlString = [escapedUrlString stringByReplacingOccurrencesOfString: #"&" withString:#"%26"];
//this is custom append method. Please implement it for you -> the result should be 'key=value' or '&keyNotFirst=value'
postString = [self appendCGIPairs:postString key:aKey value:escapedUrlString isFirst:false];
}
}
//************** Use custom enconding instead !!!! Error !!!!! **************
[request setHTTPBody:[postString dataUsingEncoding:NSUTF8StringEncoding]];
NSData *response = [NSURLConnection sendSynchronousRequest:request returningResponse:nil error:&reqError];
if(reqError!=nil) {
NSLog(#"SP Error %#", reqError);
return NO;
}
NSString *json_string = [[NSString alloc] initWithData:response encoding:NSUTF8StringEncoding];
//Handles Server Errors during execution of the web service that handles the call.
if([json_string hasPrefix:#"ERROR"] == YES){
NSLog(#"SP Error %#", lastError);
return NO;
}
//Very Careful!!!!!! Will stop for any reason.!!!!!!
//Handles errors from IIS Server that serves teh request.
NSRange range = [json_string rangeOfString:#"Runtime Error"];
if(range.location != NSNotFound) {
NSLog(#"SP Error %#", lastError);
return NO;
}
//Do the parsing
jElements = [[parser objectWithString:json_string error:nil] copy];
if([parser error] == nil) {
NSLog(#"Parsing completed");
} else {
jElements = nil;
NSLog(#"Json Parser error: %#", parser.error);
NSLog(#"Json string: %#", json_string);
return NO;
}
//Parsed JSON will be on jElements
return YES;
}
The UIWebView does not automatically support processing of Passbook .pkpass files.
In this technical note, Apple recommend implementing a check via the UIWebViewDelegate methods to sniff out the MIME type and process it accordingly.
To add passes using a UIWebView, implement the appropriate
UIWebViewDelegate methods to identify when the view loads data with a
MIME type of application/vnd.apple.pkpass
However, I cannot find anything within the UIWebView Delegate Protocol Reference that is capable of providing the MIME type.
I can successfully download and process files directly using an NSURLConnection delegate with no problem, but what I wish to achieve is for passes to be properly processed if a user clicks on an Add To Passbook button while browsing within a UIWebView. Since I do not know the link, and many providers do not suffix their links with a .pkpass extension, following Apple's advice of examining the MIME type seems the best way to go.
I have tried adding the following
- (BOOL)webView:(UIWebView *)webView shouldStartLoadWithRequest:(NSURLRequest *)newRequest
navigationType:(UIWebViewNavigationType)navigationType
{
NSMutableURLRequest *req = [NSMutableURLRequest requestWithURL:[newRequest URL]];
// Spoof iOS Safari headers for sites that sniff the User Agent
[req addValue:#"Mozilla/5.0 (iPhone; CPU iPhone OS 6_1 like Mac OS X) AppleWebKit/536.26 (KHTML, like Gecko) Version/6.0 Mobile/10A5376e Safari/8536.25" forHTTPHeaderField:#"User-Agent"];
NSURLConnection *conn = [NSURLConnection connectionWithRequest:newRequest delegate:self];
return YES;
}
My NSURLConnection delegate:
- (void)connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)response
{
NSString *mime = [response MIMEType];
if ([mime isEqualToString:#"application/vnd.apple.pkpass"] && ![_data length]) {
_data = nil; // clear any old data
_data = [[NSMutableData alloc] init];
[_webPanel stopLoading];
}
}
-(void)connection:(NSURLConnection*)connection didReceiveData:(NSData*)data
{
[_data appendData:data];
NSLog(#"Size: %d", [_data length]);
}
-(void)connectionDidFinishLoading:(NSURLConnection*)connection
{
if ([_data length]) {
PKAddPassesViewController *pkvc = [PassKitAPI presentPKPassFileFromData:_data];
pkvc.delegate = self;
[self presentViewController:pkvc
animated:YES
completion:nil];
}
}
The NSURLConnection delegates work fine when a connection is invoked directly, without the UIWebView. However, when I try launching an NSURLConnection from the UIWebView delegate the pass download fails because the only 80% or so of the .pkpass is being downloaded (I get a random mismatch of bytes in the _data variable and the Content-Length header).
So, my questions:
Is there an easier way to get hold of a MIME type, directly from the UIWebView Delegate methods?
If not, then am I going about this the right way with opening up a parallel NSURLConnection, or is there a better way?
If an NSURLConnection is the way to go, then what could be causing it to stop short of downloading the full file?
Just use js
let contentType = webView.stringByEvaluatingJavaScript(from: "document.contentType;")
- (BOOL)webView:(UIWebView*)webView shouldStartLoadWithRequest:(NSURLRequest*)request navigationType:(UIWebViewNavigationType)navigationType {
NSURL *url = request.URL;
NSURLRequest *req = [NSURLRequest requestWithURL:url];
NSURLConnection *conn = [NSURLConnection connectionWithRequest:req delegate:self];
[conn start];
return YES;
}
- (void)connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)response{
NSString *mime = [response MIMEType];
NSLog(#"%#",mime);
}
You could try subclassing NSURLProtocol and handling the response information parsing there.
Look at
- (void)URLProtocol:(NSURLProtocol *)protocol didReceiveResponse:(NSURLResponse *)response cacheStoragePolicy:(NSURLCacheStoragePolicy)policy
Don't forget to about subresources also using these hooks.
Whenever I create an NSURLConnection in a class I have, it always connects to the first URL connected to by that class. It has an ivar conn that the NSURLConnection is stored in, and here is the method that connects:
-(void)getMoreProblems
{
problemsPage++;
NSURL *url=[NSURL URLWithString:[NSString stringWithFormat:#"http://projecteuler.net/problems;page=%d",problemsPage]];
NSURLRequest *req=[NSURLRequest requestWithURL:url];
NSLog(#"%p",conn);
conn=[[NSURLConnection alloc] initWithRequest:req delegate:self];
NSLog(#"%p",conn);
}
I have checked by NSLoging the URL's description and the Connection's pointer that they are different, as well as telling the UIApplication to load the URL in safari. As far as I can tell, It tries to load the right page. I also tried both POST and GET, but it didn't make a difference. What might be causing this?
EDIT FOR ANYONE LOOKING AT THIS WITH A SIMILAR PROBLEM:
My problem ended up being that I did not reinitialize the NSMutableData I stored the connection data in after each page loaded.
This isn't really an answer, but it's too long for a comment. I can't see anything wrong with the code that you posted. I pasted your code for getMoreProblems into a new project and added the delegate methods necessary to look at the results -- as far as I can tell it worked fine. I can see in the resulting string, the problem numbers starting with 1 on the first page I receive (from the first call to getMoreProblems) and starting with problem 51 on the second call to getMoreProblems. The only thing I added to your getMoreProblems method was the if-else clause at the end. HEre is the code I used:
#synthesize window = _window,receivedData;
- (void)applicationDidFinishLaunching:(NSNotification *)aNotification {
problemsPage = 0;
[self getMoreProblems];
}
-(void)getMoreProblems {
problemsPage++;
NSURL *url=[NSURL URLWithString:[NSString stringWithFormat:#"http://projecteuler.net/problems;page=%d",problemsPage]];
NSURLRequest *req=[NSURLRequest requestWithURL:url];
NSLog(#"%p",conn);
conn=[[NSURLConnection alloc] initWithRequest:req delegate:self];
NSLog(#"%p",conn);
if (conn) {
self.receivedData = [NSMutableData data];
} else {
NSLog(#"The Connection Failed");
}
}
- (void)connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)response {
NSLog(#"%#",response.URL);
[self.receivedData setLength:0];
}
- (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data {
NSLog(#"In connection:didReceiveData:");
[self.receivedData appendData:data];
}
- (void)connectionDidFinishLoading:(NSURLConnection *)connection {
NSLog(#"Succeeded! Received %lu bytes of data",[receivedData length]);
NSString *page = [[NSString alloc] initWithData:self.receivedData encoding:NSUTF8StringEncoding];
NSLog(#"%#",page);
[self performSelector:#selector(getMoreProblems) withObject:nil afterDelay:5];
}
So, I can't reproduce your problem -- I'm guessing it lies elsewhere in some code that you didn't post.
I have a login method. Inside the method I use NSURLConnection to login and I would like the return the NSData response. The problem is that I return the NSData before the connection actually gets the data.
- (NSData*)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data{
[responseData appendData:data]; //responseData is a global variable
NSLog(#"\nData is: %#", [[[NSString alloc] initWithData:responseData
encoding:NSUTF8StringEncoding]autorelease]);//this works
isLoaded = YES; //isLoaded is a BOOL
}
- (NSData*)login:(NSString*)username withPwd:(NSString*)password{
isLoaded = NO;
NSURLConnection *connection = [[NSURLConnection alloc] initWithRequest:request
delegate:self];
if(connection){
NSLog(#"Connected");
}
while(isLoaded = NO){
[NSThread NSSleepForTimeInterval: 1];
}
isLoaded = NO;
return responseData;
}
The program gets stuck at the while loop, but without the while loop the program can retrieve the data from the server, it is just that the method seems to return responseData, before the delegate method changes it.
So my question is how can I make it so the method will return the responseData only after the server is done with it ?
Unless otherwise specified, NSURLConnection loads a URL asynchronously. It uses deleegate callbacks to update the delegate about the progress of the URL download.
Specifically, Utilize NSURLConnection delegate's connectionDidFinishLoading: method. Your NSURLConnection object will call this once all the data has been loaded. It is within this method that you can return your data.
You can load the data synchronously but you may end up blocking UI.
Good luck!
You should refactor your code.
You are using a asynchronous call (good), but you try to handle it synchronously (not so good — if not using a separate thread).
to use an asynchronous behavior, you need a callback, in cocoa-fashion this is usually a delegate method (or could be a block for newer code). Actually it is your connection:didReceiveData. this method will work with the returned data — not the one, where you started the request. therefor usually methods, that start a asynchronous request, do not return anything — and for sure not, what is expected to be return form the request.
- (void)login:(NSString*)username withPwd:(NSString*)password
{
NSURLConnection *connection = [[NSURLConnection alloc] initWithRequest:request delegate:self];
}
//Note: you cannot change the delegate method signatures, as you did (your's returns an NSData object)
- (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data{
[self.responseData appendData:data]
}
- (void)connectionDidFinishLoading:(NSURLConnection *)connection
{
//Now that the connection was successfully terminated, do the real work.
}
look at this apple example code.
You can use the sync request method
- (NSData*)login:(NSString*)username withPwd:(NSString*)password
{
NSError *error = nil;
NSURLResponse *response = nil;
NSData *responseData = [NSURLConnection sendSynchronousRequest:request returningResponse:&response error:&error]
return responseDate;
}
Documentation:
https://developer.apple.com/library/mac/#documentation/Cocoa/Reference/Foundation/Classes/NSURLConnection_Class/Reference/Reference.html#//apple_ref/occ/clm/NSURLConnection/sendSynchronousRequest:returningResponse:error:
I'm writing an iPhone native app using the JSON framework.
My app is accessing web services using JSON. The JSON data we send has nested objects, below is an example of the data served up:
{
"model": {
"JSONRESPONSE": {
"authenticationFlag": true,
"sessionId": "3C4AA754D77BFBE33E0D66EBE306B8CA",
"statusMessage": "Successful Login.",
"locId": 1,
"userName": "Joe Schmoe"
}
}
}
I'm having problem parsing using the objectForKey and valueForKey NSDictionary methods. I keep getting invalidArgumentException runtime errors.
For instance, I want to query the response data for the "authenticationFlag" element.
Thanks,
Mike
Seattle
It is hard to tell without some more details (e.g. the JSON parsing code that you are using), but two things strike me as possible:
you are not querying with a full path. In the case above, you'd need to first get the enclosing model, the json response, and only then ask the json response dictionary for the authenticationFlag value:
[[[jsonDict objectForKey:#"model"]
objectForKey:#"JSONRESPONSE"] objectForKey:#"authenticationFlag"]
perhaps you're using c-strings ("") rather than NSStrings (#"") as keys (although this would likely crash nastily or just not compile). The key should be something than can be cast to id.
While possible, both are probably false, so please include more detail.
The following is taken directly from Dan Grigsby's Tutorial at - http://mobileorchard.com/tutorial-json-over-http-on-the-iphone/ - Please attribute, stealing is bad karma.
Fetching JSON Over HTTP
We’ll use Cocoa’s NSURLConnection to issue an HTTP request and retrieve the JSON data.
Cocoa provides both synchronous and asynchronous options for making HTTP requests. Synchronous requests run from the application’s main runloop cause the app to halt while it waits for a response. Asynchronous requests use callbacks to avoid blocking and are straightforward to use. We’ll use asynchronous requests.
First thing we need to do is update our view controller’s interface to include an NSMutableData to hold the response data. We declare this in the interface (and not inside a method) because the response comes back serially in pieces that we stitch together rather than in a complete unit.
#import <UIKit/UIKit.h>
#interface ViewController : UIViewController {
IBOutlet UILabel *label;
NSMutableData *responseData;
}
To keep things simple, we’ll kick off the HTTP request from viewDidLoad.
Replace the contents of :
#import "JSON/JSON.h"
#implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
responseData = [[NSMutableData data] retain];
NSURLRequest *request = [NSURLRequest requestWithURL:[NSURL URLWithString:#"XYZ.json"]];
[[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 {
label.text = [NSString stringWithFormat:#"Connection failed: %#", [error description]];
}
- (void)connectionDidFinishLoading:(NSURLConnection *)connection {
[connection release];
}
- (void)dealloc {
[super dealloc];
}
#end
This mostly boilerplate code initializes the responseData variable to be ready to hold the data and kicks off the connection in viewDidload; it gathers the pieces as they come in in didReceiveData; and the empty connectionDidFinishLoading stands ready to do something with the results.
Using The JSON Data
Next, we’ll flesh out the connectionDidFinishLoading method to make use of the JSON data retrieved in the last step.
Update the connectionDidFinishLoading method :
- (void)connectionDidFinishLoading:(NSURLConnection *)connection {
[connection release];
NSString *responseString = [[NSString alloc] initWithData:responseData encoding:NSUTF8StringEncoding];
[responseData release];
NSArray *luckyNumbers = [responseString JSONValue];
NSMutableString *text = [NSMutableString stringWithString:#"Lucky numbers:\n"];
for (int i = 0; i < [luckyNumbers count]; i++)
[text appendFormat:#"%#\n", [luckyNumbers objectAtIndex:i]];
label.text = text;
}
It creates an NSArray. The parser is very flexible and returns objects — including nested objects — that appropriately match JSON datatypes to Objective-C datatypes.
Better Error Handling
Thus far, we’ve been using the the convenient, high-level extensions to NSString method of parsing JSON. We’ve done so with good reason: it’s handy to simple send the JSONValue message to a string to accessed to the parsed JSON values.
Unfortunately, using this method makes helpful error handling difficult. If the JSON parser fails for any reason it simply returns a nil value. However, if you watch your console log when this happens, you’ll see messages describing precisely what caused the parser to fail.
It’d be nice to be able to pass those error details along to the user. To do so, we’ll switch to the second, object-oriented method, that the JSON SDK supports.
Update the connectionDidFinishLoading method in :
- (void)connectionDidFinishLoading:(NSURLConnection *)connection {
[connection release];
NSString *responseString = [[NSString alloc] initWithData:responseData encoding:NSUTF8StringEncoding];
[responseData release];
NSError *error;
SBJSON *json = [[SBJSON new] autorelease];
NSArray *luckyNumbers = [json objectWithString:responseString error:&error];
[responseString release];
if (luckyNumbers == nil)
label.text = [NSString stringWithFormat:#"JSON parsing failed: %#", [error localizedDescription]];
else {
NSMutableString *text = [NSMutableString stringWithString:#"Lucky numbers:\n"];
for (int i = 0; i < [luckyNumbers count]; i++)
[text appendFormat:#"%#\n", [viewcontroller objectAtIndex:i]];
label.text = text;
}
}
Using this method gives us a pointer to the error object of the underlying JSON parser that we can use for more useful error handling.
Conclusion :
The JSON SDK and Cocoa's built-in support for HTTP make adding JSON web services to iPhone apps straightforward.
NSString* aStr;
aStr = [[NSString alloc] initWithData:data encoding:NSASCIIStringEncoding];
NSDictionary *dictionary = [aStr JSONValue];
NSArray *keys = [dictionary allKeys];
// values in foreach loop
for (NSString *key in keys) {
NSArray *items = (NSArray *) [dictionary objectForKey:key];
for (id *item in items) {
NSString* aStrs= item;
NSLog(#" test %#", aStrs);
NSDictionary *dict = aStrs;
NSArray *k = [dict allKeys];
for (id *it in k) {
NSLog(#"the child item: %#", [NSString stringWithFormat:#"Child Item -> %# value %#", (NSDictionary *) it,[dict objectForKey:it]]);
}
Now, Objective c has introduced in build class for JSON Parsing.
NSError *myError = nil;
NSDictionary *resultDictionary = [NSJSONSerialization JSONObjectWithData:self.responseData options:NSJSONReadingMutableLeaves error:&myError];
http://developer.apple.com/library/ios/#documentation/Foundation/Reference/NSJSONSerialization_Class/Reference/Reference.html
So utilize this class and make your application error free...:)