NSURLConnection data received after connectionDidFinishLoading - iphone

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 ;)

Related

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.

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

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);
}

iPhone authentication process

I have trouble finding info on this topic. Please help me out here.
I need to pass arguments via POST or GET method to my web server and get a reply.
Basically, if using GET method, I want to do something like server.com/?user=john&password=smith and receive the dynamically generated HTML code that is done with my php script. All this without using the web browser on my app.
How is it usually done?
You'll want to look into NSMutableURLRequest and NSURLConnection.
For example, you could use them like this do to a GET request to your server:
- (void)loginUser:(NSString *)username withPassword:(NSString *)password {
// GET
NSString *serverURL = [NSString stringWithFormat:#"http://yourserver.com/login.php?user=%#&pass=%#", username, password];
NSURL *url = [NSURL URLWithString:serverURL];
NSMutableURLRequest *req = [NSMutableURLRequest requestWithURL:url];
NSURLConnection *connection = [NSURLConnection connectionWithRequest:req delegate:self];
if (connection) {
connectionData = [[NSMutableData alloc] init];
}
}
This will send an asynchronous GET request to your server with the query string containing username and password.
If you want to send username and password using a POST request, the method would look something like this:
- (void)loginUser:(NSString *)username withPassword:(NSString *)password {
// POST
NSString *myRequestString = [NSString stringWithFormat:#"user=%#&pass=%#",username,password];
NSData *myRequestData = [NSData dataWithBytes: [myRequestString UTF8String] length: [myRequestString length]];
NSURL *url = [NSURL URLWithString:#"http://yourserver.com/login.php"];
NSMutableURLRequest *req = [NSMutableURLRequest requestWithURL:url];
[req setHTTPMethod: #"POST"];
[req setValue:#"application/x-www-form-urlencoded" forHTTPHeaderField:#"content-type"];
[req setHTTPBody: myRequestData];
NSURLConnection *connection = [NSURLConnection connectionWithRequest:req delegate:self];
if (connection) {
connectionData = [[NSMutableData alloc] init];
}
}
In order to get the response from the server, you will need to implement the NSURLConnection delegate methods, for example:
#pragma mark -
#pragma mark NSURLConnection delegate methods
#pragma mark -
- (void)connection:(NSURLConnection *)connection didReceiveAuthenticationChallenge:(NSURLAuthenticationChallenge *)challenge {
// Called if you have an .htaccess auth. on server
NSURLCredential *newCredential;
newCredential = [NSURLCredential credentialWithUser:#"your_username" password:#"your_password" persistence:NSURLCredentialPersistenceForSession];
[[challenge sender] useCredential:newCredential forAuthenticationChallenge:challenge];
}
-(void) connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)response {
[connectionData setLength: 0];
}
-(void) connection:(NSURLConnection *)connection didReceiveData:(NSData *)data {
[connectionData appendData:data];
}
-(void) connection:(NSURLConnection *)connection didFailWithError:(NSError *)error {
[connectionData release];
}
-(void) connectionDidFinishLoading:(NSURLConnection *)connection {
NSString *content = [[NSString alloc] initWithBytes:[connectionData bytes]
length:[connectionData length] encoding: NSUTF8StringEncoding];
// This will be your server's HTML response
NSLog(#"response: %#",content);
[content release];
[connectionData release];
}
References:
NSMutableURLRequest Class Reference
NSURLConnection Class Reference
Hope this helps :)
Usually this is done using a NSURLConnection. You can also use NSString's method stringWithContentsOfURL.

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

rest web services in iphone

I have a problem now. I need to pass an transactionID and an user password to a rest service and it is suppose to return me a true/false value (in XML format). However, it is consistently returning me (null).. I am totally lost some one please help.
NSString *urlString = [NSString stringWithFormat:#"https://10.124.128.93:8443/axis2/services/C3WebService/completeWithdrawal Transaction?transactionId=%#&password=%#", _transactionID.text, _userPassword.text];
NSURL *url = [[NSURL alloc] initWithString:urlString];
NSString *result = [[NSString alloc] initWithContentsOfURL:url];
NSLog(#"%#",result );
My result is constantly returning me null. How do i continue from here?
.h:
NSMutableData *responseData;
.m:
- (void)load {
NSURL *myURL = [NSURL URLWithString:#"https://10.124.128.93:8443/axis2/services/C3WebService/completeWithdrawal Transaction?transactionId=%#&password=%#", _transactionID.text, _userPassword.text];
NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:myURL
cachePolicy:NSURLRequestReloadIgnoringLocalCacheData
timeoutInterval:60];
[[NSURLConnection alloc] initWithRequest:request delegate:self];
}
- (void)connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)response {
responseData = [[NSMutableData alloc] init];
}
- (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data {
[responseData appendData:data];
}
- (void)connection:(NSURLConnection *)connection didFailWithError:(NSError *)error {
[responseData release];
[connection release];
[textView setString:#"Unable to fetch data"];
}
- (void)connectionDidFinishLoading:(NSURLConnection *)connection
{
NSLog(#"Succeeded! Received %d bytes of data",[responseData
`enter code here` length]);
NSString *txt = [[[NSString alloc] initWithData:responseData encoding: NSASCIIStringEncoding] autorelease];
}
Consider using NSURLConnection which has a callback for the result and also a callback to get detailed error details. It als doesn't execute on the UI thread (doesn't hang UI during the request).
NSURL *url = [NSURL URLWithString:#"http://www.mysite.com"];
NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:url cachePolicy:NSURLRequestUseProtocolCachePolicy timeoutInterval:60.0];
[request setHTTPMethod:#"GET"];
[[NSURLConnection alloc] initWithRequest:request delegate:self];
Then you can implement the delegate methods to get the error, the data and other details:
- (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data {
[responseData appendData:data];
}
- (void) connectionDidFinishLoading:(NSURLConnection *)connection {
[connection release];
NSString* responseString = [[NSString alloc] initWithData:responseData encoding:NSUTF8StringEncoding];
NSLog(#"result: %#", responseString);
[responseString release];
}
- (void) connection:(NSURLConnection *)connection didFailWithError:(NSError *)error {
NSLog(#"error - read error object for details");
}
You might try using "initWithContentsOfURL:encoding:error:" instead and check "error". Also, use Charles or other http sniffer and compare results to a straight browser request ( did you check results in a browser?)