Long Polling and applicationDidEnterBackground: - iphone

I'm writing a very Simple Chat Application and would like to know how to suspend the long polling selector when the Application enters background.
Currently, I have a Chatroom class (A UIView) which handles the long polling like so:
-(void)startPolling
{
[self performSelectorInBackground:#selector(longPoll) withObject: nil];
}
- (void) longPoll {
//Poll the Requested URL...
NSData* responseData = [NSURLConnection sendSynchronousRequest:request
returningResponse:&response error:&error];
[self performSelectorOnMainThread:#selector(dataReceived:)
withObject:responseData waitUntilDone:YES];
[self performSelectorInBackground:#selector(longPoll) withObject: nil];
}
-(void) dataReceived: (NSData*) data
{
//Reload my Tableview etc..
}
How do I use applicationDidEnterBackground: to suspend the longPoll selector until the application comes back to the foreground? Or is this automatically done by the Application Delegate?

The request will automatically be suspended. It's not guaranteed that the request will necessarily succeed after being resumed, so you'll have to handle errors, but it shouldn't break.
Note that there are probably better ways to write this than using performSelectorInBackground:, which always spins up a new hardware thread. For starters, it's probably better to simply loop inside longPoll instead of starting a new thread for the new request.

Related

Asynchronous Process Giving Me Trouble

I want to return information from a turn based game from the game center servers, which is all fine, but I want the player alias which is acquired using the asynchronous method:
[GKPlayer loadPlayersForIdentifiers:singleOpponentArray withCompletionHandler:^(NSArray *players, NSError *error) {
GKPlayer *returnedPlayer = [players objectAtIndex:0];
NSString *aliasToAdd = [NSString stringWithString:returnedPlayer.alias];
NSString *idToAdd = [NSString stringWithString:returnedPlayer.playerID];
NSDictionary *dictionaryToAddToAliasArray = [NSDictionary dictionaryWithObjects:[NSArray arrayWithObjects:aliasToAdd, idToAdd, nil] forKeys:[NSArray arrayWithObjects:#"alias", #"id", nil]];
[self.aliasArray addObject:dictionaryToAddToAliasArray];
}];
But the UI uses this information and it does't arrive in time. How can I make that method execute synchronously on the main thread?
Thanks.
Any UI related code must execute on the main thread.
If your app must wait for the asynchronous call to return, then first disable the UI. For example, set userInteractionEnabled = NO on your UIView.
Then, when the asynchronous methods returns, re-enable the UIView.
In the meantime, display some sort of activity indicator, e.g. UIActivityIndicatorView.
Of course, only do the above in a case where you can't perform the task in the background. Never needlessly block the UI. I'm sure you know that already of course but it's worth restating for any people new to the platform that might be reading this.
To invoke on the main thread, use one of the variants of NSObject's performSelectorOnMainThread method. Or, alternatively, queue it on gcd using the main queue by calling the dispatch_get_main_queue function.
You can do this using GCD functions:
// Show an UILoadingView, etc
[GKPlayer loadPlayersForIdentifiers:singleOpponentArray
withCompletionHandler:^(NSArray *players, NSError *error) {
// Define a block that will do your thing
void (^doTheThing)(void) = ^(void){
// this block will be run in the main thread....
// Stop the UILoadingView and do your thing here
};
// Check the queue this block is called in
dispatch_queue_t main_q = dispatch_get_main_queue();
dispatch_queue_t cur_q = dispatch_get_current_queue();
if (main_q != cur_q) {
// If current block is not called in the main queue change to it and then do your thing
dispatch_async(main_q, doTheThing);
} else {
// If current block is called in the main queue, simply do your thing
doTheThing();
}
}];

NSURLConnection Async Request and display a "Loading" alert view or subview while fulfilling request

I'm trying to figure out the optimal way to perform an NSURLConnection Async Request and have the UI shielded with an alert view while the request is being fulfilled. I've had a lot of trouble getting this to work with a synchronous request because I could not figure out how to use the multi threading features or operation queues effectively with a synchronous request, so I figure this is the best way to go. Pseudo code or actual code is fine, I just need to know which direction to go. So far I figure:
Create a UIAlertView property
Create a void function that initiates the NSURLConnection, and display the view right after it initiates
Use the delegate method to close the AlertView window -(void)connectionDidFinishLoading or something like that.
Is it this simple, or am I missing something?
Probably the easiest way to do it is to use the UIApplication.networkActivityIndicatorVisible property, and do a sync request in a background thread.
-(void)loadURLInBackground:(NSURL*)url {
NSURLRequest* req = [NSURLRequest requestWithURL:url cachePolicy:NSURLRequestUseProtocolCachePolicy timeoutInterval:10.0];
NSURLResponse* response = nil;
NSError* err = nil;
[UIApplication sharedApplication].networkActivityIndicatorVisible = YES;
NSData* data = [NSURLConnection sendSynchronousRequest:req returningResponse:&response error:&err];
[UIApplication sharedApplication].networkActivityIndicatorVisible = NO;
if( data != nil ) {
[self performSelectorOnMainThread:#selector(processData:) withObject:data waitUntilDone:NO];
} else {
[self performSelectorOnMainThread:#selector(processError:) withObject:err waitUntilDone:NO];
}
}
Use [self performSelectorInBackground:#selector(loadURLInBackground:) withObject:url]; to call the method, then just implement processData: and processError:.
You do not want to use a UIAlertView - that is a modal dialog.
You want to use something like UIActivityIndicatorView to show the spinner while the background activity is going on.
Then, as you say, your delegate method can stopAnimating the activity indicator view.
If you want to show a message like "Downloading ...", then you can wrap your activity indicator inside another view, display that view, and remove it when the delegate calls back.

Facebook request thread problem

Alright I am kind of new to threads so I have this question.
I am trying to get information of friends from Facebook, and I do not want to do that on the main thread. but for some reason when the request is not on the main thread the callback does never get called and I don't know why!
I have an Array with all the ID's from my friends and loop through this array and create an object of my custom class Friend (which gets all the information I need) with every ID.
I add this object to an array.
This friend object makes an request to Facebook and handles the response to get the data I want.
here is the code:
dispatch_async(dispatch_get_global_queue(0, 0), ^(void) {
[self getFBFriendsInfo];
});
-(void)getFBFriendsInfo{
if (friendsInfoArray) {
[friendsInfoArray removeAllObjects];
}
else{
friendsInfoArray =[[NSMutableArray alloc]init];
}
for (int i=0; i<[UDID count]; i++) {
NSString *udid = [UDID objectAtIndex:i];
FriendsInfo *friend =[[FriendsInfo alloc] initWithFacebook:facebook andUdid:udid];
[friendsInfoArray addObject:friend];
[friend release];
}
dispatch_async(dispatch_get_main_queue(), ^(void) {
[delegate friendsInfosAvailable:friendsInfoArray];
});
}
and in my custom class I do this:
[facebook requestWithGraphPath:udid andDelegate:self];
with this the callback's are never called! only if I do the request on the main thread it works:
dispatch_async(dispatch_get_main_queue(), ^(void) {
[facebook requestWithGraphPath:udid andDelegate:self];
});
This is why on a different thread you get no response:
Facebook will use NSURLConnection to perform requests. For the connection to work correctly the calling thread’s run loop must be operating in the default run loop mode (Read NSURLConnection class reference). When you use dispatch_async() there is no run loop operating in the default run loop mode (unless you are on the main dispatch queue, therefore running on the main thread). Hence, I figure the request isn't even sent (You can check that sniffing your network if you wish.).
So, in a nutshell, you should make your request on the main thread; as it is asynchronous, it wont freeze your app. Then, if the processing of the response is too expensive, handle it in the background.
I really hope this helps.
My best.

Using AsyncSocket with secondary threads on the iPhone

I use AsyncSocket on the iPhone to communicate with a server. AsyncSocket is based on run loops but my app is based on threads. That means, I start a new thread to write data and wait until a response is received on the same thread. But I can't call an AsyncSocket's method directly from another thread, I have to use:
[self performSelectorOnMainThread:#selector(writeSomeData:) withObject:dataToWrite waitUntilDone:YES];
It does work, but I cannot get the response from my method "writeSomeData:" called this way, because performSelectorOnMainThread returns nothing.
The method writeSomeData: does something like this:
-(NSData *)writeData:(NSData *)dataToWrite {
dataReceived = nil; // AsyncSocket writes data to this variable
[asyncSocket writeData:dataToWrite withTimeout:-1 tag:0];
[asyncSocket readDataToData:[#"<EOF" dataUsingEncoding:NSUTF8StringEncoding] withTimeout:-1 tag:0];
int counter = 0;
while (dataReceived == nil && counter < 5) {
// runLoop is [NSRunLoop currentRunloop]
[runLoop runMode:NSDefaultRunLoopMode beforeDate:[NSDate dateWithTimeIntervalSinceNow:0.3]];
++counter;
}
return [dataReceived copy];
}
I could get the response by accessing the class variable "dataReceived", but it's content is changed at this time.
Can anybody tell me how to use AsyncSocket (or generally, how to deal with run loop based classes) on separate threads, so that if I call a method of that class it blocks until the method is executed and a response is received?
Thank you.
Try using GCD(Grand Central Dispatch) to write your data on a separate thread and than come back to the main thread the moment that the data was written. You could do it like this:
// call this on the main thread
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0), ^{
NSData *data = [self writeData:dataToWrite];
dispatch_async(dispatch_get_main_queue(), ^{
// do something with the data on the main thread.
});
});
I hope something like this can help you...

NSOperations or NSThread for bursts of smaller tasks that continuously cancel each other?

I would like to see if I can make a "search as you type" implementation, against a web service, that is optimized enough for it to run on an iPhone.
The idea is that the user starts typing a word; "Foo", after each new letter I wait XXX ms. to see if they type another letter, if they don't, I call the web service using the word as a parameter.
The web service call and the subsequent parsing of the result I would like to move to a different thread.
I have written a simple SearchWebService class, it has only one public method:
- (void) searchFor:(NSString*) str;
This method tests if a search is already in progress (the user has had a XXX ms. delay in their typing) and subsequently stops that search and starts a new one. When a result is ready a delegate method is called:
- (NSArray*) resultsReady;
I can't figure out how to get this functionality 'threaded'.
If I keep spawning new threads each time a user has a XXX ms. delay in the typing I end up in a bad spot with many threads, especially because I don't need any other search, but the last one.
Instead of spawning threads continuously, I have tried keeping one thread running in the background all the time by:
- (void) keepRunning {
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
SearchWebService *searchObj = [[SearchWebService alloc] init];
[[NSRunLoop currentRunLoop] run]; //keeps it alive
[searchObj release];
[pool release];
}
But I can't figure out how to access the "searchFor" method in the "searchObj" object, so the above code works and keeps running. I just can't message the searchObj or retrieve the resultReady objects?
Hope someone could point me in the right direction, threading is giving me grief:)
Thank you.
Ok, I spend the last 8 hours reading up on every example out there.
I came to realize that I would have to do some "Proof of Concept" code to see if there even would be a speed problem with building a new thread for "each" keystroke.
It turns out that using NSOperation and NSOperationQueue is more than adequate, both in terms of speed and especially in terms of simplicity and abstraction.
Is called after each keystroke:
- (void) searchFieldChanged:(UITextField*) textField {
[NSObject cancelPreviousPerformRequestsWithTarget:self];
NSString *searchString = textField.text;
if ([searchString length] > 0) {
[self performSelector:#selector(doSearch:) withObject:textField.text afterDelay:0.8f];
}
}
This is mainly to stop the code form initiating a search for keystrokes that are less than 800 ms. apart.
(I would have that a lot lower if it where not for the small touch keyboard).
If it is allowed to time out, it is time to search.
- (void) doSearch:(NSString*) searchString {
[queue cancelAllOperations];
ISSearchOperation *searchOperation = [[ISSearchOperation alloc] initWithSearchTerm:searchString];
[queue addOperation:searchOperation];
[searchOperation release];
}
Cancel all operations that is currently in the queue. This is called every time a new search is
started, it makes sure that the search operation already in progress gets closed down in an orderly fashion, it also makes sure that only 1 thread is ever in a "not-cancelled" state.
The implementation for the ISSearchOperation is really simple:
#implementation ISSearchOperation
- (void) dealloc {
[searchTerm release];
[JSONresult release];
[parsedResult release];
[super dealloc];
}
- (id) initWithSearchTerm:(NSString*) searchString {
if (self = [super init]) {
[self setSearchTerm:searchString];
}
return self;
}
- (void) main {
if ([self isCancelled]) return;
[self setJSONresult:/*do webservice call synchronously*/];
if ([self isCancelled]) return;
[self setParsedResult:/*parse JSON result*/];
if ([self isCancelled]) return;
[self performSelectorOnMainThread:#selector(searchDataReady:) withObject:self.parsedResult waitUntilDone:YES];
}
#end
There are two major steps, the downloading of the data from the web service and the parsing.
After each I check to see if the search has been canceled by [NSOperationQueue cancelAllOperations] if it has, then we return and the object is nicely cleaned up in the dealloc method.
I will probably have to build in some sort of time out for both the web service and the parsing, to prevent the queue from choking on a KIA object.
But for now this is actually lightning fast, in my test I am searching an 16.000 entries dictionary and having Xcode NSLog it to the screen (slows things down nicely), each 800 ms. I issue a new search string via a timer and thereby canceling the old before it has finished its NSLog results to screen loop.
NSOperationQueue handles this with no glitches and never more that a few ms. of two threads being executed. The UI is completely unaffected by the above tasks running in the background.