NSURLConnection Bug when I run It in background.... how can I fix? - iphone

I've this code but I can't understand why I must run this on the main thread.
If I run this in the background It doesn't do the post request. Is it a bug?
How can I solve this problem?
- (void)setRead:(MWFeedItem *)entry
{ [self getToken:YES];
NSString *url=[NSString stringWithFormat:#"https://www.google.com/reader/api/0/edit-tag?a=user/-/state/com.google/read&i=%#&T=%#", entry.identifier, token];
[self postRequestWithURLState:url];
}
- (void)postRequestWithURLState:(NSString *)url
{
NSString *bodyRequest = nil;
NSURL *requestURL = [NSURL URLWithString:url];
NSMutableURLRequest *theRequest = [[NSMutableURLRequest alloc] init];
//NSLog(#"-------------- bodyRequest: %#", bodyRequest);
[theRequest setURL:requestURL];
[theRequest setTimeoutInterval:0.5];
[theRequest setHTTPMethod:#"POST"];
[theRequest setHTTPBody:[bodyRequest dataUsingEncoding:NSASCIIStringEncoding]];
[self.oauthAuthentication authorizeRequest:theRequest];
[NSURLConnection connectionWithRequest:theRequest delegate:self];
[UIApplication sharedApplication].networkActivityIndicatorVisible = NO;
}
this is my call:
-(void)segnaLettura:(MWFeedItem *)item{
[reader setRead:item];
}
- (void) segnaread:(MWFeedItem *)item{
[self performSelectorOnMainThread:#selector(segnaLettura:) withObject:item waitUntilDone:NO];
}

In order for an asynchronous NSURLConnection to function, it requires the thread's runloop to be processed. While new threads automatically get a runloop, it is up to you to run it.
You can learn how to do this in the Threading Programming Guide, and I can explain it more, but most of the time this isn't what you want. Most of the time in iOS, background threads should be managed with NSOperation or GCD. Generally, if you're manually spawning a thread on iOS 4+, you're doing it wrong. There are exceptions, but not often.
The first question here should be "why do I even have a background thread for this?"
If you really need a background thread, then the way you're doing segnaread is probably fine.

Related

UIActivityIndicator not animating

I've been beating my head against a wall with this problem, and need some help.
I am trying to display a UIActivityIndicator while I load data in the background. I'm not sure if this is relevant or not, but I am loading a tableview. The indicator appears, but doesn't spin...unless I touch the screen, or something else happens while loading-like if I receive a text message. Here is the code:
UIActivityIndicatorView *av = [[[UIActivityIndicatorView alloc]initWithActivityIndicatorStyle:UIActivityIndicatorViewStyleWhite] autorelease];
av.frame=CGRectMake(145, 160, 25, 25);
av.tag = 1;
[self.mTableView addSubview:av];
[av startAnimating];
[self performSelectorInBackground:#selector(load) withObject:nil];
I've also tried this:
UIActivityIndicatorView *av = [[[UIActivityIndicatorView alloc]initWithActivityIndicatorStyle:UIActivityIndicatorViewStyleWhite] autorelease];
av.frame=CGRectMake(145, 160, 25, 25);
av.tag = 1;
[self.view addSubview:av];
[av startAnimating];
[self performSelectorInBackground:#selector(load) withObject:nil];
and have tried it with commenting out the last line-So not having the background thread run. I have tried both versions of the code in my viewDidLoad and viewDidAppear methods.
Any ideas?
Edit Here is my load method
- (void)load {
NSString *post = [NSString stringWithFormat:#"id[]=%#", [ids objectAtIndex:0]];
for(int i = 1; i < ids.count; i++){
post = [NSString stringWithFormat:#"%#&id[]=%#", post, [ids objectAtIndex:i]];
}
NSURL *url=[NSURL URLWithString:#"http://myurl/instructions"];
NSData *postData = [post dataUsingEncoding:NSASCIIStringEncoding allowLossyConversion:YES];
NSString *postLength = [NSString stringWithFormat:#"%d", [postData length]];
NSMutableURLRequest *request = [[[NSMutableURLRequest alloc] init] autorelease];
[request setURL:url];
[request setHTTPMethod:#"POST"];
[request setValue:postLength forHTTPHeaderField:#"Content-Length"];
[request setValue:#"application/x-www-form-urlencoded" forHTTPHeaderField:#"Content-Type"];
[request setHTTPBody:postData];
/* when we user https, we need to allow any HTTPS cerificates, so add the one line code,to tell teh NSURLRequest to accept any https certificate, i'm not sure about the security aspects
*/
//[NSURLRequest setAllowsAnyHTTPSCertificate:YES forHost:[url host]];
NSError *error;
NSURLResponse *response;
NSData *urlData=[NSURLConnection sendSynchronousRequest:request returningResponse:&response error:&error];
NSString *data=[[NSString alloc]initWithData:urlData encoding:NSUTF8StringEncoding];
[self parseData:data];
[spinner stopAnimating];
}
You didn't include enough code to see where you're stopping the UIActivityIndicator animation and what's going on while you're displaying it, but the problem almost certainly is one of the following:
1) You're expecting your code to wait for some asynchronous method, which is causing your activity indicator to get shut off prematurely,
or
2) Both the "background" task and the UIActivityIndicator are running on the same thread, and your "background" task is monopolizing the thread to the point where the activity indicator doesn't get enough time to animate.
This post provides an example of how to push the UIActivityIndicator into its own thread:
Activity Indicator doesn't spin
The UI needs time to load, and can't start the animation immediately. By starting it with an NSTimer in the viewDidLoad method, the problem was solved.
timer = [NSTimer scheduledTimerWithTimeInterval:1 target:self selector:#selector(spin) userInfo:nil repeats:NO];
[[NSRunLoop mainRunLoop] addTimer:self.timer forMode:UITrackingRunLoopMode];
and the scan method:
spinner = [[[UIActivityIndicatorView alloc]initWithActivityIndicatorStyle:UIActivityIndicatorViewStyleWhite] autorelease];
spinner.center = self.view.center;
spinner.hidesWhenStopped = YES;
[spinner startAnimating];
[self.view addSubview:spinner];
if ([spinner isAnimating]) NSLog(#"animating");
else NSLog(#"not animating");
[self performSelectorInBackground:#selector(load) withObject:nil];
Hopefully this helps.
It's not clear where you're adding the activity indicator but, if it's not on the main thread, then UI calls to it may not work. I typically set up a separate routine to start & stop my activity monitors, so I can performSelectorInMainThread: them.
Not related to the activity indicator, but your load method rewrites the post variable in a loop. I think you intend to concatenate:
post = [post stringByAppendingFormat: ...]
Otherwise your web service will see only the last param.
Also, once you get your spinner animating, the next problem you'll have is that it won't stop because stopAnimating is being called off the main thread.

Refactoring my code for asynchronous NSURLConnection

There are problems with using synchronous requests. The problem is that I am not really sure how to refactor the code below to use an asynchronous request to accomplish the following two tasks:
Use a UIActivityIndicatorView subview to notify of active fetching;
Implement a Reachability check before the connection request to ensure that there is an Internet connection and that the host is available, i.e. reachabilityWithHostName:#"wwww.mywebhostingisterrible.com".
Currently I have a Fetcher class with a +(Fetcher *)sharedFetcher singleton that is used in all of my view controllers to fetch data. Currently all data is fetched using the following code:
- (NSString *)stringWithUrl:(NSURL *)url {
NSMutableURLRequest *urlRequest = [NSMutableURLRequest requestWithURL:url cachePolicy:NSURLRequestReloadIgnoringCacheData timeoutInterval:30];
NSData *urlData;
NSURLResponse *response;
NSError *error;
urlData = [NSURLConnection sendSynchronousRequest:urlRequest
returningResponse:&response
error:&error];
return [[NSString alloc] initWithData:urlData encoding:NSUTF8StringEncoding];
}
A method that fetches looks like this:
-(NSArray *)fetchDogFoodListing {
NSString *urlString = [NSString stringWithFormat:#"%#query/dogFood/%#/%lli/%#/%#",self.connectionUrl,self.queryCriteria.dogFoodName,self.queryCriteria.dogFoodBrandId,self.queryCriteria.dogFoodIngredients,self.queryCriteria.dogFoodBrand];
NSString *json = [self stringWithUrl:[NSURL URLWithString:urlString]];
return [self.factory buildIKDogFoodArrayWithJSON:json];
}
and a call from the UIViewController in -(void)viewDidLoad looks like this:
self.dogFoods = [[IKFetcher sharedFetcher] fetchDogFoodListing];
I have thought about implementing a delegate in this situation but I am very shaky on how delegation ought to work. I'm not even sure which way it should go. Should the Fetcher have a FetcherDelegate that can notify the view controller of when it is done, so the UI can update? Should the view controller have a delegate so that the fetcher can update the view when stuff is done?
I'm sure based on my questions you can see my inexperience with delegation, so I would appreciate any help.
You could do something like this
dispatch_async(queue, ^{
NSMutableURLRequest *urlRequest = [NSMutableURLRequest requestWithURL:url cachePolicy:NSURLRequestReloadIgnoringCacheData timeoutInterval:30];
NSData *urlData;
NSURLResponse *response;
NSError *error;
urlData = [NSURLConnection sendSynchronousRequest:urlRequest
returningResponse:&response
error:&error];
dispatch_async(dispatch_get_main_queue(), ^{
//.. do stuff with data here...
}
}
or you could just do the sendSynchronousRequest code inside a dispatch_sync if you want the connection to be synchronous, though you should really should be as asynchronous as possible.
you may want to look at the brilliant framework by Mugunth Kumar:
http://blog.mugunthkumar.com/products/ios-framework-introducing-mknetworkkit
I plan on using this for NSURLConnection in my future projects

How can I use NSURLConnection to make connection in background

In one of my previous post someone suggest me to use the
NSURLConnection *conn = [NSURLConnection connectionWithRequest:theRequest delegate:self];
To make connection in a new thread to keep GUI working without freezing during the connection.
But It now I can realize that this doesn't work.
This is the whole method.
- (void)postRequestWithURLState:(NSString *)url
{
NSString *bodyRequest = nil;
NSURL *requestURL = [NSURL URLWithString:url];
NSMutableURLRequest *theRequest = [[NSMutableURLRequest alloc] init];
//NSLog(#"-------------- bodyRequest: %#", bodyRequest);
[theRequest setURL:requestURL];
[theRequest setTimeoutInterval:2.0];
[theRequest setHTTPMethod:#"POST"];
[theRequest setHTTPBody:[bodyRequest dataUsingEncoding:NSASCIIStringEncoding]];
[self.oauthAuthentication authorizeRequest:theRequest];
NSURLConnection *conn = [NSURLConnection connectionWithRequest:theRequest delegate:self];
self.web = conn;
}
That is part of a class called greader. So I use this method in this mode.
reader = [[greader alloc] init];
[reader setDelegate:self];
Than I call it [reader postRequestWithURLState:url].
But in this way it still freeze my app for some seconds. How can I execute this action without freezing my app for some seconds?
This is strange. I cannot see why this method would freeze your UI.
One simple solution is to call the method in separate thread. Make the reader a property (or pass it to the method below):
-(void)postReaderRequest:(NSURL)url {
[self.reader postRequestWithURLState:url];
}
and then
[self performSelectorInBackground:#selector(postReaderRequest)
withObject:[NSArray arrayWithObject:url]];
If the result is the same, it must have been something else.
Try using NSThread to execute your NSURLConnection

Why does my app freeze when I make a POST request with NSURLRequest?

I have a method like this. It works fine when my device is connected through wifi network, but when it is connected through 3G network it freezes my app for some seconds.
So, because it is an interactive app, when it does some various post request it must continuing running so the user can continue to use the app.
Any solution for this ?
I tried reducing the [theRequest setTimeoutInterval:2.0]; but that didn't fix my problem.
// post request
- (void)postRequestWithURL:(NSString *)url
body:(NSString *)body
contentType:(NSString *)contentType
options:(NSDictionary *)dict
{
// set request
NSURL *requestURL = [NSURL URLWithString:url];
NSMutableURLRequest *theRequest = [[NSMutableURLRequest alloc] init];
if([dict count] > 0)
{
for (id key in dict) {
NSLog(#"[theRequest addValue:%# forHTTPHeaderField:%#]", [dict valueForKey:key], key);
[theRequest addValue:[dict valueForKey:key] forHTTPHeaderField:key];
}
}
if (contentType != nil) {
[theRequest addValue:contentType forHTTPHeaderField:#"Content-type"];
}
[theRequest setURL:requestURL];
[theRequest setTimeoutInterval:2.0];
[theRequest setHTTPMethod:#"POST"];
[theRequest setHTTPBody:[body dataUsingEncoding:NSASCIIStringEncoding]];
[self.oauthAuthentication authorizeRequest:theRequest];
// make request
//responseData = [NSURLConnection sendSynchronousRequest:theRequest
// returningResponse:&response
// error:&error];
NSURLConnection *conn = [[NSURLConnection alloc] initWithRequest:theRequest delegate:self startImmediately:YES];
self.web = conn;
[conn release];
NSLog(#"########## REQUEST URL: %#", url);
// request and response sending and returning objects
}
It's freezing your app because you have told it to. You have passed YES into startImmediately. This means that it will start the connection on that thread and wait until it's finished. I guess the thread you're doing this on will be the main thread - the one that also handles the ui etc :)
You need to use something like connectionWithRequest:delegate: - this will run the request in the background and tell you when it's done.
PS The reason you didn't spot the bug in wifi is because the data was sent so fast you couldn't notice the pause in your app :)
PPS The reason the timeout didn't fix it is because the request wasn't timing out - it was just getting data very slowly :)
EDIT
Something like this :
self.web = [NSURLConnection connectionWithRequest:request delegate:self];

How do I use UIActivity Indicator with NSUrlConnection

I'm using NSUrlConnection for accessing data from server.
I want that during fetching of the data from the server the user interactivity is stopped as well as the code execution.
And I read more and more on Stack Overflow or Google, but I'm not get appropriate solution.
Please see the code which I'm using.
The first time when the user clicks to access the data from the server I start the activity indicator.
[activity startAnimating];
NSString *post =
[[NSString alloc] initWithFormat:#"Email=%#&Pass=%#",Email,pass];
NSData *postData = [post dataUsingEncoding:NSASCIIStringEncoding allowLossyConversion:YES];
NSString *postLength = [NSString stringWithFormat:#"%d", [postData length]];
NSURL *url = [NSURL URLWithString:#"http://server.in/LoginStatus.php"];
NSMutableURLRequest *theRequest = [NSMutableURLRequest requestWithURL:url];
[theRequest setHTTPMethod:#"POST"];
[theRequest setValue:postLength forHTTPHeaderField:#"Content-Length"];
[theRequest setHTTPBody:postData];
NSURLConnection *theConnection = [[NSURLConnection alloc] initWithRequest:theRequest delegate:self];
if( theConnection )
{
webData = [[NSMutableData data] retain];
}
else
{
}
and in the connectionDidFinishLoading function I stop the activity indicator.
The problem is that when I check the returned data then it displays the previous result. When clicked again it display the proper result.
Please suggest if you have any idea.
Try run the UIActivityIndicator and the NSURLConnection in different threads. I had the problem that the Indicator didn't show up when the Connection was alive. Putting both in seperate Threads solved it.
Just use
[activity startAnimating];
within your method
now try to make the NSURL Connection in some thread.And call the thread from your method
and once the connection get Finish use
[activity stopAnimating];
Will definitely work