ios - How to cancel an AFHTTPRequestOperation and not retain previous progress -


i have app used test afnetworking api. downloads documents server , places them in app's sand box. user may start download, pause/resume download , cancel download. starting, pausing , resuming work expected. cancellation few things not expect.

the app

each cell in table represents "download" model. table view controller listens taps in cells , sends messages begin/cancel/pause/resume downloading. have class downloadmanager keeps track of download model objects. have third class affiledownloadapiclient (using afhttpclient pattern recommended afnetworking authors). downloadmanager calls appropriate messages on affiledownloadapiclient in turn calls appropriate method on nsoperation.

a simple afnetworking download tracking app

the code

the method below creates new afhttprequestoperation, streams bits file (this working fine), , throws in queue starts operation me. couple things note: 1) have put meta data in "content-disposition" header content length , resulting file name, because neither known when download begins. remember bits being streamed me. 2) affiledownloadapiclient keeps dictionary integer index keys , afhttprequestoperation each download corresponds index in uitableview. found necessary retrieve operation later pause, resume, etc...

this in affiledownloadapiclient:

- (void)downloadfilewithindex:(int)index filename:(nsstring *)filename {  // using standard request operation afhttprequestoperation *operation = [[afhttprequestoperation alloc] initwithrequest:request];  operation.inputstream = [nsinputstream inputstreamwithurl:request.url]; operation.outputstream = [nsoutputstream outputstreamtofileatpath:fileindocumentspath append:yes];   // block level variables //  __weak afhttprequestoperation *weakoperation = operation;   // use in download progress block  __weak nsdate *starttime = [nsdate date];  [operation setcompletionblockwithsuccess:^(afhttprequestoperation *operation, id responseobject) {      nshttpurlresponse *response = (nshttpurlresponse*)weakoperation.response;     nsstring *contentdisposition = [[response allheaderfields] objectforkey:@"content-disposition"];     nsarray *dispositionmetadata = [contentdisposition componentsseparatedbystring:@";"];      nsstring *filename = @"<?>";      // 3rd item file name     if (dispositionmetadata != nil && dispositionmetadata.count == 4)     {         filename = [dispositionmetadata objectatindex:2];     }      if ([_downloadfilerequestdelegate respondstoselector:@selector(downloadfilerequestfinishedwithdata:filename:atindex:starttime:)])         [_downloadfilerequestdelegate downloadfilerequestfinishedwithdata:responseobject filename:filename atindex:index starttime:starttime];  }  failure:^(afhttprequestoperation *operation, nserror *error) {          if ([_downloadfilerequestdelegate respondstoselector:@selector(downloadfilerequestfailedwitherror:atindex:starttime:)])             [_downloadfilerequestdelegate downloadfilerequestfailedwitherror:error atindex:index starttime:starttime];     } ];  // check "content-disposition" content-length     [operation setdownloadprogressblock:^(nsuinteger bytesread, long long totalbytesread, long long totalbytesexpectedtoread) {      nshttpurlresponse *response = (nshttpurlresponse*)weakoperation.response;     nsstring *contentdisposition = [[response allheaderfields] objectforkey:@"content-disposition"];     nsarray *dispositionmetadata = [contentdisposition componentsseparatedbystring:@";"];      // 4th item length     if (dispositionmetadata != nil && dispositionmetadata.count == 4)     {         totalbytesexpectedtoread = [[dispositionmetadata objectatindex:3] doublevalue];     }      // notify delegate of progress     if ([_requestprogressdelegate respondstoselector:@selector(requestdidreceivebytesforindex:bytes:totalbytes:)])         [_requestprogressdelegate requestdidreceivebytesforindex:index bytes:bytesread totalbytes:totalbytesexpectedtoread]; }];  // check see if operation in our dictionary  if ([[self.downloadoperations allkeys] containsobject:[nsnumber numberwithint:index]] == yes)     [self.downloadoperations removeobjectforkey:[nsnumber numberwithint:index]];  // add operation storage dictionary [self.downloadoperations setobject:operation forkey:[nsnumber numberwithint:index]];  // queue download operation. no need start operation explicitly [self enqueuehttprequestoperation:operation];  } 

now cancel,pause,resume methods. remember, pause , resume functionality seem work fine.

- (void)canceldownloadforindex:(int)index {  afhttprequestoperation *operation = [self.downloadoperations objectforkey:[nsnumber numberwithint:index]];  if (operation != nil) {      [operation cancel];      // remove object dictionary     [self.downloadoperations removeobjectforkey:[nsnumber numberwithint:index]]; } }  - (void)pausedownloadforindex:(int)index {  afhttprequestoperation *operation = [self.downloadoperations objectforkey:[nsnumber numberwithint:index]];  if (operation != nil)     [operation pause]; }  - (void)resumedownloadforindex:(int)index {  afhttprequestoperation *operation = [self.downloadoperations objectforkey:[nsnumber numberwithint:index]];  if (operation != nil)     [operation resume]; } 

the problem

let's want cancel download half way through. tap "go" wait few seconds. tap "x" cancel. below before / after image. (before on left, after on right).

before/after download , cancel operations

after tap "x" view changes show original "go" button can try again, call ready state (or "before") in case. don't understand when tap "go" second time on same download cancelled, progress indicator picks right original left off @ 1.98 mb.... it's if cancel didn't delete original bytes downloaded, remembers them , continues left off. why?

before/after download , cancel operations

questions

  1. why download after cancellation continue left off?
  2. is behavior expected or unexpected?

i apologize lengthy post , thank reading far....

[edit 1]

in order update progressview in uitableviewcell have @ least these 2 things.

  1. use data model class call download.
  2. use data model manager class managed model download objects, , state.

in table view controller listen bytes received given download @ given index:

- (void)downloaddidreceivebytesforindex:(int)downloadindex bytes:(long long)bytes totalbytes:(double)totalbytes {  nsindexpath *path = [nsindexpath indexpathforrow:downloadindex insection:0];  downloadtableviewcell *cell = (downloadtableviewcell*)[self.tableview cellforrowatindexpath:path];  download *download = [_downloadmanager.downloads objectatindex:path.row]; download.bytesdownloaded += bytes; download.percentagedownloaded = download.bytesdownloaded / totalbytes;  // factor of 0.0 1.0 not 100. cell.downloadprogressview.progress = download.percentagedownloaded;  float mb_conversion_factor = 0.000000953674;  nsstring *bytestext = [nsstring stringwithformat:@"downloading %.2f of %.2f mb", roundf((download.bytesdownloaded * mb_conversion_factor)*100)/100.0, roundf((totalbytes * mb_conversion_factor)*100)/100.0];  cell.downloadprogresslabel.text = bytestext; } 

finally, in order handle scrolling through table , re-use of uitableviewcell objects. have ensure cells created correctly, correspond correct download (at given index) , reflect downloads exact state. of might overkill, seems work well. haven't tested in instruments see if/when i'm leaking anything, though:

- (uitableviewcell *)tableview:(uitableview *)tableview cellforrowatindexpath:(nsindexpath *)indexpath { static nsstring *cellidentifier = @"cell"; downloadtableviewcell *cell = (downloadtableviewcell*)[tableview dequeuereusablecellwithidentifier:cellidentifier];  if (cell == nil) {      uiviewcontroller *temporarycontroller = [[uiviewcontroller alloc] initwithnibname:@"downloadtableviewcell" bundle:nil];     // grab pointer custom cell.     cell = (downloadtableviewcell *)temporarycontroller.view;      [cell initstate];      // listens method calls on cell     cell.delegate = self;      cell.selectionstyle = uitableviewcellselectionstylenone; }  // set index cell (it wrong if cell re-used) cell.downloadindex = indexpath.row;  download *download = [_downloadmanager.downloads objectatindex:indexpath.row]; cell.downloading = download.downloading;  cell.namelabel.text = download.name; cell.descriptionlabel.text = download.description; cell.downloadprogressview.progress = download.percentagedownloaded;  // check completed status cell.completed = download.completed; cell.completedfilenamelabel.text = download.filename;  return cell; } 

it appears if outputstream being released when removed collection in cancel download method. however, no state change made on download instance download canceled. if object continues have totalbytes , percentagedownloaded values set, progress view continue reflect partially downloaded state.


Comments

Popular posts from this blog

node.js - Bad Request - node js ajax post -

Why does Ruby on Rails generate add a blank line to the end of a file? -

keyboard - Smiles and long press feature in Android -