I've changed my old NSthread because couldn't update UI from a background thread. Now I'm using dispatch_async.
The problem is when this code is executed the second time, in ApplicationDidBecomeActive (when user hide and show again de application) Never exit from that while, keeps in infinite loop.
Here I show the new code about dispatch_async, the NSURLConnection code always worked , resume at the end... I don't think the problem is there
- (void)applicationDidBecomeActive:(UIApplication *)application
{
// first connection to the server through NSURLConnection, downloading a json list
[wsDataVideos getVideosData:URL_WS_VIDEOS];
dispatch_queue_t secondDownloadQueue = dispatch_queue_create("name",NULL);
dispatch_async(secondDownloadQueue, ^{
// wait for wsDataVideos has finished. IT SEEMS THIS COLLAPSES CPU ¿?
while (wsDataVideos.imported==NO) {}
// If are there new videos begins sincronous and slower download:
if ....{
dispatch_async(dispatch_get_main_queue(), ^{
// Update the UI: inform user begins download
[menuViewController.badgeVideos setBadgeVideosText:#"Downloading..."];
});
// Download videos (sincro)
dispatch_async(dispatch_get_main_queue(), ^{
// informs user is completed
[menuViewController.badgeVideos setBadgeVideosText:#"Downloaded"];
});
}
});
}
The first connection (json), than I wait in that while :
-(void)getVideosData:(NSString *)url_ws{
NSLog(#"Get videos data1 ");
if (wsData){
[wsData setLength:0];
}
else{
wsData=[[NSMutableData alloc] init];
}
NSURLRequest *reqCat=[NSURLRequest requestWithURL:[NSURL URLWithString:url_ws] cachePolicy:NSURLRequestUseProtocolCachePolicy timeoutInterval:60.0];
NSURLConnection *conCat=[[NSURLConnection alloc] initWithRequest:reqCat delegate:self];
}
with their methods:
-(void)connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)response{
...
-(void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data{
[wsData appendData:data];
}
...
-(void)connectionDidFinishLoading:(NSURLConnection *)connection{
...
Any ideas? thanks again.
If I put code inside while{} it works, but i think this is somthing odd..chunk, bad code, isn't it?
Maybe a better soluttion would be somthing like dispatch_group_wait() ?
// wait for wsDataVideos has finished. IT SEEMS THIS COLLAPSES CPU ¿?
while (wsDataVideos.imported==NO) {
sleep(1)
}
Related
Is it possible to catch NSURLConnection cancel using a callback?
If I'm using this code
-(void) pleaseStopDownload {
cancelled = YES;
[conn cancel];
conn = nil;
[self myUpdateUImessage];
}
from time to myUpdateUImessage is called before this callback
- (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data {
NSLog(#"didReceiveData");
if (!cancelled) {
//this code inside brackets suddenly is calling:(
//not always but from time to time
summ += data.length;
if (_progressHandler != nil)
_progressHandler(data, summ, max);
} else {
return;
}
}
So the User interface is not updated properly! That is, the final UI is shown than progress UI.
EDIT
the problem was about
NSOperationQueue *tempQueue = [[NSOperationQueue alloc] init];
[conn setDelegateQueue:tempQueue];
correct NSQueue is NSOperationQueue *tempQueue = [NSOperationQueue mainQueue];
Is it possible to catch NSURLConnection cancel using a callback?
No.
From the official documentation here:
After this method is called, the connection makes no further delegate method calls.
This means you should handle the UI cleanup as soon as you call cancel and not rely on the _cancelled variable because - (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data is not expected to be invoked anymore.
What I advice, is to call a cleanup method from your cancellation code:
-(void) pleaseStopDownload {
[conn cancel];
conn = nil;
[self handleCancelledDownload];
}
Hi am new to multithreading.... below is the code i wrote to download a video in separate thread but delegates methods not firing up anybody please help me to solve this... thanks in advance..
- (void)downLoad {
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
_videoData = [[NSMutableData alloc] init];
[NSURLConnection connectionWithRequest:[NSURLRequest requestWithURL:[NSURL URLWithString:#"http://km.support.apple.com/library/APPLE/APPLECARE_ALLGEOS/HT1211/sample_iTunes.mov"]] delegate:self];
//saving is done on main thread
});
}
- (void)connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)response {
NSLog(#"Downloading Started");
_length = [response expectedContentLength];
NSLog(#"Size:%0.2f",_length);
}
-(void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data {
[_videoData appendData:data];
float progress = (float)[_videoData length]/(float)_length;
NSLog(#"Progress:%0.2f",progress);
_timeLabel.text = [NSString stringWithFormat:#"%0.2F%%",progress*100];
[_progressView setProgress:progress animated:YES];
}
-(void)connection:(NSURLConnection *)connection didFailWithError:(NSError *)error {
NSLog(#"didFailWithError");
}
-(void)connectionDidFinishLoading:(NSURLConnection *)connection {
dispatch_async(dispatch_get_main_queue(), ^{
[self saveLocally:_videoData];
NSLog(#"File Saved !");
});
}
NSURLConnection enqueues delegate calls in a run loop (NSRunLoop), which is fine if you create a NSURLConnection from a thread that has one (like the main thread).
But you are using grand central dispatch (GCD) which creates threads and destroys / deactivates them as it likes. I don't really see the need to move download logic to a separate thread, as only the callbacks will be invoked on your main thread (which is pretty useful if you want to do UI work) but NSURlConnection will do its download magic on a background thread.
If you want to do download data processing in the background while downloading, you might want to look into NSURLSession (iOS 7) as it supports GCD queues.
I am downloading a bunch of largish zip files with the following method. It can take a little while and so I'd like to display a progress bar.
I've researched how to do with with the delegate methods for NSURLConnection and it seems straightforward, however I want to achieve the same thing with "sendAsynchronousRequest". How can I get the number of bytes downloaded as it downloads as well as the total number of bytes expected so that I can display a progress bar? I understand that I cannot use the delegate methods if I kick off a download in the manner I am doing it.
// Begin the download process
- (void)beginDownload:(NSMutableArray *)requests {
// Now fire off a bunch of requests asynchrounously to download
self.outstandingRequests = [requests count];
for (NSURLRequest *request in requests) { // Get the request
[NSURLConnection sendAsynchronousRequest:request queue:[NSOperationQueue mainQueue] completionHandler:^(NSURLResponse *response, NSData *data, NSError *error) {
// Error check
if ( error != nil ) {
// The alertview for login failed
self.appDelegate.warningView.title = #"Refresh Error!";
self.appDelegate.warningView.message = [error localizedDescription];
// Show the view
[self.appDelegate.warningView show];
// Debug
if ( DEBUG ) {
NSLog(#"A request failed - %d left!",self.outstandingRequests);
}
}
else {
// Debug
if ( DEBUG ) {
NSLog(#"A request is done - %d left!",self.outstandingRequests);
}
}
// Decrement outstanding requests
self.outstandingRequests--;
// No requests are left
if (self.outstandingRequests == 0) {
// Debug
if ( DEBUG ) {
NSLog(#"All requests are done!");
}
// Get rid of loading view
[self performSelector:#selector(dismissLoadingView) withObject:nil afterDelay:0.15];
}
}];
}
}
https://developer.apple.com/library/ios/documentation/Foundation/Reference/NSURLConnectionDownloadDelegate_Protocol/NSURLConnectionDownloadDelegate/NSURLConnectionDownloadDelegate.html#//apple_ref/doc/uid/TP40010954-CH2-SW1
How to make an progress bar for an NSURLConnection when downloading a file?
http://iphonedevsdk.com/forum/iphone-sdk-development/24233-nsurlconnection-with-uiprogressbar.html
http://iphoneeasydevelopment.blogspot.com/2011/10/use-progess-bar-when-downloading-file.html
sendAsynchronousRequest won't work for your purposes as it doesn't call your callback until the request has completed. You'll need to use initRequest:withDelegate: and handle your own data accumulation.
When the header is received (possibly multiple times for redirects) your didReceiveResponse method will be called, you can pick up the expected size there:
-(void)connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)response
{
_expectedBytes = (NSUInteger)response.expectedContentLength;
_data = [NSMutableData dataWithCapacity:_expectedBytes];
// make a progress update here
}
You'll receive a call to the delegate method didReceiveData each time a chunk of data is received, so you know how much data you've received up to this point.
-(void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data
{
[_data appendData:data];
_receivedBytes = _data.length;
// make a progress update here
}
I've read through tons of messages saying the same thing all over again : when you use a NSURLConnection, delegate methods are not called. I understand that Apple's doc are incomplete and reference deprecated methods, which is a shame, but I can't seem to find a solution.
Code for the request is there :
// Create request
NSURL *urlObj = [NSURL URLWithString:url];
NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:urlObj cachePolicy:NSURLRequestReloadIgnoringCacheData timeoutInterval:30];
[request setValue:#"gzip" forHTTPHeaderField:#"Accept-Encoding"];
if (![NSURLConnection canHandleRequest:request]) {
NSLog(#"Can't handle request...");
return;
}
// Start connection
dispatch_async(dispatch_get_main_queue(), ^{
self.connection = [[NSURLConnection alloc] initWithRequest:request delegate:self startImmediately:YES]; // Edited
});
...and code for the delegate methods is here :
- (void) connection:(NSURLConnection *)_connection didReceiveResponse:(NSURLResponse *)response {
NSLog(#"Receiving response: %#, status %d", [(NSHTTPURLResponse*)response allHeaderFields], [(NSHTTPURLResponse*) response statusCode]);
self.data = [NSMutableData data];
}
- (void) connection:(NSURLConnection *)_connection didFailWithError:(NSError *)error {
NSLog(#"Connection failed: %#", error);
[self _finish];
}
- (void) connection:(NSURLConnection *)_connection didReceiveData:(NSData *)_data {
[data appendData:_data];
}
- (void)connectionDidFinishDownloading:(NSURLConnection *)_connection destinationURL:(NSURL *) destinationURL {
NSLog(#"Connection done!");
[self _finish];
}
There's not a lot of error checking here, but I've made sure of a few things :
Whatever happens, didReceiveData is never called, so I don't get any data
...but the data is transfered (I checked using tcpdump)
...and the other methods are called successfully.
If I use the NSURLConnectionDownloadDelegate instead of NSURLConnectionDataDelegate, everything works but I can't get a hold on the downloaded file (this is a known bug)
The request is not deallocated before completion by bad memory management
Nothing changes if I use a standard HTML page somewhere on the internet as my URL
The request is kicked off from the main queue
I don't want to use a third-party library, as, ultimately, these requests are to be included in a library of my own, and I'd like to minimize the dependencies. If I have to, I'll use CFNetwork directly, but it will be a huge pain in the you-know-what.
If you have any idea, it would help greatly. Thanks!
I ran into the same problem. Very annoying, but it seems that if you implement this method:
- (void)connectionDidFinishDownloading:(NSURLConnection *)connection destinationURL:(NSURL *)destinationURL
Then connection:didReceiveData: will never be called. You have to use connectionDidFinishLoading: instead... Yes, the docs say it is deprecated, but I think thats only because this method moved from NSURLConnectionDelegate into NSURLConnectionDataDelegate.
I like to use the sendAsynchronousRequest method.. there's less information during the connection, but the code is a lot cleaner.
[NSURLConnection sendAsynchronousRequest:request queue:[[NSOperationQueue alloc] init] completionHandler:^(NSURLResponse *response, NSData *data, NSError *error){
if (data){
//do something with data
}
else if (error)
NSLog(#"%#",error);
}];
From Apple:
By default, a connection is scheduled on the current thread in the
default mode when it is created. If you create a connection with the
initWithRequest:delegate:startImmediately: method and provide NO for
the startImmediately parameter, you can schedule the connection on a
different run loop or mode before starting it with the start method.
You can schedule a connection on multiple run loops and modes, or on
the same run loop in multiple modes.
Unless there is a reason to explicitly run it in [NSRunLoop currentRunLoop],
you can remove these two lines:
[connection scheduleInRunLoop:[NSRunLoop currentRunLoop] forMode:NSRunLoopCommonModes];
[connection start];
or change the mode to NSDefaultRunLoopMode
NSURLConnection API says " ..delegate methods are called on the thread that started the asynchronous load operation for the associated NSURLConnection object."
Because dispatch_async will start new thread, and NSURLConnection will not pass to that other threat the call backs, so do not use dispatch_async with NSURLConnection.
You do not have to afraid about frozen user interface, NSURLConnection providing only the controls of asynchronous loads.
If you have more files to download, you can start some of connection in first turn, and later they finished, in the connectionDidFinishLoading: method you can start new connections.
int i=0;
for (RetrieveOneDocument *doc in self.documents) {
if (i<5) {
[[NSURLConnection alloc] initWithRequest:request delegate:self];
i++;
}
}
..
-(void)connectionDidFinishLoading:(NSURLConnection *)connection
{
ii++;
if(ii == 5) {
[[NSURLConnection alloc] initWithRequest:request delegate:self];
ii=0;
}
}
One possible reason is that the outgoing NSURLRequest has been setup to have a -HTTPMethod of HEAD. Quite hard to do that by accident though!
hi I have an UITableView. It loads numberof data from a web service. What I want to load this tableview 10 by 10.Initially it loads first 10 items. When user scroll to the end of the UITableView it should load next 10 of records from the server. so in my scrollviewDidEndDeclarating delegate I put like this
`
if (scrollView.tag==24) {
[self performSelector:#selector(loadingalbumsongs:) withObject:nil afterDelay:0.1];
}`
but the problem is when I stop the scroll it is getting stuck untill load the table view. Can anybody give me a solution for this
Thanks
Try NSURLCONNECTION that will help you to call asynchronous webservice
A NSURLConnection object is used to perform the execution of a web service using HTTP.
When using NSURLConnection, requests are made in asynchronous form. This mean that you don't wait the end of the request to continue,
This delegate must have to implement the following methods :
connection:didReceiveResponse : called after the connection is made successfully and before receiving any data. Can be called more than one time in case of redirection.
connection:didReceiveData : called for each bloc of data.
connectionDidFinishLoading : called only one time upon the completion of the request, if no error.
connection:didFailWithError : called on error.
EXAMPLE: -
NSData *data = [[NSMutableData alloc] init];
NSURL *url_string = [NSURL URLWithString:
#"Your URL"];
NSURLRequest *request = [NSURLRequest requestWithURL:url_string];
NSURLConnection *conn = [[NSURLConnection alloc] initWithRequest:request
delegate:self];
if (!conn) {
// this is better if you #throw an exception here
NSLog(#"error while starting the connection");
[data release];
}
for each block of raw data received you can append your data here in this method :
- (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)someData {
[data appendData:someData];
}
connectionDidFinishLoading will call at the end of successfully data receivied
use this code for load more action
- (void)scrollViewDidScroll:(UIScrollView *)scrollView
{
//scrollView.contentSize.height-scrollView.frame.size.height indicates UItableView scrool end
if (scrollView.contentOffset.y >= scrollView.contentSize.height-scrollView.frame.size.height)
{
if(loadMore)
{
loadmore=no;
//call your Web service
}
}
}