NSURLConnection , webservices not working in Background ios - ios

i am new in ios , i want to make one app , in app i have to call a webservices in background ,
background code is working properly but when i try to call webservices not go to on "connectionDidFinishLoading" function
where i doing a mistake please Help me
Here is my code
1. this is my background function each 30 sec its call webservices
- (void)applicationDidEnterBackground:(UIApplication *)application
{
if ([[UIDevice currentDevice] respondsToSelector:#selector(isMultitaskingSupported)]) { //Check if our iOS version supports multitasking I.E iOS 4
if ([[UIDevice currentDevice] isMultitaskingSupported]) { //Check if device supports mulitasking
UIApplication *application = [UIApplication sharedApplication]; //Get the shared application instance
__block UIBackgroundTaskIdentifier background_task; //Create a task object
background_task = [application beginBackgroundTaskWithExpirationHandler: ^ {
[application endBackgroundTask: background_task]; //Tell the system that we are done with the tasks
background_task = UIBackgroundTaskInvalid; //Set the task to be invalid
}];
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
//Perform your tasks that your application requires
while(TRUE)
{
//backgroundTimeRemaining time does not go down.
// NSLog(#"Background time Remaining: %f",[[UIApplication sharedApplication] backgroundTimeRemaining]);
[UIApplication sharedApplication].applicationIconBadgeNumber++;
NSLog(#"\n\nRunning in the background!\n\n");
[self CallWebservices2];
[NSThread sleepForTimeInterval:30]; //wait for 1 sec
}
[application endBackgroundTask: background_task];
background_task = UIBackgroundTaskInvalid;
});
}
}}
2.this is my call webservices, this code reach to CallWebservices2 function but no able to call connectionDidFinishLoading
-(void)CallWebservices2
{
NSString *urlString = [NSString stringWithFormat:#"http://yournurses.com/process/push.php?action=select_jobs&id=241"];
NSLog(#"String Url = %#",urlString);
NSURL *url = [NSURL URLWithString:urlString];
NSURLRequest *urlRequest = [NSURLRequest requestWithURL:url];
NSURLConnection *connnection = [[NSURLConnection alloc] initWithRequest:urlRequest delegate:self];
[connnection start];
}
-(void)connection:(NSURLConnection *)connection didFailWithError:(NSError *)error{
NSLog(#"Error==%#",error);
}
-(void)connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)response{
[nsUrlResponseDataNurse setLength:0];
}
-(void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data{
[nsUrlResponseDataNurse appendData:data];
}
-(void)connectionDidFinishLoading:(NSURLConnection *)connection
{
if ([nsUrlResponseDataNurse length]==0)
{}
else{
NSString *str = [[NSString alloc]initWithData:nsUrlResponseDataNurse encoding:NSUTF8StringEncoding];
allDataNurse = [str JSONValue];
NSLog(#"%#",allDataNurse);
}
}
here i define in delegate.h file
#property(nonatomic,retain)NSMutableData *nsUrlResponseDataNurse;
#property(nonatomic, retain) NSMutableArray *allDataNurse;
Regards,
Nishant Chandwani

NSURLConnection supports async network transactions "out of the box". You should not run NSURLConnection from the background. It is pointless and wasteful. The class is designed to handle background downloading efficiently.
You create an NSURLConnection on the main thread and start it running. It does it's work in the background, then calls your delegate methods on the main thread.
If you only need to get notified when the download/PUT is complete, you can use the NSURLConnection class method sendAsynchronousRequest:queue:completionHandler: which runs the whole request, then calls your completion handler once it's done. Generally you have that method call your completion handler on the main queue, since that code gets called once the URL request is finished.
Your code might look like this:
NSURL *url = [NSURL URLWithString: #"http://www.foo.php"];
NSURLRequest *request = [NSURLRequest requestWithURL: url];
[NSURLConnection sendAsynchronousRequest: request
queue: [NSOperationQueue mainQueue]
completionHandler: ^(NSURLResponse *response,
NSData *data,
NSError *connectionError)
{
if (data.length > 0 && connectionError == nil)
{
//The data for the response is in "data" Do whatever is required
}
}
];
That code will run the request in the background, and invoke the code in the completion block once it's finished. Simple and painless.

Asynchronous NSURLConnection call doesn't work in different runloop but mainRunLoop. Either use synchronous request in a different thread like :-
-(void)CallWebservices2
nsUrlResponseDataNurse = [NSURLConnection sendSynchronousRequest: returningResponse: error:]
}
, or if you badly want to make your code working, change the calls to :
[[NSURLConnection alloc] initWithRequest:urlRequest delegate:self startImmediately:NO];
[connnection scheduleInRunLoop:[NSRunLoop mainRunLoop] forMode:NSDefaultRunLoopMode];

Related

From NSURLConnection to NSURLSession

What is the best way to convert NSURLConnection to NSURLSession ?
error message: (Xcode)
ViewController.m:342:45: 'stringByAddingPercentEscapesUsingEncoding:' is deprecated: first deprecated in iOS 9.0 - Use -stringByAddingPercentEncodingWithAllowedCharacters: instead, which always uses the recommended UTF-8 encoding, and which encodes for a specific URL component or subcomponent since each URL component or subcomponent has different rules for what characters are valid.
my code :
-(void)downloadZip
{
NSLog(#"Start Downloading Zip File");
NSDate *myDate = (NSDate *)[[NSUserDefaults standardUserDefaults] objectForKey:#"LastUpdate"];
NSString *path = [NSString stringWithFormat:phpLinkgetZip, myDate];
NSURL *url = [NSURL URLWithString:[path stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding]];
NSLog(#"Checking update at Zip File : %#", path);
NSLog(#"Checking update Time : %#", myDate);
responseData = [[NSMutableData alloc] init];
NSURLRequest* updateRequest = [NSURLRequest requestWithURL: url];
NSURLConnection* connection = [[NSURLConnection alloc] initWithRequest:updateRequest delegate:self];
[connection start];
NSLog(#"Zip Downloading start...");
}
- (void)connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)response {
[responseData setLength:0];
filesize = [[NSNumber numberWithLong: [response expectedContentLength] ] retain];
}
- (void)connectionDidFinishLoading:(NSURLConnection *)connection
{
[self updateZipDownloaded];
[filesize release];
[connection release];
}
-(void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data
{
[responseData appendData:data];
NSNumber* curLength = [NSNumber numberWithLong:[responseData length] ];
float progress = [curLength floatValue] / [filesize floatValue] ;
}
- (void)connection:(NSURLConnection *)connection didFailWithError:(NSError *)error
{
NSLog(#"Zip Downloading error");
}
If you want to overcome above error please use below code
NSCharacterSet *setPath = [NSCharacterSet URLPathAllowedCharacterSet];
NSString *strURL = [path stringByAddingPercentEncodingWithAllowedCharacters:setPath];
stringByAddingPercentEncodingWithAllowedCharacters:
Returns a new string made from the receiver by replacing all
characters not in the specified set with percent-encoded characters.
Characters passed to set below methods
(NSCharacterSet *)URLUserAllowedCharacterSet;
(NSCharacterSet *)URLPasswordAllowedCharacterSet;
(NSCharacterSet *)URLHostAllowedCharacterSet;
(NSCharacterSet *)URLPathAllowedCharacterSet;
(NSCharacterSet *)URLQueryAllowedCharacterSet;
(NSCharacterSet *)URLFragmentAllowedCharacterSet;
If you want to go NSURLSession from NSURLConnetion,do the following things
NSURL *URL = [NSURL URLWithString:#"http://example.com"];
NSURLRequest *request = [NSURLRequest requestWithURL:URL];
NSURLSession *session = [NSURLSession sharedSession];
NSURLSessionDataTask *task = [session dataTaskWithRequest:request
completionHandler:
^(NSData *data, NSURLResponse *response, NSError *error) {
// ...
}];
[task resume];
NSURLSession class and related classes provide an API for downloading content via HTTP. This API provides a rich set of delegate methods for supporting authentication and gives your app the ability to perform background downloads when your app is not running or, in iOS, while your app is suspended.
To use the NSURLSession API, your app creates a series of sessions,
each of which coordinates a group of related data transfer tasks. For
example, if you are writing a web browser, your app might create one
session per tab or window. Within each session, your app adds a series
of tasks, each of which represents a request for a specific URL (and
for any follow-on URLs if the original URL returned an HTTP redirect).
Like most networking APIs, the NSURLSession API is highly
asynchronous. If you use the default, system-provided delegate, you
must provide a completion handler block that returns data to your app
when a transfer finishes successfully or with an error. Alternatively,
if you provide your own custom delegate objects, the task objects call
those delegates’ methods with data as it is received from the server
(or, for file downloads, when the transfer is complete).

Continue webservice call after transition from foreground to background in iOS objective C

Suppose I call a webservice when the app is in foreground. Now if the user sends the app to background then how do I make sure that this webservice call keeps executing in the background.
This is the piece of code that I am using in my app.
Login* login = [[Login alloc]init];
[login initiateSignInProcess];
initiateSignInProcess has 4 web service calls. they are normal
functions. I am using AFNetworking.
If any of the services fail, I call it again with a delay in the failure block of afnetworking code like below:-
failure:^(AFHTTPRequestOperation *operation, NSError *error)
{
[self performSelector:#selector(getUserId) withObject:nil afterDelay:5];
}
Now I want to know that if the user sends the app to background, then how will the code execute? Will it call this function in bakcground till it succeeds?
Best to use Background Process for fetch. Here is great tutorial for solution [ http://code.tutsplus.com/tutorials/ios-7-sdk-working-with-background-fetch--mobile-20520
Not possible in iOS6.x or lesser unless your application is has specific requirement to run in background like locations, Voip, music etc...
However this is possible with iOS7, please consider having a look at this
http://redth.codes/ios7-recipe-background-fetching/
**For(large FIle Downloade use Asynchronous Method)**
NSURL *myUrl = [NSURL URLWithString:#"Enter URL HERE"];
NSURLRequest *myRequest = [NSURLRequest requestWithURL:myUrl cachePolicy:NSURLRequestReloadIgnoringLocalCacheData timeoutInterval:60];
NSMutableData *myData = [[NSMutableData alloc] initWithLength:0];
NSURLConnection *myConnection = [[NSURLConnection alloc] initWithRequest:myRequest delegate:self startImmediately:YES];
**For(Small FIle Downloade use Synchronous Method)**
NSURL *myUrl = [NSURL URLWithString:#"Enter URl HERE"];
NSData *myData = [NSData dataWithContentsOfURL:myUrl];
UIImage *img = [UIImage imageWithData:myData];
add NSURLConnection Delegate in .h File
- (void) connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)response {
[UIApplication sharedApplication].networkActivityIndicatorVisible = YES;
[myData setLength:0];
}
- (void) connection:(NSURLConnection *)connection didReceiveData:(NSData *)data {
[myData appendData:data];
}
- (void) connection:(NSURLConnection *)connection didFailWithError:(NSError *)error {
[UIApplication sharedApplication].networkActivityIndicatorVisible = NO;
[connection release];
}
- (void) connectionDidFinishLoading:(NSURLConnection *)connection {
[UIApplication sharedApplication].networkActivityIndicatorVisible = NO;
[connection release];
//download finished - data is available in myData.
}
This is depends on OS scheduling whether it allows continue to run the services in background or kill it.
Best to use Background Fetch. Here is nice tutorial http://code.tutsplus.com/tutorials/ios-7-sdk-working-with-background-fetch--mobile-20520
Hope this solve your issue.

Multiple File Upload using uploadTaskWithRequest fromFile in background

I am trying to upload multiple images using NSURLSession .It works fine when application is running in foreground.When application enter background,uploading process stop after uploading current task.I would like to upload all the files when application is in background. Any help would be greatly appreciated. Here is my code.
//background task configuration
-(void) uploadOneByOne:(NSString *)individualpath{
NSMutableURLRequest *request=[NSMutableURLRequest requestWithURL:[NSURL URLWithString:mutableUrlString]];
NSString *requestURL = [NSString stringWithFormat:#"http://myservice/Service.svc/UploadOrdersToDrive?orderFolder=%#",OrderFolderID];
NSMutableURLRequest *request=[NSMutableURLRequest requestWithURL:[NSURL URLWithString:requestURL]];
[request setHTTPMethod:#"POST"];
NSURL *filePath =[NSURL fileURLWithPath:individualpath];
NSURLSessionConfiguration *configuration = [NSURLSessionConfiguration backgroundSessionConfiguration:kSessionIdentifier];
defaultSession= [NSURLSession sessionWithConfiguration:configuration delegate:self delegateQueue:nil];
NSURLSessionUploadTask *uploadTask =
[defaultSession uploadTaskWithRequest:request
fromFile:filePath];
[uploadTask resume];
}
NSURLSession Delegate
receive first request response
- (void)URLSession:(NSURLSession *)session dataTask:(NSURLSessionDataTask *)dataTask
didReceiveData:(NSData *)data
{
NSString * str = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding];
NSLog(#"Received String %#",str);
NSDictionary *jsonResponseData = [NSJSONSerialization JSONObjectWithData:data options:kNilOptions error:nil];
//FolderID to create next image in same folder.
OrderFolderID =[jsonResponseData
objectForKey:#"OrderFolderID"];
}
create next request
- (void)URLSession:(NSURLSession *)session task:(NSURLSessionTask *)task
didCompleteWithError:(NSError *)error
{
if(error == nil)
{
//Remove DetailFist
[orderDetailarray removeObjectAtIndex:0];
if (orderDetailarray.count >0){
ChosenImages *item = [orderDetailarray objectAtIndex:0];
[self uploadOneByOne:item.path ];
}
}
//Update progress bar
- (void)URLSession:(NSURLSession *)session task:(NSURLSessionTask *)task
didSendBodyData:(int64_t)bytesSent
totalBytesSent:(int64_t)totalBytesSent
totalBytesExpectedToSend:(int64_t)totalBytesExpectedToSend{
//update progress bar
}
Probably you didn't wait enough for next task to start. Depending from different things like WiFi and battery status, user activity etc. your next task queued in background could start in few minutes.. or few hours.
Btw I don't see code related with completionHandler implementation.
Do not forgot to implement
- (void)application:(UIApplication *)application handleEventsForBackgroundURLSession:(NSString *)identifier completionHandler:(void (^)())completionHandler
in the App delegate and appropriate session delegate.

NSURLConnection and beginBackgroundTaskWithExpirationHandler

I'm using NSURLConnection to download archive 500mb. And I want to download it on background thread, so I wrote:
NSURLRequest *theRequest = [NSURLRequest requestWithURL:url cachePolicy:NSURLRequestReloadIgnoringCacheData timeoutInterval:60];
self.theConnection = [[NSURLConnection alloc] initWithRequest:theRequest delegate:self];
self.backgroundTask = [[UIApplication sharedApplication] beginBackgroundTaskWithExpirationHandler:^{
// Cancel the connection
[self.theConnection cancel];
}];
- (void)connection:(NSURLConnection *)connection didFailWithError:(NSError *)error
{
[[UIApplication sharedApplication] endBackgroundTask:self.backgroundTaskID];
}
- (void)connectionDidFinishLoading:(NSURLConnection *)connection
{
[[UIApplication sharedApplication] endBackgroundTask:self.backgroundTaskID];
}
So if I did it on main thread it download and unarchive and working well, but if I start download and press Home button, so it start working on background thread it download 70% - 80% and it freezed. Method - (void)connection:(NSURLConnection *)connection didFailWithError:(NSError *)error don't call.
How can I download large file on background thread?
Edit 1
I found that it called
self.backgroundTask = [[UIApplication sharedApplication] beginBackgroundTaskWithExpirationHandler:^{
[self.theConnection cancel];
}];
So connection finished, but I don't call endBackgroundTask in another methods.
Apple allows your app run in background thread about 10 minutes (when you press home button).
NSURLSession is what you are looking for: NSURLSession
"This API provides a rich set of delegate methods for supporting authentication and gives your app the ability to perform background downloads when your app is not running or, in iOS, while your app is suspended."

Cannot get data using NSURLConnection with GCD

So I am downloading some data from a server to my ios client. The data needs to be formatted so I use NSNotification to notify the app when the data has been downloaded completely and when thats done, I format the data and then display it on the screen.
All this is cool but because of the size of the data, the screen freezes.I thought I should use GCD to push the data downloading to another thread so the UI is still responsive. When i did that, I dont seem to be downloading any data.
I have a method getTops that uses NSURLConnection to download the data. Initially, in my viewDidLoad I called this method and it worked fine but then I used GCD like so
dispatch_queue_t getItemsQ = dispatch_queue_create("get Items", NULL);
dispatch_async(getItemsQ, ^{
[self getTops];
});
And it stopped working. I know it gets to the getTops because I can see the log in the console but it never reaches the -(void)connectionDidFinishLoading:(NSURLConnection *)connection
Here is the code i used:
-(void)getTops{
Keychain *keychain = [[Keychain alloc]init];
NSString *auth_token = [keychain getAuthToken];
NSLog(#"at getTops");
topimageArray = [[NSMutableArray alloc]init];
NSURLConnection *connection;
webData = [[NSMutableData alloc]init];
NSURL *url = [[NSURL alloc]initWithString:base];
NSMutableURLRequest *request = [[NSMutableURLRequest alloc]init];
[request setURL: url];
[request setValue:auth_token forHTTPHeaderField:#"X-AUTH-TOKEN"];
connection = [NSURLConnection connectionWithRequest:request delegate:self];
// [connection start];
[UIApplication sharedApplication].networkActivityIndicatorVisible = YES;
NSLog(#"at getTops conn start");
}
-(void)connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)response{
NSLog(#"recieved response");
[webData setLength:0];
}
-(void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data{
if(data) {
NSLog(#"Appending data");
[webData appendData:data];
}
}
-(void)connectionDidFinishLoading:(NSURLConnection *)connection{
NSArray *response= [NSJSONSerialization JSONObjectWithData:webData options:0 error:nil];
NSLog(#"Tops full response::: %#",response);
theArray = [[NSArray alloc]initWithArray:response];
NSLog(#"DONE");
//Notify that the data is ready to be formated
[[NSNotificationCenter defaultCenter]postNotificationName:#"getTops" object:nil];
[UIApplication sharedApplication].networkActivityIndicatorVisible = NO;
}
-(void)connection:(NSURLConnection *)connection didFailWithError:(NSError *)error{
NSLog(#"error::::%#", error);
NSString *errormsg = error.localizedDescription;
UIAlertView *alertview = [[UIAlertView alloc]initWithTitle:#"Error" message:errormsg delegate:self cancelButtonTitle:#"OK" otherButtonTitles:nil, nil];
[alertview show];
[UIApplication sharedApplication].networkActivityIndicatorVisible = NO;
}
I thought maybe I should remove the [UIApplication sharedApplication].networkActivityIndicatorVisible but that didnt help
Edit:: Added NSLogs to delegate methods.
The Log that I get is
at getTops conn start
And thats it.
The simplest way is to use sendAsynchronousRequest:queue:completionHandler:. No delegates are necessary, just put the code from connectionDidFinishLoading: in the completion block.
Loads the data for a URL request and executes a handler block on an operation queue when the request completes or fails.
[NSURLConnection sendAsynchronousRequest:request
queue:[NSOperationQueue mainQueue]
completionHandler:^(NSURLResponse *response, NSData *data, NSError *error) {
< your code >
}];

Resources