NSURLConnection delegates not being called even when run on main thread - objective-c

I know that this kind of question has been asked many times, but all of them point to saying that the connection must be on a different thread.
-(void)distanceMatrix{
NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:[NSURL URLWithString:distanceMatrixURL]
cachePolicy:NSURLRequestUseProtocolCachePolicy
timeoutInterval:10];
connection2 = [[NSURLConnection alloc] initWithRequest:request delegate:self startImmediately:NO];
[connection2 scheduleInRunLoop:[NSRunLoop mainRunLoop]
forMode:NSDefaultRunLoopMode];
NSLog(#"Is%# main thread", ([NSThread isMainThread] ? #"" : #" NOT"));
[connection2 start];
if (connection2)
{
responseData2 = [NSMutableData data];
connectionIsActive = YES;
} else {
NSLog(#"connection failed");
}
}
- (void)connection2:(NSURLConnection *)connection2 didReceiveResponse:(NSURLResponse *)response
{NSLog(#"recieved response");
[responseData2 setLength:0];
}
- (void)connection2:(NSURLConnection *)connection2 didReceiveData:(NSData *)data
{
[responseData2 appendData:data];
}
- (void)connection2:(NSURLConnection *)connection2 didFailWithError:(NSError *)error
{
connectionIsActive = NO;
NSLog(#"failed!!");
}
- (void)connection2DidFinishLoading:(NSURLConnection *)conn
{
connectionIsActive = NO;
SBJsonParser *json = [[SBJsonParser alloc] init];
NSString *responseString = [[NSString alloc] initWithData:responseData2 encoding:NSUTF8StringEncoding];
NSError *jsonError = nil;
NSDictionary *parsedJSON = [json objectWithString:responseString error:&jsonError];
travelTime= [[[[parsedJSON valueForKey:#"rows"] valueForKey:#"elements"] valueForKey:#"duration"] valueForKey:#"text"];
NSLog(#"traveltime = %#", travelTime);
}
When I log it, it says that it runs on the main thread. Connection2 is active but none of the delegates are called.
Also, this is the way I am calling distanceMatrix method
-(id)initWithJsonResultDict:(NSDictionary *)jsonResultDict andUserCoordinates: (CLLocationCoordinate2D)userCoords andTimeURL:(NSString*)timeURL
{
self.distanceMatrixURL = timeURL;
[self distanceMatrix];
//more code here for other purposes
}

Because you have added a 2 into the names of all of the delegate methods. That changes the method signature so you aren't implementing the correct methods. Remove all of the 2 at the start of the methods - (void)connection2: and it should work.

Related

Retrieving images once saved to a server

I have code written that uploads an image to the server, but I am not sure how to retrieve the images after I upload them. I tried using an NSURL request, but the did receiveData delegate method is never called. Below I've included all relevant code related to uploading the picture, and then my attempt at pulling the data using an NSURL request. Is there anything conceptually that I'm doing wrong? Thank you.
- (IBAction)nextButtonPressed:(id)sender {
[self.signupController uploadProfilePicture:UIImagePNGRepresentation(self.imageView.image) completion:^(NSError *error){
[[NSOperationQueue mainQueue] addOperationWithBlock:^{
[[SyncController sharedInstance] sync];
[self performSegueWithIdentifier:#"addFriendsSegue" sender:self];
}];
}];
}
And the uploadProfilePicture method:
- (void)uploadProfilePicture:(NSData *)imageData completion:(void (^)(NSError *error))completion {
BRUser *user = [BRSession userWithContext:[[BRCoreDataManager sharedManager] mainContext]];
[self.apiClient uploadProfilePicture:imageData forUser:user parameters:nil completion:[self uploadProfilePictureHandlerWithCompletion:completion]];
}
And then, also, here is the upload profile picture method in the API client:
- (void)uploadProfilePicture:(NSData *)imageData forUser:(BRUser *)user parameters:(NSDictionary *)parameters completion:(BRAPIClientCompletionBlock)completion {
NSError *error;
NSURLRequest *request = [self.requestSerializer multiformRequestForAPIAction:BRAPIActionCreate nestedResource:#"image" parent:user data:imageData parameters:parameters error:&error];
if (error) {
completion(nil, error);
}
NSURLSessionDataTaskCompletionBlock dataTaskCompletion = [self requestHandlerWithHTTPStatusErrors:#{ #400 : #(BRAPIUnauthorized) } completion:completion];
NSURLSessionDataTask *task = [self.authURLSession dataTaskWithRequest:request completionHandler:dataTaskCompletion];
[task resume];
}
and the multiform method called in the previous method:
- (NSURLRequest *)multiformRequestForAPIAction:(BRAPIAction)action nestedResource:(id)resource parent:(id)parent data:(NSData *)data parameters:(NSDictionary *)parameters error:(NSError *__autoreleasing *)error {
NSParameterAssert(action);
NSParameterAssert(resource);
NSParameterAssert(parent);
NSParameterAssert(data);
NSString *method = [BRAPIRequestSerializer HTTPMethodForAPIAction:action];
NSURL *url = [self.apiURL nestedURLForResource:resource parent:parent];
NSLog(#"URL: %#",[url absoluteStringWithTrailingSlash]);
return [self.serializer multipartFormRequestWithMethod:method
URLString:[url absoluteStringWithTrailingSlash]
parameters:parameters
constructingBodyWithBlock:^(id<AFMultipartFormData> formData) {
[formData appendPartWithFileData:data name:#"image" fileName:#"image.png" mimeType:#"image/png"];
}
error:error];
}
And my failed attempt at retrieving the data via the url it's stored at:
-(void) downloadImageFromURL :(NSString *)imageUrlString{
// Create the request.
NSURLRequest *theRequest=[NSURLRequest requestWithURL:[NSURL URLWithString:imageUrlString]
cachePolicy:NSURLRequestUseProtocolCachePolicy
timeoutInterval:60.0];
// Create the NSMutableData to hold the received data.
// receivedData is an instance variable declared elsewhere.
NSData * receivedData = [[NSMutableData alloc] init];
// create the connection with the request
// and start loading the data
NSURLConnection *theConnection=[[NSURLConnection alloc] initWithRequest:theRequest delegate:self];
if (!theConnection) {
// Release the receivedData object.
receivedData = nil;
// Inform the user that the connection failed.
NSLog(#"connection falied");
} else {
NSLog(#"connection succesful");
};
}
- (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data
{
NSLog(#"data: %#",data);
}
I also made sure to include the NSURLConnectionDelegate. The didRecieveData method is never called.
Feel free to let me know if there's more code you need to see!

NSURLConnection Delegates Issue

I have a problem with NSURLConnection delegate. Simply I need to get a result from the server based on that result, I will do some stuff.
The problem is :
-(void)connectionDidFinishLoading:(NSURLConnection *)connection
Method is calling at last and I am receiving answer in this part. Then I cannot use the result that came from the server.
-(void)connectionDidFinishLoading:(NSURLConnection *)connection
{
NSLog(#"DidfinishLaunch started");
self.json = [NSJSONSerialization JSONObjectWithData:self.responseData options:NSJSONReadingMutableLeaves error:nil];
if (!self.parsedJsonContent) {
self.parsedJsonContent = [[NSMutableArray alloc]init];
}
for (int i=0; i<self.json.count; i++) {
NSString *result = [self.json objectAtIndex:i];
self.user.result = result;}
}
}
and when I want to use the value of result in MainViewController:
NSLog(#"result: %#",[self.jsonProcess GetJsonResultForLogin]);
it comes empty. So how am I going to grab the value of result, couse the value of result is filling in connectionDidFinishLoading which is the last method that called.
Maybe you need to grab the data received in:
-(void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data
and just use the
-(void)connectionDidFinishLoading:(NSURLConnection *)connection
to know when the connection ends.
SPJsonProcess.m
-(void)OpenAConnection:(NSString *)URL appendStringURL:(NSString *)appendURL
{
NSMutableString *postUrl = [NSMutableString stringWithString:URL];
if (![appendURL isEqualToString:nil]) {
[postUrl appendString:appendURL];
}
NSURL *jsonUrl = [NSURL URLWithString:postUrl];
NSData *data = [NSData dataWithContentsOfURL:jsonUrl];
self.responseData = [NSMutableData data];
NSURLRequest *request = [NSURLRequest requestWithURL:jsonUrl];
connection = [[NSURLConnection alloc]initWithRequest:request delegate:self startImmediately:NO];
[connection scheduleInRunLoop:[NSRunLoop mainRunLoop] forMode:NSDefaultRunLoopMode];
[connection start];
-(void)connectionDidFinishLoading:(NSURLConnection *)connection
{
NSLog(#"DidfinishLaunch started");
self.json = [NSJSONSerialization JSONObjectWithData:self.responseData options:NSJSONReadingMutableLeaves error:nil];
if (!self.parsedJsonContent) {
self.parsedJsonContent = [[NSMutableArray alloc]init];
}
for (int i=0;i < self.json.count; i++) {
NSString *result = [self.json objectAtIndex:i];
self.user.result = result;
}
}
-(NSString *)GetJsonResultForLogin
{
return self.user.result;
}
LOGIN VIEW CONTROLLER
>-(void)loginViewFetchedUserInfo:(FBLoginView *)loginView user:(id<FBGraphUser>)user
{
>[self.jsonProcess OpenAConnection:#"My Domain adress" appendStringURL:appendString];
>NSLog(#"Method invoked");
>NSLog(#"result: %#",[self.jsonProcess GetJsonResultForLogin]);
result as a log comes null. But in didFinishLoading method gets result from server without a problem.

how can I use NSURLConnection Asynchronously?

I am using this code to load data to my App, can you tell me how can I make this asynchronously?
NSMutableURLRequest *request2 = [NSMutableURLRequest requestWithURL:[NSURL URLWithString:requestString] cachePolicy:NSURLRequestReloadIgnoringLocalCacheData timeoutInterval:10.0];
NSURLConnection *connection = [[NSURLConnection alloc] initWithRequest:request2 delegate:self];
if (connection)
{
NSLog(#"NSURLConnection connection==true");
NSURLResponse *response;
NSError *err;
NSData *responseData = [NSURLConnection sendSynchronousRequest:request2 returningResponse:&response error:&err];
self.news =[NSJSONSerialization JSONObjectWithData:responseData options:nil error:nil];
NSLog(#"responseData: %#", self.news);
}
else
{
NSLog(#"NSURLConnection connection==false");
};
I think you should be bothered reading the documentation. There's a sendAsynchronousRequest:queue:completionHandler: method.
Create the connection with initWithRequest:delegate:startImmediately:, set yourself as its delegate and implement the delegate methods.
Block code is your friend. I have created a class which does this for you
Objective-C Block code. Create this class here
Interface class
#import <Foundation/Foundation.h>
#import "WebCall.h"
#interface WebCall : NSObject
{
void(^webCallDidFinish)(NSString *response);
}
#property (nonatomic, retain) NSMutableData *responseData;
-(void)setWebCallDidFinish:(void (^)(NSString *))wcdf;
-(void)webServiceCall :(NSString *)sURL_p : (NSMutableArray *)valueList_p : (NSMutableArray *)keyList_p;
#end
Implementation class
#import "WebCall.h"
#import "AppDelegate.h"
#implementation WebCall
#synthesize responseData;
-(void)setWebCallDidFinish:(void (^)(NSString *))wcdf
{
webCallDidFinish = [wcdf copy];
}
- (void)connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)response
{
NSHTTPURLResponse* httpResponse = (NSHTTPURLResponse*)response;
int responseStatusCode = [httpResponse statusCode];
NSLog(#"Response Code = %i", responseStatusCode);
if(responseStatusCode < 200 || responseStatusCode > 300)
{
webCallDidFinish(#"failure");
}
[responseData setLength:0];
}
- (BOOL)connection:(NSURLConnection *)connection canAuthenticateAgainstProtectionSpace:(NSURLProtectionSpace *)protectionSpace
{
return [protectionSpace.authenticationMethod isEqualToString:NSURLAuthenticationMethodServerTrust];
}
- (void)connection:(NSURLConnection *)connection didReceiveAuthenticationChallenge:(NSURLAuthenticationChallenge *)challenge
{
[challenge.sender useCredential:[NSURLCredential credentialForTrust:challenge.protectionSpace.serverTrust] forAuthenticationChallenge:challenge];
[challenge.sender continueWithoutCredentialForAuthenticationChallenge:challenge];
}
- (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data
{
[responseData appendData:data];
}
- (void)connection:(NSURLConnection *)connection didFailWithError:(NSError *)error
{
NSLog(#"WebCall Error: %#", error);
webCallDidFinish(#"failure");
}
- (void)connectionDidFinishLoading:(NSURLConnection *)connection
{
NSString *response = [[NSString alloc] initWithData:responseData encoding:NSUTF8StringEncoding];
response = [response stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceAndNewlineCharacterSet]];
webCallDidFinish(response);
}
-(void)webServiceCall :(NSString *)sURL_p : (NSMutableArray *)valueList_p : (NSMutableArray *)keyList_p
{
NSMutableString *sPost = [[NSMutableString alloc] init];
//If any variables need passed in - append them to the POST
//E.g. if keyList object is username and valueList object is adam will append like
//http://test.jsp?username=adam
if([valueList_p count] > 0)
{
for(int i = 0; i < [valueList_p count]; i++)
{
if(i == 0)
{
[sPost appendFormat:#"%#=%#", [valueList_p objectAtIndex:i],[keyList_p objectAtIndex:i]];
}
else
{
[sPost appendFormat:#"&%#=%#", [valueList_p objectAtIndex:i], [keyList_p objectAtIndex:i]];
}
}
}
NSData * postData = [sPost dataUsingEncoding:NSASCIIStringEncoding allowLossyConversion:NO];
NSString * postLength = [NSString stringWithFormat:#"%d",[postData length]];
NSURL * url = [NSURL URLWithString:sURL_p];
NSMutableURLRequest * request = [NSMutableURLRequest requestWithURL:url cachePolicy:NSURLRequestReloadIgnoringCacheData timeoutInterval:5];
[request setValue:#"application/x-www-form-urlencoded" forHTTPHeaderField:#"Content-Type"];
[request setHTTPMethod:#"POST"];
[request setValue:postLength forHTTPHeaderField:#"Content-Length"];
[request setHTTPBody:postData];
NSURLConnection *theConnection = [[NSURLConnection alloc] initWithRequest:request delegate:self startImmediately:YES];
if (theConnection)
{
self.responseData = [NSMutableData data];
}
}
#end
Then you to make this web call, you call it like this
WebCall *webCall = [[WebCall alloc] init];
[webCall setWebCallDidFinish:^(NSString *response){
//This method is called as as soon as the web call is finished
NSString *trimmedString = [response stringByTrimmingCharactersInSet:
[NSCharacterSet whitespaceAndNewlineCharacterSet]];
if([trimmedString rangeOfString:#"failure"].location == NSNotFound)
{
//Successful web call
}
else
{
//If the webcall failed due to an error
}
}];
//Make web call here
[webCall webServiceCall:#"http://www.bbc.co.uk/" :nil :nil];
See the setWebCallDidFinish method, it will not be called until the webcall has finished.
Hope that helps!!
Here is some code which I am using in my app:
NSURLRequest *request = [NSURLRequest requestWithURL:[NSURL URLWithString:yourURL]];
[NSURLConnection sendAsynchronousRequest:request
queue:[NSOperationQueue mainQueue]
completionHandler:^(NSURLResponse *response, NSData *data, NSError *error) {
if (error) {
NSLog(#"Error loading data from %#. Error Userinfo: %#",yourURL, [error userInfo]);
} else {
NSDictionary *dataFromServer = [NSJSONSerialization JSONObjectWithData:data options:NSJSONReadingMutableContainers error:nil];
contentAsString = [[[dataFromServer objectForKey:#"page"] objectForKey:#"content"] stripHtml];
completionHandler(contentAsString);
}];
fyi: stripHTML is a NSString Category to remove HTML tags from JSON --> Found it Here
you can call your content in your class like that:
[yourClass getDataWithcompletionHandler:^(NSString *content) {
yourObject.content = content;
[yourClass saveManagedObjectContext];
}];
if you implement it once, you won't want to use synchronous connection again...
Check this out: HTTPCachedController
It will help you send POST and GET requests, while it will cache the response and after that it will return the cached data when no internet connection is available.
HTTPCachedController *ctrl = [[[HTTPCachedController alloc] initWithRequestType:1 andDelegate:self] autorelease];
[ctrl getRequestToURL:#"https://api.github.com/orgs/twitter/repos?page=1&per_page=10"];
You will get notified when the data are fetched through a delegate method:
-(void)connectionFinishedWithData:(NSString*)data andRequestType:(int)reqType

Objective C NSURLConnection dosen't get response data

I am creating this application, it communicates with a PHP script on my web-server.
Last night it was working perfectly. But today two of the connections does not get response.
I've tried the NSURL link in my browser, it works fine. Also one of the connections work, but as i said two connections does not work?
- (void) getVitsTitelByID:(int)id {
NSString *url = [NSString stringWithFormat:#"http://webserver.com /ivitserdk.php?function=gettitelbyid&id=%d", id];
NSURLRequest *request = [NSURLRequest requestWithURL:[NSURL URLWithString:url] cachePolicy:NSURLRequestReloadIgnoringCacheData timeoutInterval:1.0];
connectionTitelByID = [[NSURLConnection alloc] initWithRequest:request delegate:self];
}
connectionDidReciveData:
if(connection == connectionTitelByID){
responseTitel = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding];
}
connectionDidFinishLoading:
if(connection == connectionTitelByID){
titelLabel.text = responseTitel;
}
I've tried and debugging it.
responseTitel seems to be (null).
Help would be apriceated :)
didReceiveData may be called N (several) times. save the data to a mutably data buffer (queue it up) and in didFinish read it into a string
mock code:
- (void) getVitsTitelByID:(int)identifier {
NSString *url = [NSString stringWithFormat:#"http://webserver.com/ivitserdk.php?function=gettitelbyid&id=%d", identifier];
NSURLRequest *request = [NSURLRequest requestWithURL:[NSURL URLWithString:url] cachePolicy:NSURLRequestReloadIgnoringCacheData timeoutInterval:1.0];
connectionTitelByID = [[NSURLConnection alloc] initWithRequest:request delegate:self];
dataForConnectionTitelByID = [NSMutableData data];
[connectionTitelByID start];
}
- (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data {
if(!data.length) return;
if(connection == connectionTitelByID)
[dataForConnectionTitelByID appendData:data];
}
- (void)connectionDidFinishLoading:(NSURLConnection *)connection {
if(connection == connectionTitelByID) {
id str = [[NSString alloc] initWithData:dataForConnectionTitelByID encoding:NSUTF8StringEncoding];
NSLog(#"%#",str);
dataForConnectionTitelByID = nil;
connectionTitelByID = nil;
}
}

How to improve memory management when doing low level http work

I'm starting to cleanup an older prototype I worked on and it's doing a great deal of low level http work. The problem I'm having is how and when to do a release inside the "connectionDidFinishLoading" method below. I have some items only created inside the if but when I release them before the method call I get several BAD ACCESS errors and thought to ask how I should be doing memory management in this scenario.
- (void)searchForNewHats:(HatViewController *)hatVwController
{
responseData = [[NSMutableData data] retain]; //responseData is a property that I retain - fyi
hatController = hatVwController; //hatController is a property that I retain - fyi
NSURL *url = [NSURL URLWithString:#"http://localhost/jsondata"];
NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:url cachePolicy:NSURLRequestUseProtocolCachePolicy timeoutInterval:60.0];
[request setHTTPMethod:#"GET"];
[[NSURLConnection alloc] initWithRequest:request delegate:self];
}
- (void)connectionDidFinishLoading:(NSURLConnection *)connection {
[connection release];
NSString *responseString = [[NSString alloc] initWithData:responseData encoding:NSUTF8StringEncoding];
NSArray *json = [responseString JSONValue];
if (json != NULL) {
NSArray *items = [json valueForKeyPath:#"d"];
HatParseJson* hatParseJson = [[HatParseJson alloc] init];
NSArray* hatz = [hatParseJson parseJson:items];
NSMutableArray* newHats = [[NSMutableArray alloc] init];
NSUInteger i, count = [hatz count];
for (i = 0; i < count; i++) {
Hat* obj = [hatz objectAtIndex:i];
[newHats addObject:obj];
//[obj release]; this blows up for example ...
}
[hatParseJson release];
[hatController newHatJsonFinished:newReleases];
}else {
[hatController newHatJsonFinished:nil];
}
[responseString release];
}
- (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 {
}
Thank you in advance
some updates and comments inline:
- (void)searchForNewHats:(HatViewController *)hatVwController
{
assert(0 == responseData);
responseData = [NSMutableData new];
assert(0 == hatController);
self.hatController = hatVwController;
NSURL * url = [NSURL URLWithString:#"http://localhost/jsondata"];
NSMutableURLRequest * request = [NSMutableURLRequest requestWithURL:url cachePolicy:NSURLRequestUseProtocolCachePolicy timeoutInterval:60.0];
[request setHTTPMethod:#"GET"];
/* the delegate is retained in iOS, but not necessarily in OS X so... maybe you want to make connection an ivar of self? */
NSURLConnection * connection = [[NSURLConnection alloc] initWithRequest:request delegate:self];
/* don't you want to hold on to this? */
[connection release], connection = 0;
}
- (void)connectionDidFinishLoading:(NSURLConnection *)inConnection {
if (ConnectionIsAnIvar) {
if (self.connection != inConnection) {
assert(0 && "connection delegate messages sent to wrong instance. threading issue or worse?");
return;
}
}
else {
[connection release];
}
NSString *responseString = [[NSString alloc] initWithData:responseData encoding:NSUTF8StringEncoding];
NSArray *json = [responseString JSONValue];
if (json != NULL) {
NSArray *items = [json valueForKeyPath:#"d"];
HatParseJson* hatParseJson = [[HatParseJson alloc] init];
NSArray* hatz = [hatParseJson parseJson:items];
NSMutableArray* newHats = [[NSMutableArray alloc] init];
/* why not simply:
NSMutableArray* newHats = [hatz mutableCopy];
*/
NSUInteger i, count = [hatz count];
for (i = 0; i < count; i++) {
Hat* obj = [hatz objectAtIndex:i];
[newHats addObject:obj];
/* [obj release]; this blows up for example ... */
/* >> it should blow up. objectAtIndex: uses a get, not retain or copy */
}
[hatParseJson release];
[hatController newHatJsonFinished:newReleases];
}
else {
[hatController newHatJsonFinished:nil];
}
[responseString release];
}
[obj release] is a wrong thing to do because you are just adding objects from one array to another. Instead you should release newHats when done because you explicitly allocate it with alloc.