Getting the NSURLConnection delegate callback into the right thread - objective-c

I can't seem to get a Delegate callback to work when I thread my code. I am trying to call a remote server using NSURLConnection to get an access_token. The access_token is received in connection:didReceiveResponse: delegate. This is not a problem until I thread it. I thought I was passing the correct objects for my delegates, but it does not reach connection:didReceiveResponse:
Can anyone see why connection:didReceiveResponse: in LoginViewController does not get called when I thread the call? Thanks.
LoginViewController.m
- (void)connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)response
{
NSString *access_token = [self getTokenFromResponse: response];
[self.delegate didGetAccessToken:access_token];
}
- (void)fetchAccessTokenNoUI
{
NSURLRequest *request = [NSURLRequest requestWithURL:[NSURL URLWithString: #"www.mysite.com"]];
[NSURLConnection connectionWithRequest:request delegate:self];
}
AccessToken.h
#interface AccessToken : NSObject
#property (atomic, strong) LoginViewController *loginViewController; // doing this to try to keep it persistent
- (void) fetchAccessTokenWithDelegate: (id <LoginDelegate>)delegate;
#end
AccessToken.m
- (void) fetchAccessTokenWithDelegate: (id < LoginDelegate >)delegate
{
dispatch_queue_t downloadQueue = dispatch_queue_create("Fetch access_token queue", NULL);
dispatch_async(downloadQueue, ^ {
// this works fine if I don't do it in a queue
self.loginViewController = [[LoginViewController alloc] init];
self.loginViewController.delegate = delegate;
[self.loginViewController fetchAccessTokenNoUI];
});
dispatch_release(downloadQueue);
}
CallingClass.m
- (void)didGetAccessToken:(NSString *)access_token
{
if (!access_token)
{
LoginViewController *userProfileViewController = [[LoginViewController alloc] init];
userProfileViewController.delegate = self;
[self.navigationController pushViewController:userProfileViewController animated:YES];
}
}
- (IBAction)favourite:(id)sender
{
AccessToken *accessToken = [[AccessToken alloc] init];
[accessToken fetchAccessTokenWithDelegate:self];
}

So after some more research, I found the solution.
I don't actually need AccessToken.h/m. I thought that the NSURLConnection call would block my UI thread. But NSURLConnection connectionWithRequest:delegate: is automatically run in a separate thread.
So all I have to do is delete AccessToken.h and AccessToken.m. Then, in CallingClass.m, I change favourte: to
- (IBAction)favourite:(id)sender
{
LoginViewController *loginViewController = [[LoginViewController alloc] init];
[loginViewController fetchAccessTokenWithDelegate:self];
}
That's it! Don't need to dispatch_queue_t at all.

Have you put stubs to implement the required protocol methods,
-(void)connectionDidFinishLoading:(NSURLConnection *)connection;
-(void)connection:(NSURLConnection *)connection didFailWithError:(NSError *)error;
-(void)connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)response
-(void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data;
Because I had a similar problem which went away after implementing them.

Related

use of undeclared identifier

I am new to Objective-C coding ,please bear with me to ask if this is simple question
My Header file:
#import <Cocoa/Cocoa.h>
#interface AppDelegate : NSObject <NSApplicationDelegate>
#property (assign) IBOutlet NSWindow *window;
- (void)applicationDidFinishLaunching:(NSNotification *)aNotification;
- (void)connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse*) response;
#end
My Implementation file:
#import "AppDelegate.h"
#implementation AppDelegate
- (void)applicationDidFinishLaunching:(NSNotification *)aNotification
{
// Insert code here to initialize your application
NSMutableData *receivedData =[NSMutableData dataWithCapacity: 0];
// Create the request
NSURLRequest *theRequest=[NSURLRequest requestWithURL:[NSURL URLWithString:#"http://www.apple.com/"] cachePolicy:NSURLRequestUseProtocolCachePolicy
timeoutInterval:60.0];
// Create the NSMutableData to hold the received data.
// receivedData is an instance variable declared elsewhere.
receivedData = [NSMutableData dataWithCapacity: 0];
// 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.
}
}
- (void)connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)response
{
NSMutableData *receivedData =[NSMutableData dataWithCapacity:0];
// This method is called when the server has determined that it
// has enough information to create the NSURLResponse object.
// It can be called multiple times, for example in the case of a
// redirect, so each time we reset the data.
// receivedData is an instance variable declared elsewhere.
[receivedData setLength:0];
}
#end
when i run the code at implementation appDelegate it gives warning
Method definition for connection:didRecieveResponse :not found
and where
-(void)connection line it gives "use of undelcared identifier connection did u mean Collection".
You do not need to declare the method
- (void)connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse*) response;
in your header file if you are using the NSURLConnectionDataDelegate Protocol in your code. Just add the protocol in the implementation and use the methods from that protocol.

Objective-C issue with connectionDidFinishLoading and Delegate

Am trying to use an API class to fetch data from a URL and then have that data returned to a method inside the ViewController. I have the ViewController call the API method.
Have tried several places on SO already and come up with the following, but it is not working and as an Objective-C noobie have tried reading documentation and such but still not understanding what could be going wrong.
I have a viewcontroller calling an method 'fetch' inside a class 'api'.
I have the connection delegates inside api class and it works ok (correct data is printed inside connectionDidFinishLoading method).
I need a delegate to return the data to a method inside the viewcontroller class.
So far I have
ViewController.h
#import "Api.h"
#interface ViewController : UIViewController <apiDelegate>{
}
- (void)apiSucceeded:(NSString *)jsonString;
- (void)apiFailed:(NSString *)failedMessage;
#end
ViewController.m
#import "ViewController.h"
#import "Api.h"
- (void)apiSucceeded:(NSString *)jsonString {
NSLog(#"WORKED");
}
- (void)apiFailed:(NSString *)failedMessage {
NSLog(#"FAILED");
}
- (void)viewDidLoad {
[super viewDidLoad];
Api* myapi = [[Api alloc]init];
[myapi fetch];
}
#end
Api.h
#protocol apiDelegate
#required
- (void)apiSucceeded:(NSString *)jsonString;
- (void)apiFailed:(NSString *)failedMessage;
#end
#interface Api : NSObject {
id _delegate;
}
#property (nonatomic, assign) id _delegate;
-(void)fetch;
#end
Api.m
#import "Api.h"
#import "ViewController.h"
#synthesize _delegate;
- (void)fetch{
//connection setup....
[NSURLConnection connectionWithRequest:[request autorelease] delegate:self];
}
-(void)connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)response
{
[self.receivedData setLength: 0];
}
-(void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data
{
// NSLog(#"append data");
[self.receivedData appendData:data];
// NSLog(#"Bytes: %d", [receivedData length]);
}
-(void)connection:(NSURLConnection *)connection didFailWithError:(NSError *)error
{
//TODO error handling for connection
if ([_delegate respondsToSelector:#selector(apiFailed:)]) {
[_delegate apiFailed:[error localizedDescription]];
}
NSLog(#"Cannot Connect");
}
-(void)connectionDidFinishLoading:(NSURLConnection *)connection
{
NSLog(#"DONE. Received Bytes: %d", [self.receivedData length]);
NSString *jsonString = [[NSString alloc] initWithBytes: [receivedData mutableBytes] length:[receivedData length] encoding:NSUTF8StringEncoding];
if ([_delegate respondsToSelector:#selector(apiSucceeded:)]) {
[_delegate apiSucceeded:jsonString];
}
}
#end
There is no error but it just does not run 'apiSucceeded' method.
Please dont misunderstand this as a question regarding 'connectionDidFinishLoading'. That bit works well, its the handing of the data BACK to a ViewController that is causing issues.
Can anybody see what I have done wrong please?
I seems that you forgot to set
myapi.delegate = self;
after
Api* myapi = [[Api alloc]init];

NSURLConnection returns old data although NSURLRequestReloadIgnoringLocalAndRemoteCacheData

I’ve got a serious problem. In my app I m downloading a XML-File using NSURLConnection. So far so good. But from time to time, the NSURLConnection returns an old version of the requested file although I set The cachePolicy to NSURLRequestReloadIgnoringLocalAndRemoteCacheData.
The really disgusting part of the bug is, that the connection always return the old version of the file until I active and then deactivate the airplane mode or I rebooted the device.
An other interesting fact is: When I activate Wifi and call the app to reload the file the correct and actual version of the file is returned. But after turning off Wifi the old version is retuned again.
I m really becoming desperate with this problem and hope anybody can help me?
Here is the code from my class, downloading the file:
#import "HTTPReciever.h"
#import "Constants.h"
#interface HTTPReciever ()
#property (strong, nonatomic) NSMutableData *receivedData;
#property (strong, nonatomic) NSString *directoryPath;
#property (strong, nonatomic) NSString *filename;
#property (assign, nonatomic) BOOL storeData;
#end
#implementation HTTPReciever
#synthesize receivedData = _receivedData;
#synthesize delegate = _delegate;
#synthesize directoryPath = _directoryPath;
#synthesize filename = _filename;
- (BOOL)recieveDataFromURL:(NSURL *)url
storeAtDirectoryPath:(NSString *)directoryPath
withName:(NSString *)name
{
[self setDirectoryPath:directoryPath];
[self setFilename:name];
[self setStoreData:YES];
return [self recieveDataFromURL:url];
}
- (BOOL)recieveDataFromURL:(NSURL *)url
{
NSURLRequest *theRequest = [[NSURLRequest alloc] initWithURL:url
cachePolicy:NSURLRequestReloadIgnoringLocalAndRemoteCacheData
timeoutInterval:cDefaultHTTPTimeOutInterval];
self.receivedData = nil;
self.receivedData = [[NSMutableData alloc] initWithLength:0];
NSURLConnection *connection = [[NSURLConnection alloc] initWithRequest:theRequest
delegate:self
startImmediately:NO];
if ([NSURLConnection canHandleRequest:theRequest] && connection) {
[connection start];
return YES;
} else {
return NO;
}
}
#pragma mark - NSURLConnectionDelegate protocol
- (void)connection:(NSURLConnection *)connection
didReceiveData:(NSData *)data
{
[self.receivedData appendData:data];
}
- (void)connection:(NSURLConnection *)connection
didReceiveResponse:(NSURLResponse *)response
{
[UIApplication sharedApplication].networkActivityIndicatorVisible = YES;
[self.receivedData setLength:0];
if ([response isKindOfClass:[NSHTTPURLResponse self]]) {
NSDictionary *headers = [(NSHTTPURLResponse *)response allHeaderFields];
NSString *modified = [headers objectForKey:#"Last-Modified"];
if (modified) {
// not yet used
}
}
}
- (void)connectionDidFinishLoading:(NSURLConnection *)connection
{
[UIApplication sharedApplication].networkActivityIndicatorVisible = NO;
if (self.storeData) {
[[NSFileManager defaultManager] createDirectoryAtPath:self.directoryPath
withIntermediateDirectories:YES
attributes:nil
error:nil];
[self.receivedData writeToFile:[NSString stringWithFormat:#"%#%#", self.directoryPath, self.filename]
atomically:YES];
}
[self.delegate receiver:self
didFinishLoadingData:self.receivedData
lastVersion:YES];
}
- (void)connection:(NSURLConnection *)connection
didFailWithError:(NSError *)error
{
[self.delegate receiverFailedLoadingData:self];
}
- (NSCachedURLResponse *)connection:(NSURLConnection *)connection
willCacheResponse:(NSCachedURLResponse *)cachedResponse
{
return nil;
}
#end

NSUrlConnection in NSThread - No delegate is executed!

I am using an NSURLConnection in an NSThread but none of the NSURLConnection's delegate methods are executed! I have a main method in my NSTread subclass and a while loop that keeps the thread active. Any help?
Sorry for all of this code but I think its the best way to describe my problem. So this is an object that does the async connection calling createConnectionWithPath:userObjectReference
#interface WSDAsyncURLConnection : NSObject
{
NSMutableData *receivedData;
NSDate *connectionTime;
NSURLConnection *connection;
id _theUserObject;
}
#property (nonatomic, retain) NSMutableData *receivedData;
#property (nonatomic, retain) NSDate *connectionTime;
#property (nonatomic, assign) NSURLConnection *connection;
- (void)createConnectionWithPath:(NSString *)thePath userObjectReference:(id)userObject;
#end
#import "WSDAsyncURLConnection.h"
#implementation WSDAsyncURLConnection
#synthesize connectionTime, receivedData, connection;
- (void) terminate
{
if (self.connection) {
[self.connection release];
self.connection = nil;
}
}
- (void) createConnectionWithPath:(NSString *)thePath userObjectReference:(id)userObject;
{
_theUserObject = userObject;
NSURLRequest *theRequest = [NSURLRequest requestWithURL:[NSURL URLWithString:thePath]
cachePolicy:NSURLRequestReloadIgnoringLocalAndRemoteCacheData timeoutInterval:60];
self.connection = [[NSURLConnection alloc] initWithRequest:theRequest delegate:self startImmediately:YES];
if (self.connection)
{
/* record the start time of the connection */
self.connectionTime = [NSDate date];
/* create an object to hold the received data */
self.receivedData = [NSMutableData data];
}
}
- (void) connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)response
{
[self.receivedData setLength:0];
}
- (void) connection:(NSURLConnection *)connection didReceiveData:(NSData *)data
{
/* appends the new data to the received data */
[self.receivedData appendData:data];
}
- (void) connection:(NSURLConnection *)connection didFailWithError:(NSError *)error
{
[self terminate];
}
- (void) connectionDidFinishLoading:(NSURLConnection *)connection
{
// displays the elapsed time in milliseconds
NSTimeInterval elapsedTime = [[NSDate date] timeIntervalSinceDate:self.connectionTime];
// displayes the length of data received
NSUInteger length = [self.receivedData length];
NSString* aStr = [[NSString alloc] initWithData:receivedData encoding:NSASCIIStringEncoding];
[self terminate];
[[NSNotificationCenter defaultCenter] postNotificationName:WSDAsynchURLConnectionDidFinished
object:_theUserObject
userInfo:[NSDictionary dictionaryWithObject:aStr forKey:#"urlResponseString"]];
NSLog(#"ti=%f, l=%d, response=%#", elapsedTime, length, aStr);
}
#end
This code is mostly from an apple's example project and it works fine outside an NSThread.
But when I use it in the following thread subclass no delegate method is executed !!
#implementation IncomingThread
- (void) main {
NSAutoreleasePool *poool = [[NSAutoreleasePool alloc] init];
// I start the URLConnection here ... But no delegate is executed !
[urlConn createConnectionWithPath:#"http://localhost:8888" userObjectReference:nil];
while (![self isCancelled]) {
[NSThread sleepForTimeInterval:3.];
}
[poool release];
}
- (id) init
{
self = [super init];
if (self != nil) {
urlConn = [[WSDAsyncURLConnection alloc] init];
}
return self;
}
- (void) dealloc {
NSLog(#"deallocating (%#)...", [self className]);
[urlConn release];
[super dealloc];
}
First of all: you don't need to use NSURLConnection in the separate thread. Since it is asynchronous it doesn't block the main thread.
Second: there is not processing of your connection because you always stop the execution of the thread's runloop with this peace of code:
while (![self isCancelled]) {
[NSThread sleepForTimeInterval:3.];
}
From the docs for the sleepForTimeInterval:
No run loop processing occurs while the thread is blocked.
You’re doing this the hard way. NSURLConnection does not play very nice with threads, since it needs a run loop to work. Your thread does not have a run loop and therefore the code does not work. Why not run the connection on the main thread? Or you can wrap the connection in an NSOperation, sample code here. And nowadays you also have the option to use a synchronous connection and dispatch it to a global GCD queue.
Did you remember to assign the delegate?
Something like:
self.connection.delegate = self;
Just because your class WSDAsyncURLConnection implements the delegate methods, doesn't mean they are being called.
Late but it may save others life :)
Solution link is : NSURLConnection delege methods does not work

Objective C: How to release delegates in this situation

I am using custom delegate objects to do some cleanup tasks after a request finishes. ASIHTTPRequest doesn't retain delegates so I can't autorelease them. Right now this is how I am allocating and releasing the delegates.
App Delegate
MyDelegate *delegate = [[MyDelegate alloc] init];
ASIHTTPRequest *request = [ASIHTTPRequest requestWithURL:url];
[request setDelegate:delegate];
MyDelegate.m
- (void)requestFinished:(ASIHTTPRequest *)request
{
[self release];
}
- (void)requestFailed:(ASIHTTPRequest *)request
{
[self release];
}
Is there a better way to do this? Having the delegates release themselves seems ugly and Xcode's build and analyze feels uncomfortable with what I'm doing.
A simple approach would be to maintain a mutable set of delgates for each active request in your main controller (the app delegate, in this case):
#interface MyAppController
{
NSMutableSet * activeDelegates;
}
#end
#implementation MyAppController
- (id)init
{
if ((self = [super init]) == nil) { return nil; }
activeDelegates = [[NSMutableSet alloc] initWithCapacity:0];
return self;
}
- (void)dealloc
{
[activeDelegates release];
}
- (void)createRequest
{
MyDelegate *delegate = [[MyDelegate alloc] init];
[activeDelegates addObject:delegate];
[delegate release];
ASIHTTPRequest *request = [ASIHTTPRequest requestWithURL:url];
[request setDelegate:self];
...
}
- (void)requestFinished:(ASIHTTPRequest *)request
{
MyDelegate *delegate = [request delegate];
[delegate doSomething];
[activeDelegates removeObject:delegate];
{
- (void)requestFailed:(ASIHTTPRequest *)request
{
[activeDelegates removeObject:[request delegate]];
}
#end
Why do you have a separate class purely to be a delegate? That's not how delegate objects typically work. Normally the controller that created the ASIHTTPRequest becomes the delegate, at which point you don't have to worry about releasing it because it will outlive the ASIHTTPRequest already (and if your controller gets dealloced before the ASIHTTPRequest is done, you need to cancel the request).
If You don't want to create a "controller" class for all your delegate instances, i would still at least follow the memory convention rules, and release the delegate immediately after setting it to asihhtprequest. Then i would include a propery in the delegate, something with a name managesItsOwnLifetime (BOOL) and on setting this to YES i would do a [self retain] ...