In my project I need to send data to server, for that I've used the following code to achieve the task:
- (void)sendJSONToServer:(NSString *) jsonString
{
// Create a new NSOperationQueue instance.
operationQueue = [NSOperationQueue new];
//
// Create a new NSOperation object using the NSInvocationOperation subclass to run the operationQueueTask method
NSInvocationOperation *operation = [[NSInvocationOperation alloc] initWithTarget:self
selector:#selector(operationQueueTask:)
object:jsonString];
// Add the operation to the queue and let it to be executed.
[operationQueue addOperation:operation];
}//End of sendJSONToServer method
-(void) operationQueueTask:(NSString *) jsonString
{
//NSOperationQueue *remoteResultQueue = [[NSOperationQueue alloc] init];
dispatch_queue_t myQueue = dispatch_queue_create("SERVER_QUEUE",NULL);
dispatch_async(myQueue, ^{
// Performing long running process
// Sending json data to server asynchronously
NSData *postData = [jsonString dataUsingEncoding:NSASCIIStringEncoding allowLossyConversion:YES];
NSString *postLength = [NSString stringWithFormat:#"%lu", (unsigned long)[jsonString length]];
NSMutableURLRequest *request = [[NSMutableURLRequest alloc] initWithURL:[NSURL URLWithString:#"MY_URL_eg_http://www.example.com"]];
[request setHTTPMethod:#"POST"];
[request setValue:postLength forHTTPHeaderField:#"Content-Length"];
[request setValue:#"application/json" forHTTPHeaderField:#"Content-Type"];
[request setHTTPBody:postData];
[NSURLConnection sendAsynchronousRequest:request queue:operationQueue completionHandler:^(NSURLResponse *response, NSData *data, NSError *error)
{
NSLog(#"Response is:%#",[[NSString alloc] initWithData:data encoding:NSASCIIStringEncoding]);
}];
dispatch_async(dispatch_get_main_queue(), ^{
// Update the UI
NSLog(#"Thread Process Finished");
});
});
}//End of operationQueueTask method
By the above code I'm able to send data and get response.
But when there is no internet the data will not be sent to server. How to handle this situation based on the response we get.
Let's say we get response success on fair condition ans false on worst condition.
UPDATED CODE FOR RETRIES
-(id)init
{
self = [super init];
if (self != nil)
{
//initialize stuffs here
pendingOperationQueue = [[NSMutableArray alloc] init];
operationQueue = [NSOperationQueue new];
}
return self;
}//End of init method
- (void)sendJSONToServer:(NSString *) jsonString
{
NSOperation *operation = [[NSInvocationOperation alloc] initWithTarget:self selector:#selector(operationQueueTask:) object:[NSString stringWithString:[pendingOperationQueue objectAtIndex:0]]];
[operation start];
}//End of sendJSONToServer method
-(void) operationQueueTask:(NSString *) jsonString
{
//NSOperationQueue *remoteResultQueue = [[NSOperationQueue alloc] init];
dispatch_queue_t myQueue = dispatch_queue_create("SERVER_QUEUE",NULL);
dispatch_async(myQueue, ^{
// Performing long running process
// Sending json data to server asynchronously
NSData *postData = [jsonString dataUsingEncoding:NSASCIIStringEncoding allowLossyConversion:YES];
NSString *postLength = [NSString stringWithFormat:#"%lu", (unsigned long)[jsonString length]];
NSMutableURLRequest *request = [[NSMutableURLRequest alloc] initWithURL:[NSURL URLWithString:#"MY_URL_http://www/example.com"]];
[request setHTTPMethod:#"POST"];
[request setValue:postLength forHTTPHeaderField:#"Content-Length"];
[request setValue:#"application/json" forHTTPHeaderField:#"Content-Type"];
[request setHTTPBody:postData];
[NSURLConnection sendAsynchronousRequest:request queue:operationQueue completionHandler:^(NSURLResponse *response, NSData *data, NSError *error)
{
NSLog(#"Response is:%#",[[NSString alloc] initWithData:data encoding:NSASCIIStringEncoding]);
if([[[NSString alloc] initWithData:data encoding:NSASCIIStringEncoding] rangeOfString:#"true"].location == NSNotFound)
{
// Add the operation to the queue and let it to be executed.
NSLog(#"Failed To Add To Server, Rerunning the task");
}
else
{
NSLog(#"Successfully Added To Server");
NSLog(#"ADDED_DATA_TO_SERVER: %#", jsonString);
if([pendingOperationQueue count] > 0)
{
[pendingOperationQueue removeObjectAtIndex:0];
if([pendingOperationQueue count] > 0)
{
NSOperation *operation = [[NSInvocationOperation alloc] initWithTarget:self selector:#selector(operationQueueTask:) object:[NSString stringWithString:[pendingOperationQueue objectAtIndex:0]]];
[operation start];
}
}
}
}];
});
}//End of operationQueueTask method
Heads up! This is a long answer. TL;DR: You can't re-run an NSOperation, but you can design your classes and methods to make it easy to retry requests.
First a quick answer to your title question: you can't re-run an NSOperation, they're not designed to do that. From the docs:
An operation object is a single-shot object — that is, it executes its
task once and cannot be used to execute it again.
With that out of the way, lets take a look at what you're currently doing and clean it up a bit so that re-using it is easier. There's a ton of async stuff going on in there that you don't need; I'll go through it piece by piece.
Let's start with your operationQueueTask: method. The first thing you do in the method is:
dispatch_queue_t myQueue = dispatch_queue_create("SERVER_QUEUE",NULL);
That means that every time that method is called, you're creating a new dispatch queue. While you can do that if you really want to, that's not what dispatch queues are really designed for. A better idea would be to use one of the background queues that are already available:
dispatch_queue_t myQueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_BACKGROUND, 0);
Next you are dispatching a block asynchronously to that queue. That block:
Sets up your NSMutableURLRequest.
Calls [NSURLConnection sendAsynchronousRequest:...].
Dispatches another block (which has a comment in it about updating the UI) to the main queue.
1 and 2 are fine, I don't see anything you need to change there. 3, however, is problematic because of where that dispatch is being called. The way you have it setup now, NSURLConnection will fire off its asynchronous request and then, before that even has a chance to run, you fire off the block to the main queue to update the UI. What you need to do instead is fire off that block in the completion handler passed to [NSURLConnection sendAsynchronousRequest:...]. Like so:
[NSURLConnection sendAsynchronousRequest:request queue:operationQueue completionHandler:^(NSURLResponse *response, NSData *data, NSError *error)
{
NSLog(#"Response is:%#",[[NSString alloc] initWithData:data encoding:NSASCIIStringEncoding]);
dispatch_async(dispatch_get_main_queue(), ^{
// Update the UI
NSLog(#"Thread Process Finished");
});
}];
Now, notice the name of the method you're calling on NSURLConnection? sendAsynchronousRequest:. It actually handles queuing the request on a background queue for you. Which mean, you don't actually need (or want) all the dispatch_* stuff at the beginning of this method. With that in mind, we can reduce it down to:
-(void) operationQueueTask:(NSString *) jsonString
{
NSData *postData = [jsonString dataUsingEncoding:NSASCIIStringEncoding allowLossyConversion:YES];
NSString *postLength = [NSString stringWithFormat:#"%lu", (unsigned long)[jsonString length]];
NSMutableURLRequest *request = [[NSMutableURLRequest alloc] initWithURL:[NSURL URLWithString:#"MY_URL_eg_http://www.example.com"]];
[request setHTTPMethod:#"POST"];
[request setValue:postLength forHTTPHeaderField:#"Content-Length"];
[request setValue:#"application/json" forHTTPHeaderField:#"Content-Type"];
[request setHTTPBody:postData];
[NSURLConnection sendAsynchronousRequest:request queue:operationQueue completionHandler:^(NSURLResponse *response, NSData *data, NSError *error)
{
NSLog(#"Response is:%#",[[NSString alloc] initWithData:data encoding:NSASCIIStringEncoding]);
dispatch_async(dispatch_get_main_queue(), ^{
// Update the UI
NSLog(#"Thread Process Finished");
});
}];
} //End of operationQueueTask method
Now, on to your sendJSONToServer: method. You're doing a similar thing here that you did at the start of operationQueueTask:: you're creating a new NSOperationQueue each time it runs; that's also not needed (nor typically wanted). What you should probably be doing is creating that operationQueue when your class is initialized (it looks like it's already an instance variable on your class, so you're good there):
// NOTE: I'm just using a default initializer here; if you already have an initializer, use that instead
- (instancetype)init {
if (self = [super init]) {
operationQueue = [NSOperationQueue new];
}
return self;
}
That gets rid of your first line. Next, you're creating an NSInvocationOperation which calls operationQueueTask: and then adding it to your operationQueue. Since you've been re-creating your operationQueue every time, I'm going to assume that it isn't used for anything other than these server requests. In that case, you actually don't need to do this on your operationQueue at all because, as we discovered in the previous method, NSURLConnection is already handling all the background threading for you. In that case, we can actually just copy the code from operationQueueTask: to sendJSONToServer: and get rid of operationQueueTask: altogether. That makes it look like:
- (void)sendJSONToServer:(NSString*)jsonString {
NSData *postData = [jsonString dataUsingEncoding:NSASCIIStringEncoding allowLossyConversion:YES];
NSString *postLength = [NSString stringWithFormat:#"%lu", (unsigned long)[jsonString length]];
NSMutableURLRequest *request = [[NSMutableURLRequest alloc] initWithURL:[NSURL URLWithString:#"MY_URL_eg_http://www.example.com"]];
[request setHTTPMethod:#"POST"];
[request setValue:postLength forHTTPHeaderField:#"Content-Length"];
[request setValue:#"application/json" forHTTPHeaderField:#"Content-Type"];
[request setHTTPBody:postData];
[NSURLConnection sendAsynchronousRequest:request queue:operationQueue completionHandler:^(NSURLResponse *response, NSData *data, NSError *error)
{
NSLog(#"Response is:%#",[[NSString alloc] initWithData:data encoding:NSASCIIStringEncoding]);
dispatch_async(dispatch_get_main_queue(), ^{
// Update the UI
NSLog(#"Thread Process Finished");
});
}];
}
Note: We still need to keep operationQueue around since we pass it to [NSURLConnection sendAsynchronousRequest:... as the queue that it should run on.
So, how do we go about retrying the request when it fails? The simplest method is to add a recursive function that calls itself when the request fails. You'll pass this method the jsonString you want to send and the maximum number of times it should try to send it before it gives up for good.
To facilitate that, lets make one more change to you existing function: instead of handling the completion block inside the function, lets make the completion block a parameter you pass to the function so that it can be processed elsewhere.
- (void)sendJSONToServer:(NSString*)jsonString withCompletionHandler:(void (^)(NSURLResponse *response, NSData *data, NSError *connectionError))completionHandler {
NSData *postData = [jsonString dataUsingEncoding:NSASCIIStringEncoding allowLossyConversion:YES];
NSString *postLength = [NSString stringWithFormat:#"%lu", (unsigned long)[jsonString length]];
NSMutableURLRequest *request = [[NSMutableURLRequest alloc] initWithURL:[NSURL URLWithString:#"MY_URL_eg_http://www.example.com"]];
[request setHTTPMethod:#"POST"];
[request setValue:postLength forHTTPHeaderField:#"Content-Length"];
[request setValue:#"application/json" forHTTPHeaderField:#"Content-Type"];
[request setHTTPBody:postData];
[NSURLConnection sendAsynchronousRequest:request queue:operationQueue completionHandler:completionHandler];
}
Now, lets build that recursive function. I'll call it:
- (void)sendJSONToServer:(NSString*)jsonString withRetryAttempts:(NSUInteger)retryTimes;
The basic flow will be:
Check if retryTimes is greater than 0
If it is, attempt to send the request to the server
When the request finishes, check the response for success
If successful, update the UI on the main queue
If not successful, subtract one from retryTimes and call this function again
That looks something like:
- (void)sendJSONToServer:(NSString*)jsonString withRetryAttempts:(NSUInteger)retryTimes {
if (retryTimes > 0) {
[self sendJSONToServer:jsonString withCompletionHandler:^(NSURLResponse *response, NSData *data, NSError *error) {
NSLog(#"Response is:%#",[[NSString alloc] initWithData:data encoding:NSASCIIStringEncoding]);
if (/* check response to make sure it succeeded */) {
dispatch_async(dispatch_get_main_queue(), ^{
// Update the UI
NSLog(#"Thread Process Finished");
});
} else {
// Note: you can add a dispatch_after here (or something similar) to wait before the next attempt
// You could also add exponential backoff here, which is usually good when retrying network stuff
[self sendJSONToServer:jsonString withRetryAttempts:(retryTimes - 1)];
}
}];
} else {
// We're out of retries; handle appropriately
}
}
Note: There are some bits in there that are just comments because they are application specific; they'll need to be implemented before that code will compile/run.
Now, instead of calling [yourClass sendJSONToServer:jsonString], call: [yourClass sendJSONToServer:jsonString withRetryTimes:maxRetries] and, if the request fails, it should retry up to maxRetries times.
One last note: As #Deftsoft mentioned, Apple's Reachability classes are a nice way to know if you have an active connection to the network or not. It's a good idea to check that first before trying to call sendJSONToServer:withRetryTimes:. That way you're not trying to make requests when it's not possible to even connect in the first place.
You can Apple reachability classes below is the reference code which will provide you the better idea.
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(networkChanged:) name:kReachabilityChangedNotification object:nil];
reachability = [Reachability reachabilityForInternetConnection];
[reachability startNotifier];
- (void)networkChanged:(NSNotification *)notification
{
NetworkStatus remoteHostStatus = [reachability currentReachabilityStatus];
if(remoteHostStatus == NotReachable) { NSLog(#"not reachable");}
else if (remoteHostStatus == ReachableViaWiFiNetwork) { NSLog(#"wifi"); }
else if (remoteHostStatus == ReachableViaCarrierDataNetwork) { NSLog(#"carrier"); }
}
Related
I want to execute my SOAP web service Asynchronously,Because I got some lag in getting data while calling synchronously.Also i was able to get result when calling a single web service when a multiple web service is called in (view did load) or in (view will appear) i was unable to get the data.
Can anyone tell how to call a asynchronous SOAP webService: Here is my code
cws = [[CustomWebService alloc]init];
NSString *soapMessage = [NSString stringWithFormat:#"my soap string"];
[[NSNotificationCenter defaultCenter]addObserver:self selector:#selector(msgCount) name:#"my response name" object:nil];
NSDictionary *Details=[[NSDictionary alloc]initWithObjectsAndKeys:nil];
[cws getSoapAction:#".........." andNameSpace:#"" andDetails:Details andUrlIs:[AppDelegate URLSource] andSoapMessage:soapMessage ];
[cws getPageName:#"my response name"];
NSLog(#"SOAP MESSAGE IS %#",soapMessage);
And I get the response here:
-(void)msgCount
{
[[NSNotificationCenter defaultCenter]removeObserver:self];
NSMutableDictionary *diict=[[NSMutableDictionary alloc]initWithDictionary:[cws msgCount]];
NSLog(#"the response is %#",diict);
}
you can wrap your webservice call in a GCD async block to make the code run asynchronously in the background
dispatch_async(dispatch_get_main_queue(), ^{
//code that should run asynchronously
});
NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:[NSURL URLWithString:#"UR URL"] cachePolicy:NSURLRequestUseProtocolCachePolicy timeoutInterval:60.0];
[request setHTTPMethod:#"POST"];
NSString *postString = #"UR KEY1=UR VALUE1"; //for single variable
//OR for Multiple
[request setValue:#"UR VALUE1" forKey:#"UR KEY1"];
[request setValue:#"UR VALUE2" forKey:#"UR KEY2"];
[request setHTTPBody:[postString dataUsingEncoding:NSUTF8StringEncoding]];
NSOperationQueue *queue = [[NSOperationQueue alloc] init];
[NSURLConnection sendAsynchronousRequest:request queue:queue completionHandler:^(NSURLResponse *response, NSData *data, NSError *error)
{
if (error) {
NSLog(#"Error,%#", [error localizedDescription]);
} else {
NSLog(#"%#", [[NSString alloc] initWithData:data encoding:NSASCIIStringEncoding]);
}
}];
I'm currently trying to execute a method after a NSURLSession completes. The problem is I can't manage in any way the asynchronous nature of its object. I've tried with GCD and NSOperation as suggested in the other questions but nothing changes: after the initialization with dataTaskWithRequest:completionHandeler:, the application starts executing the next method in the program.
Here is the method which realizes the networking:
-(void)sendData{
NSData *JSONdata = [NSJSONSerialization dataWithJSONObject:userInfoToJSON options:0 error:&error];
NSURL *url = [NSURL URLWithString:#"http://mobdev2015.com/register.php"];
NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:url];
NSString *postLength = [NSString stringWithFormat:#"%lu", (unsigned long)[JSONdata length]];
[request setHTTPMethod:#"POST"];
[request setValue:#"application/json" forHTTPHeaderField:#"Accept"];
[request setValue:#"application/json" forHTTPHeaderField:#"Content-Type"];
[request setValue:postLength forHTTPHeaderField:#"Content-Length"];
[request setHTTPBody:JSONdata];
[[session dataTaskWithRequest:request completionHandler:^(NSData *data, NSURLResponse *response, NSError *error) {
requestReply = [[NSString alloc] initWithData:data encoding:NSASCIIStringEncoding];
}]resume];
});
}
Here is the method instead which calls the one above and where I want to implement a synchronous execution:
-(void)MethodWhichNeedsToBeSync{
//Creating a JSON object..
[self sendData:userInfoToJSON];
//MethodB wants to be executed if and only if sendData is completed
[self MethodB];
}
Thank you for the replies.
The way to execute a method after the completion of an asynchronous method is to have that method called in the completion block (or closure) of the asynchronous method.
For your case, it would look something like this:
[[session dataTaskWithRequest:request completionHandler:^(NSData *data, NSURLResponse *response, NSError *error) {
requestReply = [[NSString alloc] initWithData:data encoding:NSASCIIStringEncoding];
if (!error) {
[self MethodB];
}
}]resume];
});
where the call to be run after successful completion of the asynchronous method is moved into the completion handler.
That would accomplish the execution of MethodB if and only if sendData is completed, as you wanted.
As far as I can tell from the NSURLSession documentation there's no way to use it to make synchronous requests. I assume that's because you really shouldn't be making calls that could block the main queue.
However, if using NSURLSession isn't required you can simply use NSURLConnection instead. You would initialize it with your request and call "sendSynchronousRequest:returningResponse:".
Hey so I am getting the classic EXC_BAD_ACCESS (Code = 1, address = 0x10) and can't seem to figure out why. I get the error with the following method:
+ (void)logoutWithXId:(NSString *)xId compelationHandler:(void (^)(BOOL))hasSucceeded
{
NSError *error;
// create json object for a users session
NSDictionary *session = [NSDictionary dictionaryWithObjectsAndKeys:
xId, #"x_id",
nil];
NSData *jsonSession = [NSJSONSerialization dataWithJSONObject:session options:NSJSONWritingPrettyPrinted error:&error];
NSString *url = [NSString stringWithFormat:#"%#xsession/logout", potCatURL];
NSURL *URL = [NSURL URLWithString:url];
NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:URL
cachePolicy:NSURLRequestUseProtocolCachePolicy
timeoutInterval:30.0];
NSString *headerValue = [NSString stringWithFormat:#"Token token=%#", APIToken];
[request setHTTPMethod:#"POST"];
[request setValue:#"application/json" forHTTPHeaderField:#"Accept"];
[request setValue:#"application/json" forHTTPHeaderField:#"Content-Type"];
[request setValue:headerValue forHTTPHeaderField:#"Authorization"];
[request setValue:[NSString stringWithFormat:#"%lu", (unsigned long)[jsonSession length]] forHTTPHeaderField:#"Content-Length"];
[request setHTTPBody:jsonSession];
[NSURLConnection sendAsynchronousRequest:request queue:[NSOperationQueue mainQueue] completionHandler:^(NSURLResponse *response, NSData *data, NSError *error) {
if (error == nil)
{
NSHTTPURLResponse *httpResponse = (NSHTTPURLResponse *)response;
NSInteger statusCode = httpResponse.statusCode;
if (statusCode == 200)
{
//********************* error happens here *******************************
hasSucceeded(true);
}
else
{
hasSucceeded(false);
}
}
else
{
hasSucceeded(false);
}
}];
}
When I call the method like so:
[Xsession logoutWithXId:[userInfo stringForKey:#"x_id"] compelationHandler:nil]
I get the EXC_BAD_ACCESS error but when I call it like:
[Xsession logoutWithXId:[userInfo stringForKey:#"x_id"] compelationHandler:^(BOOL hasCreated){}]
with an empty compilationHandler it runs fine. Why is this happening, can someone please explain?
It's due to the nil handler. You must always do the following:
if (hasSucceeded) {
hasSucceeded(someValue);
}
The crash is caused by dereferencing the nil block pointer. This is different from trying to call a method on a nil variable reference (which is fine). You must never dereference a nil block pointer.
There is some great information about this in another answer here.
The reason you get a crash is that you don't check whether passed argument is nil or not. Blocks in this case are different (you should treat it like a pointer to a function not like NSObject subclass). You cannot call a function on address 0.
To fix it you should check if the block is not nil before calling it:
if (hasSucceded) {
hasSucceeded(value);
}
I'm coding an iOS App which uses an URL Request to a PHP document to send and receive data. Here is my code.
NSString *myRequestString = [NSString stringWithFormat:#"userid=%#&recieverid=%#&messege=%#&type=%#&password=%#", ownID, _friendID, content_encoded, msg_type, password];
NSData *myRequestData = [NSData dataWithBytes: [myRequestString UTF8String] length: [myRequestString length]];
NSMutableURLRequest *request = [[NSMutableURLRequest alloc] initWithURL: [NSURL URLWithString: #"something.php"]];
[request setHTTPMethod: #"POST"];
[request setValue:#"application/x-www-form-urlencoded" forHTTPHeaderField:#"content-type"];
[request setHTTPBody: myRequestData];
//My activiy Indicator starts here
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
// Now send a request and get Response
NSData *returnData = [NSURLConnection sendSynchronousRequest: request returningResponse: nil error: nil];
NSString *result = [[NSString alloc] initWithBytes:[returnData bytes] length:[returnData length] encoding:NSUTF8StringEncoding];
//Here some internal coding happens...
dispatch_async(dispatch_get_main_queue(), ^(void) {
//Stopping activity indicator
});
});
But if the user locks his Phone while he sends the data (possible in other apps like Snapchat etc as well) the app freezes when the user returns and has to reopen it.
I want to know if theres a better way of doing if the app connects to the Server and the user closes the app that doesn't let this error occur.
Thank you :)
Anton
And sorry about my poor english Im not a native speaker.
I'd suggest:
Specifying a background task identifier, as suggested by John Woods, so that if the user leaves the app in the middle of the request, it will attempt to continue the network request in the background.
Use sendAsynchronousRequest rather than dispatching sendSynchronousRequest to the background.
Make sure you correctly detect and handle errors (because it's not entirely clear to me whether the problem rests in the code of your question or whatever processing you're doing with it later).
Unrelated, but I'd avoid using the bytes-related NSData methods.
Thus:
// I'm guessing that you're percent encoding `messege` [sic], but I'd do it for all of those parameters (notably password)
NSString *myRequestString = [NSString stringWithFormat:#"userid=%#&recieverid=%#&messege=%#&type=%#&password=%#", ownID, _friendID, content_encoded, msg_type, password];
// Use `dataUsingEncoding` rather than bytes rendition:
//
// NSData *myRequestData = [NSData dataWithBytes: [myRequestString UTF8String] length: [myRequestString length]];
NSData *myRequestData = [myRequestString dataUsingEncoding:NSUTF8StringEncoding];
NSMutableURLRequest *request = [[NSMutableURLRequest alloc] initWithURL: [NSURL URLWithString: #"something.php"]];
[request setHTTPMethod: #"POST"];
[request setValue:#"application/x-www-form-urlencoded" forHTTPHeaderField:#"content-type"];
[request setHTTPBody: myRequestData];
// start background task
UIBackgroundTaskIdentifier __block task = [[UIApplication sharedApplication] beginBackgroundTaskWithExpirationHandler:^{
[[UIApplication sharedApplication] endBackgroundTask:task];
task = UIBackgroundTaskInvalid;
}];
// activity indicator starts here
[NSURLConnection sendAsynchronousRequest:request queue:[NSOperationQueue mainQueue] completionHandler:^(NSURLResponse *response, NSData *data, NSError *connectionError) {
if (!data) {
NSLog(#"%s: sendAsynchronousRequest error: %#", __PRETTY_FUNCTION__, connectionError);
} else {
// use NSData rendition rather than bytes rendition:
//
// NSString *result = [[NSString alloc] initWithBytes:[returnData bytes] length:[returnData length] encoding:NSUTF8StringEncoding];
NSString *result = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding];
// here some internal coding happens...
}
// stop activity indicator here
// stop background task
if (task != UIBackgroundTaskInvalid) {
[[UIApplication sharedApplication] endBackgroundTask:task];
task = UIBackgroundTaskInvalid;
}
}];
You have to implement a background task. When the phone locks, all apps transition to a background "inactive" state. You need to implement a long running background task.
https://developer.apple.com/library/ios/documentation/iphone/conceptual/iphoneosprogrammingguide/ManagingYourApplicationsFlow/ManagingYourApplicationsFlow.html
Hi I'm using this code to send post values to a server but I want the HUD to appear during the time the request is being done, because it appears only when it ends the request.
-(IBAction)sendk:(id)sender {
/*HUD*/
SLHUD *hudView = [SLHUD Mostrar:self.view]; // Creates a Hud object.
hudView.text = #"Please Wait"; // Sets the text of the Hud.
UIActivityIndicatorView *activityIndicator = [[UIActivityIndicatorView alloc] initWithActivityIndicatorStyle:UIActivityIndicatorViewStyleWhite];
activityIndicator.alpha = 1.0;
activityIndicator.center = CGPointMake(160, 280);
activityIndicator.hidesWhenStopped = NO;
[activityIndicator setTag:899];
[self.view addSubview:activityIndicator];
[activityIndicator startAnimating];
/*FIN HUD*/
NSString *post =[[NSString alloc] initWithFormat:#"user=%#&pass=%#",[username text],[password text]];
NSLog(#"%#",post);
NSURL *url=[NSURL URLWithString:#"URL TO SERVER"];
NSData *postData = [post dataUsingEncoding:NSUTF8StringEncoding allowLossyConversion:YES];
NSString *postLength = [NSString stringWithFormat:#"%d", [postData length]];
NSMutableURLRequest *request = [[NSMutableURLRequest alloc] init];
[request setURL:url];
[request setHTTPMethod:#"POST"];
[request setValue:postLength forHTTPHeaderField:#"Content-Length"];
[request setValue:#"application/json" forHTTPHeaderField:#"Accept"];
[request setValue:#"application/x-www-form-urlencoded" forHTTPHeaderField:#"Content-Type"];
[request setHTTPBody:postData];
//[NSURLRequest setAllowsAnyHTTPSCertificate:YES forHost:[url host]];
NSError *error = [[NSError alloc] init];
NSHTTPURLResponse *response = nil;
NSData *urlData=[NSURLConnection sendSynchronousRequest:request returningResponse:&response error:&error];
NSLog(#"%ld",(long)[response statusCode]);
NSString *responseData = [[NSString alloc]initWithData:urlData encoding:NSUTF8StringEncoding];
NSLog(#"%#",responseData);
The problem is that the code is blocking the main thread until the network request finishes. The screen will only update after the sendk method returns, but the method won't return until the sendSynchronousRequest method is finished. The solution is to dispatch the networking code (everything after /*FIN HUD*/) onto a background thread, or use sendAsynchronousRequest, and use the completion block to notify the main thread when the response arrives.
The code framework for using a background thread looks like this
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_LOW, 0), ^{
// do networking stuff here
dispatch_async( dispatch_get_main_queue(), ^{
// turn off the HUD and remove the spinner here
// also do something with the network response here
});
});