iOS background Location not sending http request - iphone

My app needs to track the users location in the background but it is failing to send a 'get' request. The http request gets sent immediately when the app comes to the foreground. I am using RestKit for all my network requests and I followed this tutorial to setup my background locations service.
In my applicationDidEnterBackground
-(void)applicationDidEnterBackground:(UIApplication *)application
{
self.bgLocationManager = [[CLLocationManager alloc] init];
self.bgLocationManager.delegate = self;
[self.bgLocationManager startMonitoringSignificantLocationChanges];
NSLog(#"Entered Background");
}
and I stopMonitoringSignificantLocationChange in my applicationDidBecomeActive delegate
This is my locationManager delegate where I accept the new updated location and send to my server
-(void) locationManager:(CLLocationManager *)manager
didUpdateToLocation:(CLLocation *)newLocation
fromLocation:(CLLocation *)oldLocation
{
NSLog(#"I am in the background");
bgTask = [[UIApplication sharedApplication]
beginBackgroundTaskWithExpirationHandler:
^{
[[UIApplication sharedApplication] endBackgroundTask:bgTask];
}];
// ANY CODE WE PUT HERE IS OUR BACKGROUND TASK
NSString *currentLatitude = [[NSString alloc]
initWithFormat:#"%g",
newLocation.coordinate.latitude];
NSString *currentLongitude = [[NSString alloc]
initWithFormat:#"%g",
newLocation.coordinate.longitude];
NSString *webToken = [[NSUserDefaults standardUserDefaults] stringForKey:#"userWebToken"];
NSLog(#"I am in the bgTask, my lat %#", currentLatitude);
NSDictionary *queryParams;
queryParams = [NSDictionary dictionaryWithObjectsAndKeys:webToken, #"auth_token", currentLongitude, #"lng", currentLatitude, #"lat", nil];
RKRequest* request = [[RKClient sharedClient] post:#"/api/locations/background_update" params:queryParams delegate:self];
//default is RKRequestBackgroundPolicyNone
request.backgroundPolicy = RKRequestBackgroundPolicyContinue;
// AFTER ALL THE UPDATES, close the task
if (bgTask != UIBackgroundTaskInvalid)
{
[[UIApplication sharedApplication] endBackgroundTask:bgTask];
bgTask = UIBackgroundTaskInvalid;
}
}
The network requests works as planned but it will not get called in the background. Is there any additional steps I need? In my info.plist I have the Required Background modes key and location-services as the value.
EDIT
I also referred to this past SO answer. I ran some tests with putting logs throughout the didUpdateToLocation call and they were all called but the 'get' request was not sent. Instead when I finally launch the app to the foreground it sent all the built of network requests (over 10).
EDIT (2)
I added RKRequestBackgroundPolicyContinue to my request but it did not change my results. (As you can see here in the background upload/download for restkit). I see Restkit initialize the host but fails to send the request until the app becomes active.
ANSWER
RestKit must be doing something that is prohibited in the background. Using an NSURLRequest works perfectly.
NSMutableURLRequest * urlRequest = [NSMutableURLRequest requestWithURL:[NSURL URLWithString:#"http://www.example.com/api/locations/background_update"]];
[urlRequest setValue:#"application/json" forHTTPHeaderField:#"Accept"];
[urlRequest setValue:#"application/json" forHTTPHeaderField:#"Content-Type"];
[urlRequest setHTTPMethod:#"POST"];
[urlRequest setHTTPBody:jsonData];
NSHTTPURLResponse *response = nil;
[NSURLConnection sendSynchronousRequest:urlRequest
returningResponse:&response
error:&error];
It is fine to use a synchronous request since there is no UI to disrupt with background tasks

Re-creating original suggestion as an answer
Have your try replacing your restKit calls with a stock synchronous NSURLConnection? – dklt Sep 20

I'm using exactly the same code as you and it works for me in RestKit. The only way I could make it work is ny creating a synchronous request (it doesn't make a lot of sense to do it asynchronously in this context anyway!). Please check this code and let us know if it works:
// REMEMBER. We are running in the background if this is being executed.
// We can't assume normal network access.
// bgTask is defined as an instance variable of type UIBackgroundTaskIdentifier
// Note that the expiration handler block simply ends the task. It is important that we always
// end tasks that we have started.
_bgTask = [[UIApplication sharedApplication] beginBackgroundTaskWithExpirationHandler:
^{
[[UIApplication sharedApplication] endBackgroundTask:_bgTask];
}];
// ANY CODE WE PUT HERE IS OUR BACKGROUND TASK
// For example, I can do a series of SYNCHRONOUS network methods (we're in the background, there is
// no UI to block so synchronous is the correct approach here).
NSNumber *latNumber = [NSNumber numberWithDouble:location.coordinate.latitude];
NSNumber *lngNumber = [NSNumber numberWithDouble:location.coordinate.longitude];
NSNumber *accuracyNumber = [NSNumber numberWithDouble:location.horizontalAccuracy];
NSDictionary *params = [NSDictionary dictionaryWithKeysAndObjects:#"lat",latNumber,#"lng",lngNumber,#"accuracy",accuracyNumber, nil];
RKURL *URL = [RKURL URLWithBaseURL:[NSURL URLWithString:SERVER_URL] resourcePath:#"/user/location/update" queryParameters:params];
RKRequest *request = [RKRequest requestWithURL:URL];
request.method = RKRequestMethodGET;
NSLog(#"Sending location to the server");
RKResponse *response = [request sendSynchronously];
if (response.isFailure)
NSLog(#"Unable to send background location, failure: %#", response.failureErrorDescription);
else {
NSError *error = nil;
NSDictionary *parsedBody = [response parsedBody:&error];
if (YES == [[parsedBody objectForKey:#"result"] boolValue]){
NSLog(#"Background location sent to server");
}
else {
//Something went bad
NSLog(#"Failed to send background location");
}
}
// AFTER ALL THE UPDATES, close the task
if (_bgTask != UIBackgroundTaskInvalid)
{
[[UIApplication sharedApplication] endBackgroundTask:_bgTask];
_bgTask = UIBackgroundTaskInvalid;
}
I'm almost sure the new thread spawned for your RKClient request is automatically killed after invoking it.

When you're application is running in the background you can finish a HTTP request you started before you entered the background but you cannot initiate a new request. You can only initiate certain network operations while in the background (voip, newsstand).

Related

Perform AFHTTPClient request, in background, with NSOperationQueue chronologically

I have an HTTPClient request as follows :
NSURL *url = [NSURL URLWithString:[NSString stringWithFormat:urlStringMain]];
AFHTTPClient *httpClient = [[AFHTTPClient alloc] initWithBaseURL:url];
NSDictionary *params = [NSDictionary dictionaryWithObjectsAndKeys:
//parameters
nil];
[self beginBackgroundUpdateTask];
[httpClient postPath:postPath parameters:params success:^(AFHTTPRequestOperation *operation, id responseObject) {
//id results = [NSJSONSerialization JSONObjectWithData:responseObject options:NSJSONWritingPrettyPrinted error:nil];
//completion code
[self endBackgroundUpdateTask];
} failure:^(AFHTTPRequestOperation *operation, NSError *error) {
//failure code
}];
[httpClient release];
The background task is executed in :
- (void) beginBackgroundUpdateTask{
[operationQueue addOperationWithBlock:^{
NSLog(#"started upload process as a background job");
self.backgroundUpdateTask = [[UIApplication sharedApplication] beginBackgroundTaskWithExpirationHandler:^{
[self endBackgroundUpdateTask];
}];
}];
}
And ended in :
- (void) endBackgroundUpdateTask{
NSLog(#"complete upload process as a background job");
[[UIApplication sharedApplication] endBackgroundTask: self.backgroundUpdateTask];
self.backgroundUpdateTask = UIBackgroundTaskInvalid;
}
where self.backgroundUpdateTask is a UIBackgroundTaskIdentifier object, and operationQueue is an object of NSOperationQueue (public member), initialized in viewDidLoad:
operationQueue = [[NSOperationQueue alloc] init];
[operationQueue setMaxConcurrentOperationCount:NSOperationQueueDefaultMaxConcurrentOperationCount];
Now what I want to do is, perform these requests chronologically, in the background, such that pushing/popping from the viewController does not affect the request. It should also not be affected if the application goes into background. At times I post text, and at other times I post an image. Now, images take longer to upload, than text so if subsequent requests of text and images are made, texts are posted first, and images later. This breaks the chronology of the tasks, hence I wanted to use the NSOperationQueue. But being new to operation queues, I cannot seem to make it work. The chronology is still not being respected. How do I perform the task in the way I want to.
PS. Also, as you can see in the code, i have added [self endBackgroundUpdateTask] in both, the completion block of the httpClient request, and the beginBackgroundUpdateTask method. Now i understand this is not good. Where exactly should the endBackgroundUpdateTask method be called ?
Thank you.
The HTTPClient can handle the background task for you if you ask it to, simply set the appropriate flag (you need to create the operation and call setShouldExecuteAsBackgroundTaskWithExpirationHandler:).
Rather than change how the uploads operate, keep it simple. Send a date with the upload and use that to maintain your order information on the server.
If you really must execute your operations serially then you can get the operationQueue from the client and set it to only execute a single operation at a time.
In either case you shouldn't need to create your own operation queue.

NSURLConnection sendAsynchronousRequest: Blocking Main Thread

I'm using NSURLConnection to make multiple asynchronous requests. I'd like to show a progress indicator to show how many requests have been completed out of the total number to be performed. However, when I attempt to set up and display this progress indicator either before making the request, or in another method called before performing the request, it will not show. The progress indicator displays fine when the request is commented out. But when it's not, it's as if Xcode looks ahead and sees an asynchronous request coming and blocks the main thread, thereby making UI changes impossible.
Here's the relevant code being called, including both the request and code to show the progress indicator:
- (void)getRegionalInformationFromChecked:(NSSet *)set atIndex:(NSInteger)index {
__block BOOL responseRecieved = NO;
NSString *stringForURL = [NSString stringWithFormat:#"http://www.thebluealliance.com/api/v1/event/details?event=%#",[[set allObjects] objectAtIndex:index]];
NSURL *url = [NSURL URLWithString:stringForURL];
NSOperationQueue *queue = [[NSOperationQueue alloc] init];
NSLog(#"URL IS GO: %#", stringForURL);
[NSURLConnection sendAsynchronousRequest:[NSURLRequest requestWithURL:url] queue:queue completionHandler:^(NSURLResponse *_response, NSData *_data, NSError *_error) {
NSLog(#"CHECKED DATA RETURNED AT INDEX %i", index);
NSError *error;
NSDictionary *dict = [NSJSONSerialization JSONObjectWithData:_data options:NSJSONReadingMutableContainers error:&error];
if (!_regionalDetails) {
_regionalDetails = [[NSMutableArray alloc] init];
}
[_regionalDetails addObject:dict];
responseRecieved = YES;
}];
regionalSchedulesToGet = [set count];
while (responseRecieved == NO) {}
[[MBProgressHUD HUDForView:[[UIApplication sharedApplication] keyWindow]] setLabelText:[NSString stringWithFormat: #"Getting regional %i of %i", index+2, [set count]]];
if (index+1 < [set count]) {
[self getRegionalInformationFromChecked:set atIndex:index+1];
} else {
[[MBProgressHUD HUDForView:[[UIApplication sharedApplication] keyWindow]] setLabelText:#"Writing to file"];
}
}
When the asynchronous request's block is commented out, the MBProgressHUD displays its value fine. But when the block is inserted, the SDK refuses to update the progress indicator, even after leaving the block (after which any threading issues should have been resolved). It does not update until there are no more requests to display, at which point it reads "Writing to file".
Why does an asynchronous request seem to block the main thread, and why can I not make changes on the main thread immediately before or after the request is called?
With
while (responseRecieved == NO) {}
you block the main thread (probably with almost 100% CPU load) until the asynchronous block has finished. Then you call your
function recursively, start another asynchronous block and block again until that has
finished. Therefore the program control does not return to the main runloop until all
operations have finished. Only then the UI updates are done.
Instead of waiting synchronously (which is always a bad idea),
you should start the next operation at the end of the completion block.
Note also that the queue argument of sendAsynchronousRequest is the queue on which
the completion handler is called, so you can just use [NSOperationQueue mainQueue].
Then your code looks roughly like this:
- (void)getRegionalInformationFromChecked:(NSSet *)set atIndex:(NSInteger)index
{
[[MBProgressHUD HUDForView:[[UIApplication sharedApplication] keyWindow]]
setLabelText:[NSString stringWithFormat:#"Getting regional %i of %i", index+1, [set count]]];
NSString *stringForURL = [NSString stringWithFormat:#"http://www.thebluealliance.com/api/v1/event/details?event=%#",[[set allObjects] objectAtIndex:index]];
NSURL *url = [NSURL URLWithString:stringForURL];
NSLog(#"URL IS GO: %#", stringForURL);
[NSURLConnection sendAsynchronousRequest:[NSURLRequest requestWithURL:url] queue:[NSOperationQueue mainQueue]
completionHandler:^(NSURLResponse *_response, NSData *_data, NSError *_error) {
NSLog(#"CHECKED DATA RETURNED AT INDEX %i", index);
NSError *error;
NSDictionary *dict = [NSJSONSerialization JSONObjectWithData:_data options:NSJSONReadingMutableContainers error:&error];
if (!_regionalDetails) {
_regionalDetails = [[NSMutableArray alloc] init];
}
[_regionalDetails addObject:dict];
if (index+1 < [set count]) {
[self getRegionalInformationFromChecked:set atIndex:index+1];
} else {
[[MBProgressHUD HUDForView:[[UIApplication sharedApplication] keyWindow]] setLabelText:#"Writing to file"];
// ... perhaps call a completion function from here ?
}
}];
}
But note that the initial call to getRegionalInformationFromChecked will now
return almost immediately (that's how asynchronous tasks work :-).
Try to dispatch on the main thread all the methods that involve UI refresh

Implementation of Async Request causing many leaks

I've inherited a project that uses of ASIHttpRequest for all network communication. I am unclear as to which specific version we're using. All I can tell is that, from the .h files, the oldest creation date on a particular file is 17/08/10 (ASIDataDecompressor).
We're using completion and failure blocks. For some reason, the failure block is often triggered, which should only really happen if the server fails to respond. Our logs look sane, and we haven't received any notifications (Airbrake) that there were server problems around the time the errors occur, so for now I'm moving forward with the assumption that our server is fine and it's the app that is the culprit.
I decided to run the app through Instruments (Leaks) and was astonished to see that when I force a request to fail, ~27 leaks are created immediately. I'm don't know how to get around Instruments all that well, so I'm not really sure what to do with the information now that I have it.
I figured I'd post my code to see if there's anything glaring.
In viewDidLoad, this code is executed
[[MyAPI sharedAPI] getAllHighlights:pageNumber:perPage onSuccess:^(NSString *receivedString,NSString *responseCode) {
[self getResults:receivedString];
if(![responseCode isEqualToString:#"Success"]) {
[self hideProgressView];
appDelegate.isDiscover_RefreshTime=YES;
[[MyAPI sharedAPI] showAlert:responseCode];
} else {
NSString *strLogEvent=#"Discover_Highlights_Loaded Page_";
strLogEvent=[strLogEvent stringByAppendingFormat:#"%i",intPageNumber];
[FlurryAnalytics logEvent:strLogEvent timed:YES];
}
} onFail:^(ASIFormDataRequest *request) {
NSDictionary *parameters = [[MyAPI sharedAPI] prepareFailedRequestData:request file:#"Discover" method:_cmd];
[FlurryAnalytics logEvent:#"Unable_to_Connect_to_Server" withParameters:parameters timed:true];
[self hideProgressView];
appDelegate.isDiscover_RefreshTime=YES;
[[AfarAPI sharedAPI] showAlert:#"Unable to Connect to Server."];
[tblHighlightsGrid reloadData];
[tblListHighlights reloadData];
}];
These typedefs have been defined at the top of API Singleton:
typedef void (^ASIBasicBlockWrapper)(NSString *responseString,NSString *responseCode);
typedef void (^ASIBasicBlockWrapperFail)(ASIFormDataRequest *request);
MyAPISingleton#getAllHighlights...
- (void)getAllHighlights:(NSString *)pageNumber:(NSString *)perPage onSuccess:(ASIBasicBlockWrapper)cb1 onFail:(ASIBasicBlockWrapperFail)cb2{
NSString *access_token= [[NSUserDefaults standardUserDefaults] objectForKey:#"access_token"];
NSString *url = [baseURL stringByAppendingFormat:AFAR_GET_ALL_HIGHLIGHTS_ENDPOINT, pageNumber,perPage];
if (access_token) { url = [url stringByAppendingFormat:ACCESS_TOKEN, access_token]; }
__block ASIFormDataRequest *request = [ASIFormDataRequest requestWithURL:[NSURL URLWithString:url]];
[request setRequestMethod:#"GET"];
[request setDelegate:self];
[self executeAsynchronousRequest:request onSuccess:cb1 onFail:cb2];
}
And finally, MyAPI#executeAsynchronousRequest:
- (void) executeAsynchronousRequest:(ASIFormDataRequest *)request onSuccess:(ASIBasicBlockWrapper)cb1 onFail:(ASIBasicBlockWrapperFail)cb2
{
[request setCompletionBlock:^{
int statusCode = [request responseStatusCode];
NSString *statusMessage = [self statusErrorMessage:statusCode];
cb1([request responseString],statusMessage);
}];
[request setFailedBlock:^{
cb2(request);
}];
[request startAsynchronous];
}
Does anything stand out as to why 27 leaks are created?
I figured this out.
The ASIHttpRequest Documentation is very clear about the fact that you need to designate your request object with the __block storage mechanism:
Note the use of the __block qualifier when we declare the request, this is important! It tells the block not to retain the request, which is important in preventing a retain-cycle, since the request will always retain the block.
In getAllHighlights(), I'm doing that, but then I'm sending my request object as an argument to another method (executeAsyncRequest). The __block storage type can only be declared on local variables, so in the method signature, request is just typed to a normal ASIFormDataRequest, and so it seems as though it loses its __block status.
The trick is to cast (I'm not sure if that's technically accurate) the argument before using it in a block.
Here's my leak free implementation of executeAsyncRequest:
- (void) executeAsyncRequest:(ASIFormDataRequest *)request onSuccess:(ASIBasicBlockWrapper)cb1 onFail:(ASIBasicBlockWrapperFail)cb2
{
// this is the important part. now we just need to make sure
// to use blockSafeRequest _inside_ our blocks
__block ASIFormDataRequest *blockSafeRequest = request;
[request setCompletionBlock: ^{
int statusCode = [blockSafeRequest responseStatusCode];
NSString *statusMessage = [self statusErrorMessage:statusCode];
cb1([blockSafeRequest responseString],statusMessage);
}];
[request setFailedBlock: ^{
cb2(blockSafeRequest);
}];
[request startAsynchronous];
}

What is the last function in iPhone application lifecycle

Before my application is going to be closed I have to logout user from web service. And I can't find the very last function that is invoked before application die?
-(void)LogoutUser
{
int userId = [[GlobalData sharedMySingleton] getUserId];
NSString *soapMsg =
[NSString stringWithFormat:
#"<?xml version=\"1.0\" encoding=\"utf-8\"?>...", userId
];
NSURL *url = [NSURL URLWithString: #"http://....asmx"];
NSMutableURLRequest *req = [NSMutableURLRequest requestWithURL:url];
NSString *msgLength = [NSString stringWithFormat:#"%d", [soapMsg length]];
[req addValue:#"text/xml; charset=utf-8" forHTTPHeaderField:#"Content-Type"];
[req addValue:#"http://..." forHTTPHeaderField:#"SOAPAction"];
[req addValue:msgLength forHTTPHeaderField:#"Content-Length"];
[req setHTTPMethod:#"POST"];
[req setHTTPBody: [soapMsg dataUsingEncoding:NSUTF8StringEncoding]];
conn = [[NSURLConnection alloc] initWithRequest:req delegate:self];
if (conn)
{
webData = [[NSMutableData data] retain];
}
}
-(void) connection:(NSURLConnection *) connection didReceiveResponse:(NSURLResponse *) response
{
[webData setLength: 0];
}
-(void) connection:(NSURLConnection *) connection didReceiveData:(NSData *) data
{
[webData appendData:data];
}
-(void) connection:(NSURLConnection *) connection didFailWithError:(NSError *) error
{
[webData release];
[connection release];
}
-(void) connectionDidFinishLoading:(NSURLConnection *) connection
{
NSString *theXML = [[NSString alloc]
initWithBytes: [webData mutableBytes]
length:[webData length]
encoding:NSUTF8StringEncoding];
[theXML release];
[connection release];
[webData release];
}
There are two places you'll need to trigger your logout code from, both of which are detailed in the UIApplicationDelegate Protocol Reference documentation.
For pre-iOS 4 devices (and to cover other circumstances) you should use:
- (void)applicationWillTerminate:(UIApplication *)application
As Apple puts it:
For applications that do not support
background execution or are linked
against iOS 3.x or earlier, this
method is always called when the user
quits the application. For
applications that support background
execution, this method is generally
not called when the user quits the
application because the application
simply moves to the background in that
case. However, this method may be
called in situations where the
application is running in the
background (not suspended) and the
system needs to terminate it for some
reason.
However, you'll need to use...
- (void)applicationDidEnterBackground:(UIApplication *)application
...on iOS 4+ devices, as (once again from the Apple docs):
In iOS 4.0 and later, this method is
called instead of the
applicationWillTerminate: method when
the user quits an application that
supports background execution
That said, irrespective of all the above, you'll most likely want to logout of the web service when your app is backgrounded and log back in when it's "woken up" as well. See the above mentioned applicationDidEnterBackground: method and the applicationWillEnterForeground: method documentation for more details.
- (void)applicationDidEnterBackground:(UIApplication *)application {
/*
Use this method to release shared resources, save user data, invalidate timers, and store enough application state information to restore your application to its current state in case it is terminated later.
If your application supports background execution, called instead of applicationWillTerminate: when the user quits.
*/
}
this may be not a last function but. you can do logout here.
For typical apps under iOS 4.x, applicationWillResignActive and perhaps applicationDidEnterBackground will be called both before your app is terminated (at some unknown time in the future), and at other times as well when the app isn't being terminated. However it might be a good idea to log out here, as your app may never get any further CPU run time.
If you have pending network activity, such as trying to logout, you might want to use the multitasking call beginBackgroundTaskWithExpirationHandler: to request a bit of additional time in the background to finish the log out process, such as handshaking with any network callbacks required.

NSOperationQueue and ASIHTTPRequest

I'm writing test cases for a wrapper class written around ASIHTTPRequest. For reasons I can't determine, my test cases complete with failure before the ASIHTTPRequest finishes.
Here's how the program flow works.
Start in my test case.
Init my http engine object, instruct it to create a new list
Create the new ASIHTTPRequest object and set it up.
Add the request to an operation queue.
Wait until that queue is empty
Check to see if my delegate methods were called and fail the test if they weren't.
Now, most of the time everything works fine and the test passes, but some of the time it fails because my delegate methods were called AFTER the operation queue returned control to my wait method.
Test Case
// Set my flags to 'NO'
- (void)setUp {
requestDidFinish = NO;
requestDidFail = NO;
}
- (void)testCreateList {
NSString *testList = #"{\"title\": \"This is a list\"}";
JKEngine *engine = [[JKEngine alloc] initWithDelegate:self];
NSString *requestIdentifier = [engine createList:jsonString];
[self waitUntilEngineDone:engine];
NSString *responseString = responseString_;
[engine release];
GHAssertNotNil(requestIdentifier, nil);
GHAssertTrue(requestDidFinish, nil);
GHAssertTrue([responseString hasPrefix:#"{\"CreateOrEditListResult\""], nil);
}
// Puts the test into a holding pattern until the http request is done
- (void)waitUntilEngineDone:(JKEngine *)engine {
[engine waitUntilFinishedRunning];
}
// The delegate method called on successful completion
- (void)requestFinished:(NSString *)requestIdentifier withResponse:(NSString *)response {
NSLog(#"request did finish");
requestDidFinish = YES;
responseIdentifier_ = [requestIdentifier retain];
responseString_ = [response retain];
}
Engine Code
- (NSString *)createList:(NSString *)list {
ASIHTTPRequest *request = [[ASIHTTPRequest alloc] initWithURL:[NSURL URLWithString:url]];
[request addRequestHeader:#"Content-Type" value:kContentType];
[request setRequestMethod:kPOST];
request.delegate = self;
[request appendPostData:[list dataUsingEncoding:NSUTF8StringEncoding]];
NSString *requestIdentifier = [NSString stringWithNewUUID];
[operationQueue_ addOperation:request];
[operationDictionary_ setObject:request forKey:requestIdentifier];
return requestIdentifier;
}
// This is the ASIHTTPRequest delegate method that's called on success
// but it sometimes isn't called until AFTER the operationQueue finishes running
- (void)requestFinished:(ASIHTTPRequest *)request {
DLog([request responseString]);
BOOL canNotifiyDelegate = [self.delegate respondsToSelector:#selector(requestFinished:withResponse:)];
if (canNotifiyDelegate) {
NSArray *keyArray = [operationDictionary_ allKeysForObject:request];
NSString *requestIdentifier = [keyArray objectAtIndex:0];
[operationDictionary_ removeObjectForKey:requestIdentifier];
if ([keyArray count] != 1) {
ALog(#"It looks like a request was added to the operation dictionary multiple times. There's a bug somewhere.", nil);
}
[self.delegate requestFinished:requestIdentifier withResponse:[request responseString]];
}
}
- (void)waitUntilFinishedRunning {
[operationQueue_ waitUntilAllOperationsAreFinished];
}
This is the way ASIHTTPRequest works. Delegate methods are called on the main thread, and calls to delegates do not block the request thread, so it's perfectly possible your delegates will be called after the queue finishes.
ASIHTTPRequest calls delegate methods on the main thread, by default GH-Unit runs its tests on a background thread. I'm still a little hazy on exactly what was going on, but forcing my network tests to run on the main thread fixed the problem.
I implemented the following method in my network test class.
- (BOOL)shouldRunOnMainThread {
return YES;
}