How to pass a json object as a parameter to the webservice? - iphone

I am using a webservice which takes json object as parameter. Here's my code:
-(void)createHttpHeaderRequest {
NSString *x = #"{\"GetVehicleInventory\": {\"ApplicationArea\": {\"Sender\": {\"ComponentID\":}}}" (something like that)
NSString *sample = [NSString stringWithFormat:#"https://trialservice.checkitout?XML_INPUT=%#",x];
NSString * final = (NSString *) CFURLCreateStringByAddingPercentEscapes(kCFAllocatorDefault, (CFStringRef)sampleReq, NULL, CFSTR(":/?#[]#!$&'()*+,;=\""), kCFStringEncodingUTF8);
NSMutableRequest *request = [NSMutableREquest requestWithURL:[NSURL URLWithString:final]];
NSURLConnection * theConnection = [[NSURLConnection alloc]initWithRequest:theRequest delegate:self];
if (theConnection) {
NSLog(#"Service hit");
}
}
- (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data {
NSError * error;
NSDictionary * dict = [NSJSONSerialization JSONObjectWithData:data options:NSJSONReadingMutableContainers error:&error];
NSArray * categories = [dict objectForKey:#"category"];
[self.delegate giveControlBackToController:categories];
}
When I am trying to NSLog sample it gives me the complete URL which on pasting into a browser gives me the result back, but when I am calling NSLog on the request, it shows null and nothing happens after this.
The control never goes to its NSURLConnection delegate method.

The trick is that most browsers automatically escape URLs whilst NSURL doesn't. You'll need to do it manually; have a look at the CFURLCreateStringByAddingPercentEscapes function.

This should probably be a comment instead of an answer, but unfortunately you can't format code in a comment. Add the following method to your delegate and see whether it gets called. If it does, let us know what the response is.
- (void)connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)response {
NSHTTPURLResponse *httpResponse = (NSHTTPURLResponse *)response;
NSLog(#"Connection response code: %d", httpResponse.statusCode);
}

Related

Objective-C JSON Parse don't work in other classes

I have a strange problem with the NSURLConnectionDataDelegate.
I have created a class for parsing of json.
Here the code:
- (void)createJSONDictionaryFromURL:(NSURL *)url
{
NSURLRequest *request = [NSURLRequest requestWithURL:url];
(void)[[NSURLConnection alloc] initWithRequest:request delegate:self];
}
- (void)connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)response
{
jsonData = [[NSMutableData alloc] init];
}
- (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data
{
[jsonData appendData:data];
}
- (void)connection:(NSURLConnection *)connection didFailWithError:(NSError *)error
{
NSLog(#"Fail with error");
}
- (void)connectionDidFinishLoading:(NSURLConnection *)connection
{
self.jsonDictionary = [NSJSONSerialization JSONObjectWithData:jsonData options:0 error:nil];
NSLog(#"%#", self.jsonDictionary);
}
In the Method connectionDidFinishLoading, is everything correct, the console shows the correct data.
But I want to use this in the viewDidLoad Method from my ViewController.
JSONParser *parser = [[JSONParser alloc] init];
NSDictionary *dict = [[NSDictionary alloc] init];
[parser createJSONDictionaryFromURL:url];
dict = parser.jsonDictionary;
NSLog(#"%#", dict);
The console shows (null), can anybody help me?
Sorry for my bad english
Your JSONParser is asynchronous because of the network connection (it is asynchronous). So, when you currently call parser.jsonDictionary you see that the data isn't ready yet.
You don't want to try blocking until the data is available, rather you should setup a callback to get the data to your view controller when it's ready (so you can save it and update the UI).
That could be done by adding a delegate property to your JSONParser and having the view controller assign itself and implement some callback method. Or, using less code, the JSONParser can offer a block property that the view controller sets and which is called in connectionDidFinishLoading: (make sure the #property uses (copy, nonatomic)`).

NSURLConnection data received after connectionDidFinishLoading

I have a question about NSURLConnection:
I want download an image with:
NSURLConnection *theConnection=[[NSURLConnection alloc] initWithRequest:urlRequest delegate:self];
the first delegate called (correctly) is
- (void)connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)response
after this is called one time:
- (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data
and immediately:
- (void)connectionDidFinishLoading:(NSURLConnection *)connection
up to here everything is correct, but after connectionDidFinishLoading is fired once again the didReceiveData delegate for the same connection, I identify connection by:
NSString * connectionKey = [[[connection originalRequest] URL] absoluteString];
Is this possible?
update, more infos:
My app fire many simultaneous connections and I store infos for any connection in a dictionary, when a delegate was called I retrive connection info using the key: NSString * connectionKey = [[[connection originalRequest] URL] absoluteString]; for 99% of connections everything is all right but for one (avery time the same!) connection I have this behavior
here the complete implementation:
- (void)downloadFileFromUrl:(NSURL *)url
inPath:(NSString *)completeFilePath
dataReceivedBlock:(void (^)(long long byteDownloaded ,long long totalByte))dataReceivedBlock
endBlock:(void (^)(NSString * downloadPath, NSDictionary * responseHeaders))endBlock
failBlock:(void (^)(NSString * downloadPath, NSDictionary * responseHeaders, NSError * error))failBlock
{
//save the connection infos
NSMutableDictionary * requestData = [[NSMutableDictionary alloc] init];
if(dataReceivedBlock)
[requestData setObject:[dataReceivedBlock copy] forKey:#"dataReceivedBlock"];
if(endBlock)
[requestData setObject:[endBlock copy] forKey:#"endBlock"];
if(failBlock)
[requestData setObject:[failBlock copy] forKey:#"failBlock"];
[requestData setObject:[NSNumber numberWithBool:YES] forKey:#"usingBlock"];
[requestData setObject: completeFilePath forKey:#"downloadDestinationPath"];
//delete the file if already on fs
if([[NSFileManager defaultManager] fileExistsAtPath: completeFilePath])
[[NSFileManager defaultManager] removeItemAtPath:completeFilePath error:nil];
// Create the request.
NSURLRequest * urlRequest = [NSURLRequest requestWithURL:url cachePolicy:NSURLCacheStorageAllowed timeoutInterval:TIME_OUT];
NSURLConnection *theConnection=[[NSURLConnection alloc] initWithRequest:urlRequest delegate:self];
if (!theConnection)
{
// Inform the user that the connection failed.
failBlock(completeFilePath,nil,[NSError errorWithDomain:#"Connection fail" code:0 userInfo:nil]);
}
//add connection infos to the requests dictionary
NSString * connectionKey = [[[theConnection originalRequest] URL] absoluteString];
[self.requests setObject:requestData forKey:connectionKey];
}
here a delegate example:
- (void)connectionDidFinishLoading:(NSURLConnection *)connection
{
NSString * connectionKey = [[[connection originalRequest] URL] absoluteString];
NSMutableDictionary * requestData = [self.requests objectForKey:connectionKey];
NSFileHandle *file = [requestData objectForKey:#"fileHandle"];
[file closeFile];
//se la richiesta usa o no block
BOOL usingBlock = [[requestData objectForKey:#"usingBlock"] boolValue];
if(usingBlock)
{
__block void (^endBlock)(NSString * , NSDictionary *) = [requestData objectForKey:#"endBlock"];
if(endBlock)
endBlock([requestData objectForKey:#"downloadDestinationPath"],[requestData objectForKey:#"responseHeaders"]);
}
else
[self.delegate downloadEnded:[requestData objectForKey:#"responseHeaders"]];
//elimino dati richiesta
[self.requests removeObjectForKey:[NSString stringWithFormat:#"%#",connectionKey]];
//Inibisco standby
[UIApplication sharedApplication].idleTimerDisabled = NO;
}
It's impossible. It's must be different connection callback.
ensure each connection use unique delegate, or unique switch branch in the same delegate.
Obviously the solution is really simple -_-
The error is in the connection key identifier:
NSString * connectionKey = [[[connection originalRequest] URL] absoluteString];
my assumption is that key are unique, but if I make x parallel downloadS for the same file at the same url the assumption is wrong ;)

Asynchronous NSURLConnection request responds multiple times

I am facing weired issue. I am sending Asynchronus NSUrlrequest call but in return i am getting multiple time responde with some part of json
can someone please help me with what I did wrong.
code
NSString *_query = #"http://abc.com/index.php";
NSData *myRequestData = [NSData dataWithBytes:[_requestString UTF8String]
length:[_requestString length]];
__block NSMutableURLRequest *request = [[NSMutableURLRequest alloc] initWithURL:[NSURL URLWithString:_query]];
[request setHTTPMethod: #"POST" ];
[request setValue:#"application/x-www-form-urlencoded" forHTTPHeaderField:#"content-type"];
[request setHTTPBody: myRequestData ];
[[NSURLConnection alloc] initWithRequest:request delegate:self];
NSRunLoop *runLoop = [NSRunLoop currentRunLoop];
[runLoop addTimer:timeOutTimer forMode:NSDefaultRunLoopMode];
Response
- (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data
{
// check is response is a valid JSON?
NSError *error;
id jsonObj = [NSJSONSerialization JSONObjectWithData: data options:kNilOptions error:&error];
BOOL isValid = [NSJSONSerialization isValidJSONObject:jsonObj];
NSString *content = [[NSString alloc] initWithData:data
encoding:NSUTF8StringEncoding];
NSLog(#"Content: %#",content);
if (isValid)
{
NSDictionary *data = [content JSONValue];
}
[content release];
}
As data is received by the client, this callback gets called:
- (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data
didReceiveData is giving you data as it's receiving it and can be called multiple times with chunks of the data.
From the NSURLConnection docs:
The delegate is periodically sent connection:didReceiveData: messages
as the data is received. The delegate implementation is responsible
for storing the newly received data.
From those docs:
- (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data
{
// Append the new data to receivedData.
// receivedData is an instance variable declared elsewhere.
[receivedData appendData:data];
}
When its all done, connectionDidFinishLoading will get called and your appended data is ready for you to use.
Finally, if the connection succeeds in downloading the request, the
delegate receives the connectionDidFinishLoading: message. The
delegate will receive no further messages for the connection and the
NSURLConnection object can be released.

parsing json response in my objective-c program

im using SBJSON to parse my response, bus somehow i cant get my data out.
how should i parse this reponed ?
{"StatusCode":0,"Message":"email already exists","Content":{"HasApplication":false,"IsFaceBook":false,"UserActive":false,"UserAuthenticationType":0,"UserCredits":0,"UserDayAdded":0,"UserEmail":null,"UserEmailWasVerified":false,"UserGuid":null,"UserID":0,"UserSellerApproved":false,"UserTokef":0},"TotalCount":0}
i start like this :
- (void)connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)response
{
[responseData setLength:0];
NSLog(#"%#",response);
}
- (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data
{
[responseData appendData:data];
NSLog(#"Data recived");
}
- (void)connection:(NSURLConnection *)connection didFailWithError:(NSError *)error
{
NSLog(#"Connection failed! Error - %# %#",[error localizedDescription],
[[error userInfo] objectForKey:NSURLErrorFailingURLStringErrorKey]);
responseData = nil;
}
- (void)connectionDidFinishLoading:(NSURLConnection *)connection
{
NSLog(#"conenction loading finished");
NSString *responseString = [[NSString alloc] initWithData:responseData encoding:NSUTF8StringEncoding];
SBJsonParser *parser = [[SBJsonParser alloc] init];
NSMutableDictionary *jsonDictionary = [parser objectWithString:responseString error:nil];
}
but whats next ?i need the StatusCode value and the UserID.
One you have the dictionary, you can use it, for example:
NSMutableDictionary *jsonDictionary = [parser objectWithString:responseString error:nil];
NSNumber * statusCode = [jsonDictionary objectForKey:#"StatusCode"];
NSString * message = [jsonDictionary objectForKey:#"Message"];
NSDictionary * content = [jsonDictionary objectForKey:#"Content"];
// etc...
From the looks of the JSON string, you have a Dictionary with three Key Value pairs, one of those being another Dictionary with several Key Value pairs. So to deal with that once you have assigned the JSON responseString to your NSMutableDictionary:
Should be something like:
NSNumber *statusCode = [jsonDictionary objectForKey:#"StatusCode"];
NSDictionary *contentDict = [jsonDictionary objectForKey#"Content"];
NSString *userID = [contentDict valueForKey#"UserID"];
On a completely different note, if you are going to be doing a lot of web-service interaction, you may want to take a serious look at AFNetworking. It will change your life :)
Also, I can't see why you would need jsonDictionary to be an NSMutableDictionary. Unless you are changing it later on, use NSDictionary, it has less overhead.
well.....
NSMutableDictionary *jsonDictionary = [parser objectWithString:responseString
Maybe it's not NSMutableDictionary, but NSDictionary.
and then:
NSString *statusCode = [jsonDictionary valueForKey:#"StatusCode"];
NSDictionary *contentDict = [jsonDictionary objectForKey#"Content"];
NSString *userID = [contentDict valueForKey#"UserID"];

NSURLConnection not responding

I'm trying to get some data from an URL, but for some reason, nothing happens when I do the following. Neither didReceiveResponse:, didReceiveData:, didFailWithError: or connectionDidFinishLoading: are reached, except when I add a timeout to my request by doing this: [request setTimeoutInterval:10.0]
Here's what I'm doing :
-(void)getConfigFromServer{
[self getContentAtURL:kUrlGetUser];
}
//Va chercher le contenu à l'URL passée en paramètre
- (void)getContentAtURL: (NSURL *)url {
[UIApplication sharedApplication].networkActivityIndicatorVisible = YES;
NSUserDefaults *userDefaults = [NSUserDefaults standardUserDefaults];
NSString * userLogin = [userDefaults objectForKey:#"UserLogin"];
NSString * userPassword = [userDefaults objectForKey:#"UserPassword"];
NSURL * urlFinal = [NSURL URLWithString:[NSString stringWithFormat:#"%#", url]];
NSLog(#"Request : %#", [NSString stringWithFormat:#"%#", url]);
NSMutableURLRequest *request = [[NSMutableURLRequest alloc] init];
[request setURL:urlFinal];
[request setHTTPShouldHandleCookies:NO];
[request setTimeoutInterval:10.0];
NSString *sourceString = [NSString stringWithFormat:#"%#:%#", userLogin, userPassword];
NSData * sourceData = [sourceString dataUsingEncoding:NSUTF8StringEncoding];
NSString *authString = [sourceData base64EncodedString];
authString = [NSString stringWithFormat: #"Basic %#", authString];
[request setValue:authString forHTTPHeaderField:#"Authorization"];
[request setValue:#"text/xml" forHTTPHeaderField:#"Accept"];
NSURLConnection * connection=[NSURLConnection connectionWithRequest:request delegate:self];
if(connection){
NSLog(#"Connection started");
receivedData = [NSMutableData data];
}else{
NSLog(#"Error while trying to initiate the connection");
}
}
- (void)connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)response {
[receivedData setLength:0];
if ([response respondsToSelector:#selector(statusCode)])
{
int statusCode = [((NSHTTPURLResponse *)response) statusCode];
if (statusCode >= 400)
{
[connection cancel]; // stop connecting; no more delegate messages
NSDictionary *errorInfo
= [NSDictionary dictionaryWithObject:[NSString stringWithFormat:
NSLocalizedString(#"Server returned status code %d",#""),
statusCode]
forKey:NSLocalizedDescriptionKey];
NSError *statusError = [NSError errorWithDomain:#"Error"
code:statusCode
userInfo:errorInfo];
[self connection:connection didFailWithError:statusError];
}
}
}
- (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data{
[receivedData appendData:data];
}
- (void)connection:(NSURLConnection *)connection didFailWithError:(NSError *)error{
[UIApplication sharedApplication].networkActivityIndicatorVisible = NO;
NSLog(#"Connection failed! Error - %# %#",[error localizedDescription],[[error userInfo] objectForKey:NSURLErrorFailingURLStringErrorKey]);
}
-(void)connectionDidFinishLoading:(NSURLConnection *)connection{
[UIApplication sharedApplication].networkActivityIndicatorVisible = NO;
[self fetchedData:receivedData];
}
EDIT : I'm still having this problem, and now have it on the actual device too. As I said it in the comments, I use ARC on this app for the first time, and I'm using XCode 4.2.
Any idea?
This is old thread but still this answer might help someone out there.
If you are calling this in background thread, check whether you thread is exiting before delegates is called.
Try doing this.
NSURLConnection * connection = [[NSURLConnection alloc]
initWithRequest:request
delegate:self startImmediately:NO];
[connection scheduleInRunLoop:[NSRunLoop mainRunLoop]
forMode:NSDefaultRunLoopMode];
[connection start];
Error message, as you have told in comments, is saying that the request has timed out.
I am sure that if you remove the line with timeout, you will get either the same response or actual data, after 60 seconds. Make sure you wait enough, because if your connection is very weak for some reason, the request may not time out after 60 seconds because it keeps downloading data.
Is your app in foreground while you launch this? Make sure you don't suspend it.
Furthermore, you say that the server is online, but your code is timing out. Means something is probably wrong with your connection after all.
In another comment you say that sometimes it works. Even more points to the fact that the connection is weak/unreliable or breaking up.
You're passing an NSURL to your getContentAtURL but then treating it as though it was a string in :
NSURL * urlFinal = [NSURL URLWithString:[NSString stringWithFormat:#"%#", url]];
try changing it to:
NSURL * urlFinal = [NSURL URLWithString:[NSString stringWithFormat:#"%#", [url absoluteString]]];
btw, you know you're not using your login and password don't you?
EDIT:
When you say receivedData = [NSMutableData data]; what is data?
I think that might be the problem.
I tend to use the following to setup the data object when data first arrives:
- (void)connection:(NSURLConnection *)theConnection didReceiveData:(NSData *)incrementalData {
if (receivedData ==nil) { receivedData = [[NSMutableData alloc] initWithCapacity:2048]; }
[receivedData appendData:incrementalData];
}
Some thoughts here:
Please do not store passwords in UserDefaults, use the keychain for that (maybe you are only testing, I just wanted to have this mentioned)
Why do you convert your NSURL url to a new NSURL by calling stringWithFormat: on the url? (Oh, I see #ade already mentioned that)
Is this an ARC-enabled App? If not, your NSMutableData will be autoreleased too soon, use self.receivedData = [NSMutableData data] and you will see a crash.
Try making your NSURLConnection an instance variable and hold on to it. I think it should work the way you do it right now, but I tend to hold on to it and never had the problem you mention here.
Weird that your 10 seconds timeout seems to work, I had trouble getting this to work and found that on iPhone, the timeout can not be lower than 240 seconds. Yes, I was confused about this as well, but: https://stackoverflow.com/a/3611383/148335
I think the problem will be with server side. Try to increase TimeoutInterval to 60 so there will be more time to fetch data from server. Didn't you get "connection timed out" message after 10 seconds?
EDIT
Hi, I think still you are not reached a solution. When I read the code in detail, I found some statements are confusing.
1) [request setValue:#"text/xml" forHTTPHeaderField:#"Accept"]; replace with
[request setValue:#"text/plain" forHTTPHeaderField:#"Accept"];
Also include,
[request setValue:#"text/plain" forHTTPHeaderField:#"Content-Type"];
And finally...
The error "connection timed out" indicates that the iPhone does not receiving any data from server. As soon as iPhone gets a bye of data it will call - (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data. Try to put NSLog after each statements to verify variables hold desired value. Try to check the length of data --sourceData-- before sending ...