In my application i am using ASINetworkQueue method for downloading files. I have n number of buttons and for each button click different Queues will be created with multiple Requests, and starts download asynchronously (multiple download). It is working fine, but I couldn't track when each Queue completes. I have used,
[self.myQueue cancelAllOperations];
[self.myQueue setDownloadProgressDelegate:currentProgress];
[self.myQueue setDelegate:self];
[self.myQueue setShowAccurateProgress:YES];
[self.myQueue setRequestDidFinishSelector:#selector(requestFinished:)];
[self.myQueue setQueueDidFinishSelector:#selector(queueComplete:)];
and added requests as,
ASIHTTPRequest *request = [ASIHTTPRequest requestWithURL:[NSURL URLWithString:str]];
[request setDownloadProgressDelegate:currentProgress];
[request setShowAccurateProgress:YES];
[request setDelegate:self];
[request shouldContinueWhenAppEntersBackground];
[request allowResumeForFileDownloads];
[request startAsynchronous];
[self.myQueue addOperation:request];
and last, [self.myQueue go];
and the delegate method is
- (void)queueComplete:(ASINetworkQueue *)queue
{
NSLog(#"Queue completed");
}
but its not called at the end. What went wrong here? Any idea?
And also, if there are multiple Queues performing at the same time, how can we differentiate which queue is completed at the end?
Edit:
- (void)download{
UIImageView *image = (UIImageView *)[mainView viewWithTag:selectedTag];
for (UIProgressView *currentProgress in [image subviews]) {
if ([currentProgress isKindOfClass:[UIProgressView class]]) {
NSLog(#"Prog tag: %d",currentProgress.tag);
if(currentProgress)
{
currentProgress.progress = 0.0;
[[self myQueue] cancelAllOperations];
[self setNetworkQueue:[ASINetworkQueue queue]];
[[self myQueue] setDownloadProgressDelegate:currentProgress];
[[self myQueue] setDelegate:self];
[[self myQueue] setShowAccurateProgress:YES];
ASIHTTPRequest *request;
[myQueue setQueueDidFinishSelector:#selector(queueComplete:)];
for (int h = 0; h < [urlArray count]; h++) {
request = [ASIHTTPRequest requestWithURL:[NSURL URLWithString:[urlArray objectAtIndex:h]]];
[[self myQueue] addOperation:request];
}
[[self myQueue] go];
}
}
}
}
You are starting the request before adding it to the queue.
[request startAsynchronous];
The starting of the request should only be handled by the queue. So remove that line and the complete callback should be called.
When you have more than one queue, you should keep a reference to each queue. You could put them in an NSMutableArray. Then in the complete callback you can check which queue is complete by checking them against the queues in your array.
EDIT:
It seems that you forget to set your myQueue iVar, so it was nil and ignored all method calls. So add:
self.myQueue = [ASINetworkQueue queue];
To have more than one queue in an array:
self.queues = [NSMutableArray array] // queues is a retained property
ASINetworkQueue *queue = [ASINetworkQueue queue];
[queues addObject:queue];
Related
i want to download a file and show the progress bar
i was able to do this.
now , i want to show the progress value in a label and use this code to progress init and update label :
[queue setDelegate:self];
[queue setRequestDidFinishSelector:#selector(updateLabel)];
[queue setDownloadProgressDelegate:progress];
[queue setShowAccurateProgress:YES];
ASIHTTPRequest *request;
request = [ASIHTTPRequest requestWithURL:url];
[request setDelegate:self];
[request setTemporaryFileDownloadPath:[filePath stringByAppendingString:#".download"]];
[request setAllowResumeForFileDownloads:YES];
[request setDidFinishSelector:#selector(updateLabel)];
[request setDidReceiveDataSelector:#selector(updateLabel)];
[request setShouldContinueWhenAppEntersBackground:YES];
[request setShouldAttemptPersistentConnection:NO];
[request setDownloadDestinationPath:filePath];
[queue addOperation:request];
[queue go];
but not save in the destination path !
and when i clear this code :
[request setDidReceiveDataSelector:#selector(updateLabel)];
saving done !
what is problem ?
i want to update label text when progress value changed
This is what something you need to do with the Main Thread. Updating the UI of the application is performed by the main thread rather than any of the background thread.
Or
alternatively you can use the below code snippet which works for me :
- (void)fetchThisURLFiveTimes:(NSURL *)url
{
ASIHTTPRequest *request = [ASIHTTPRequest requestWithURL:url];
[request addOperation:request];
[request cancelAllOperations];
[request setDownloadProgressDelegate:myProgressIndicator];
[request setDelegate:self];
[request setRequestDidFinishSelector:#selector(queueComplete:)];
[request go];
}
- (void)queueComplete:(ASINetworkQueue *)queue
{
NSLog(#"Value: %f", [myProgressIndicator progress]);
[self performSelectorOnMainThread:#selector(updateLabel) withObject:nil waitUntilDone:NO];
}
I have been using ASIHTTPRequest to fetch the data and i want to cancel the request how i do it??
i do the code just like this..
-(void) serachData{
NSURL *url= [NSURL URLWithString:self.safestring];
ASIFormDataRequest *request = [ASIFormDataRequest requestWithURL:url];
[request setTimeOutSeconds:7200];
[request setDelegate:self];
[request startAsynchronous];
}
- (NSMutableDictionary *)requestFinished:(ASIHTTPRequest *)request
{
NSLog(#"requestFinished");
NSString *responseString = [request responseString];
SBJsonParser *json = [[SBJsonParser alloc] init];
NSMutableArray *array = [[NSMutableArray alloc] initWithObjects[jsonobjectWithString:responseString], nil];
NSLog(#"array %#",array);
}
- (void)requestFailed:(ASIHTTPRequest *)request{
NSLog(#"requestFailed");
}
//if i press cancel button(when requestFinished /requestFailed method in process ) then the ASIHTTPRequest fail and finish method Stop /abort! how i do this??
-(IBAction)CancleREquest:(id)sender{
NSLog(#"CancleREquest");
}
Your cancel specific ASIHTTPRequest then :
if(![yourASIHTTPRequest isCancelled])
{
// Cancels an asynchronous request
[yourASIHTTPRequest cancel];
// Cancels an asynchronous request, clearing all delegates and blocks first
[yourASIHTTPRequest clearDelegatesAndCancel];
}
Note : To cancel all ASIHTTPRequest then :
for (ASIHTTPRequest *request in ASIHTTPRequest.sharedQueue.operations)
{
if(![request isCancelled])
{
[request cancel];
[request setDelegate:nil];
}
}
EDIT : Use AFNetworking as ASIHTTPRequest is deprecated as its has not been update since march 2011.
Nice simple version:
for (ASIHTTPRequest *request in ASIHTTPRequest.sharedQueue.operations){
[request cancel];
[request setDelegate:nil];
}
I suggest you to keep a reference to the pending request in an ivar/property of your controller, then send the cancel message to it from your button handler.
//-- in your class interface:
#property (nonatomic, assign) ASIFormDataRequest *request;
....
//-- in your class implementation:
#synthesize request;
.....
-(void) serachData{
NSURL *url= [NSURL URLWithString:self.safestring];
self.request = [ASIFormDataRequest requestWithURL:url];
[self.request setTimeOutSeconds:7200];
[self.request setDelegate:self];
[self.request startAsynchronous];
}
-(IBAction)CancleREquest:(id)sender{
[self.request cancel];
NSLog(#"request Canceled");
}
You have several options when canceling, though; from ASIHTTPRequest docs:
Cancelling an asynchronous request
To cancel an asynchronous request (either a request that was started with [request startAsynchronous] or a request running in a queue you created), call [request cancel]. Note that you cannot cancel a synchronous request.
Note that when you cancel a request, the request will treat that as an error, and will call your delegate and/or queue’s failure delegate method. If you do not want this behaviour, set your delegate to nil before calling cancel, or use the clearDelegatesAndCancel method instead.
// Cancels an asynchronous request
[request cancel]
// Cancels an asynchronous request, clearing all delegates and blocks first
[request clearDelegatesAndCancel];
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.
I have a problem with hiding modalviewcontroller when I connect to server with ASIHttpRequest.
I connect in background thread and show modalview in main thread.
This is my code:
[self performSelectorInBackground:#selector(loginServerRequest) withObject:nil];
- (void)loginServerRequest {
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
NSURL *url = [NSURL URLWithString:#"https://11.111.111.11/api/login"];
ASIFormDataRequest *request = [ASIFormDataRequest requestWithURL:url];
[request setPostValue:[AccountSettings getCompany] forKey:#"companyName"];
[request setPostValue:[AccountSettings getEmail] forKey:#"email"];
[request setPostValue:[AccountSettings getPassword] forKey:#"password"];
[request setRequestMethod:#"POST"];
[request setTimeOutSeconds:10];
[request setValidatesSecureCertificate:NO];
[request setDelegate:self];
[request startSynchronous];
[pool drain];
}
- (void)requestFinished:(ASIHTTPRequest *)request
{
[self performSelector:#selector(hideServerConnectView) withObject:nil afterDelay:0.0];
int status = [request responseStatusCode];
NSLog(#"%i", status);
if ([self.nibName isEqualToString:#"RootViewController"]) {
if (status == 200) {
//some code
}
}
}
- (void)hideServerConnectView {
[self.parentViewController dismissModalViewControllerAnimated:NO];
}
If server responses immediately modalviewcontroller doesn't hide!
If pass some seconds then everything is okay.
What's the problem??
I changed my code like this:
[self loginServerRequest];
ServerConnectView *viewC = [[ServerConnectView alloc] init];
[self.view addSubview:viewC.view];
[self presentModalViewController:viewC animated:YES];
[viewC release];
- (void)loginServerRequest {
NSURL *url = [NSURL URLWithString:#"https://11.111.111.11/api/login"];
ASIFormDataRequest *request = [ASIFormDataRequest requestWithURL:url];
[request setPostValue:[AccountSettings getCompany] forKey:#"companyName"];
[request setPostValue:[AccountSettings getEmail] forKey:#"email"];
[request setPostValue:[AccountSettings getPassword] forKey:#"password"];
[request setRequestMethod:#"POST"];
[request setTimeOutSeconds:10];
[request setValidatesSecureCertificate:NO];
[request setDelegate:self];
[request startAsynchronous];
}
- (void)requestFinished:(ASIHTTPRequest *)request{
[self hideServerConnectView];
int status = [request responseStatusCode];
NSLog(#"%i", status);
if ([self.nibName isEqualToString:#"RootViewController"]) {
if (status == 200) {
//some code
}
}
}
- (void)hideServerConnectView {
[self.parentViewController dismissModalViewControllerAnimated:NO];
}
And it didn't solve my problem.
Any ideas? Or something wrong?
You're mixing async and sync methods.
You set up the request as though it's an async request, but then call [request startSynchronous];.
Because of this, the delegate methods will not be called and your modal will not be dismissed.
The fix is to fire off the request async, using [request startAsynchronous];
This also means that you don't need to call performSelectorInBackground (or setup the autorelease pool in the loginServerRequest method).
In your asynchronous version, move [self hideServerConnectView]; just after [self loginServerRequest];
OR use - (void)performSelectorOnMainThread:(SEL)aSelector withObject:(id)arg waitUntilDone:(BOOL)wait to invoke - (void)hideServerConnectView since UI update must occur on the Main Thread.
I have this method:
-(void)updateSomething
{
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
NSURL *url = [NSURL URLWithString:#"some url"];
ASIFormDataRequest *httpRequest = [ASIFormDataRequest requestWithURL:url];
[httpRequest startSynchronous];
//some other stuff
[pool drain];
}
I call this method onapplicationDidFinishLaunching
IF i call it on the main thread it works fine
[self getMyItems];
But when I call it on a separate thread I get a "Program received signal: "EXC_BAD_ACCESS"
[self performSelectorInBackground:#selector(getMyItems) withObject:nil];
Any idea how to resolve this issue?
Why perform individual requests on separate threads when you can use ASINetworkQueue?
ASINetworkQueue *aQueue = [[ASINetworkQueue alloc] init];
[aQueue addOperation:requestToAdd];
[aQueue setDelegate:self];
[aQueue setRequestDidFinishSelector:#selector(requestFinished:)];
[aQueue setRequestDidFailSelector:#selector(requestFailed:)];
[aQueue setQueueDidFinishSelector:#selector(queueFinished:)];
[aQueue go];
ASINetworkQueue is a subclass of NSOperationQueue and ASI*Requests are run on separate threads.