I'm using ASIHTTP for download some documents from a server. I have a button that first control the differences between the local version (saved on iPad file system) and the server version and then (after a confirm alert view) starts the downloads.
To download the documents I use this code:
for (SyncNode *node in _syncDocuments) {
//SyncNode is a my Class that handle document
// start the download
NSString *filename = [NSString stringWithFormat:#"%#.%#",node.uniqueIdentifier,node.extension];
NSString *tempPath = [SavedDocument pathToSyncFile:filename];
// tempPath is a string like this
// /var/mobile/Applications/5C05AA6F-55B3-46FD-8330-094BD0E61973/Documents/SyncDocumentsFolder/D49B421C-7528-4A3D-A8F6-942FF03881D0/f2e650e2-cff7-4485-9152-5445bee436f2.jpg
//BaseHTTPRequest is an extension of ASIHTTPRequest
BaseHTTPRequest *request = [[[BaseHTTPRequest alloc] initWithURL:[NSURL URLWithString:node.contentLocation] accountUUID:_uuid] autorelease];
request.userInfo = [NSDictionary dictionaryWithObject:[NSNumber numberWithInt:i+1] forKey:#"requestNumber"];
[request setDelegate:self];
[request setShowAccurateProgress:YES];
[request setDownloadProgressDelegate:self];
[request setDownloadDestinationPath:tempPath];
[request startAsynchronous];
}
Scenario.
I don't have any local file stored on iPad
I push the button and the routine calculate the size of the download
I accept the download and a routine dowloads all documents. When I try to display one of this documents (in a web view) it works fine and I can see the document.
I delete all local documents (the delete process works perfectly, in fact the documents exists on the file system).
Now, I'am in the same situation of 1. but when I try to dowload the same documents again (the second time) all ASIHTTPRequest return and call my - (void)requestFinished:(ASIHTTPRequest *)request method, in a very short time interval.
Example: I have a directory with 8MB of documents; the first time the routine needs 11 or 12 seconds to complete all download. Instead the second time the same routine, with the same documents needs less than 1 second to complete all downloads.
It's seem that the download not run, but ASIHTTP doesn't call any error delegate methods.
When I try to show a document downloaded (the second time) I see this error message in the console:
Error copying file to temp path Error Domain=NSCocoaErrorDomain Code=260 "The operation
couldn’t be completed. (Cocoa error 260.)" UserInfo=0x1229e900
{NSFilePath=/var/mobile/Applications/5C05AA6F-55B3-46FD-8330-
094BD0E61973/Documents/SyncDocumentsFolder/D49B421C-7528-4A3D-A8F6-942FF03881D0/f2e650e2-cff7-
4485-9152-5445bee436f2.jpg, NSUnderlyingError=0x1229e570 "The operation couldn’t be completed.
No such file or directory"}
This problem drive me crazy !
Any ideas ?
Try to set a cache policy for your request using setCachePolicy propery.
[request setCachePolicy:(ASIDoNotReadFromCacheCachePolicy | ASIDoNotWriteToCacheCachePolicy)];
For more informations read this: http://allseeing-i.com/ASIHTTPRequest/How-to-use#using_a_download_cache
Related
I have four urls which consists images...
I'm downloding those images and placing them into documents folder..
here is my code..
-(void)viewDidLoad
{
NSMutableArray *myUrlsArray=[[NSMutableArray alloc]init];
[myUrlsArray addObject:#"http://blogs.sfweekly.com/thesnitch/steve_jobs3.jpg"];
[myUrlsArray addObject:#"http://www.droid-life.com/wp-content/uploads/2012/12/Steve-Jobs-Apple.jpg"];
[myUrlsArray addObject:#"http://2.bp.blogspot.com/-T6nbl0rQoME/To0X5FccuCI/AAAAAAAAEZQ/ipUU7JfEzTs/s1600/steve-jobs-in-time-magazine-front-cover.png"];
[myUrlsArray addObject:#"http://images.businessweek.com/ss/08/09/0929_most_influential/image/steve_jobs.jpg"];
[myUrlsArray addObject:#"http://cdn.ndtv.com/tech/gadget/image/steve-jobs-face.jpg"];
for (int i=0; i<myUrlsArray.count; i++)
{
[self downloadImageFromURL:[myUrlsArray objectAtIndex:i] withName:[NSString stringWithFormat:#"MyImage%i.jpeg",i]];
}
}
#pragma mark- downloading File
-(void)downloadImageFromURL:(NSString *)myURLString withName:(NSString *)fileName
{
UIImage *image = [[UIImage alloc] initWithData:[NSData dataWithContentsOfURL:[NSURL URLWithString:myURLString]]];
NSLog(#"%f,%f",image.size.width,image.size.height);
// Let's save the file into Document folder.**
NSString *documentsPath = [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory,NSUserDomainMask, YES) objectAtIndex:0];
NSString *jpegPath = [NSString stringWithFormat:#"%#/%#",documentsPath,fileName];// this path if you want save reference path in sqlite
NSData *data2 = [NSData dataWithData:UIImageJPEGRepresentation(image, 1.0f)];//1.0f = 100% quality
[data2 writeToFile:jpegPath atomically:YES];
}
NOW... I need to display a UIProgressView for above downloading progress accurately.
how can i achieve this functionality...
Can any one provide some guidelines to achieve this..
Thanks in advance...
I'd suggest you use some asynchronous downloading technique (either AFNetworking, SDWebImage, or roll your own with delegate-based NSURLSession) rather than dataWithContentsOfURL so that (a) you don't block the main queue; and (b) you can get progress updates as the downloads proceed.
I'd also suggest creating a NSProgress for each download. When your delegate method gets updates about how many bytes have been downloaded, update the NSProgress object.
You then can associate each NSProgress with a observedProgress for a UIProgressView, and when you update your NSProgress, the UI can be updated automatically.
Or, if you and a single UIProgressView to show the aggregate progress of all of the NSProgress for each download, you can create a parent NSProgress, establish each download's NSProgress as a child of the parent NSProgress, and then, as each download updates its respective NSProgress, this will automatically trigger the calculation of the parent NSProgress. And again, you can tie that parent NSProgress to a master UIProgressView, and you'll automatically update the UI with the total progress, just by having each download update its individual NSProgress.
There is a trick, though, insofar as some web services will not inform you of the number of bytes to be expected. They'll report an "expected number of bytes" of NSURLResponseUnknownLength, i.e. -1! (There are logical reasons why it does that which are probably beyond the scope of this question.) That obviously makes it hard to calculate what percentage has been downloaded.
In that case, there are a few approaches:
You can throw up your hands and just use an indeterminate progress indicator;
You can try changing the request such that web service will report meaningful "expected number of bytes" values (e.g. https://stackoverflow.com/a/22352294/1271826); or
You can use an "estimated download size" to estimate the percentage completion. For example, if you know your images are, on average, 100kb each, you can do something like the following to update the NSProgress associated with a particular download:
if (totalBytesExpectedToWrite >= totalBytesWritten) {
self.progress.totalUnitCount = totalBytesExpectedToWrite;
} else {
if (totalBytesWritten <= 0) {
self.progress.totalUnitCount = kDefaultImageSize;
} else {
double written = (double)totalBytesWritten;
double percent = tanh(written / (double)kDefaultImageSize);
self.progress.totalUnitCount = written / percent;
}
}
self.progress.completedUnitCount = totalBytesWritten;
This is a bit of sleight of hand that uses the tanh function to return a "percent complete" value that smoothly and asymptotically approaches 100%, using the kDefaultImageSize as the basis for the estimation.
It's not perfect, but it yields a pretty decent proxy for percent completion.
Your call to dataWithContentsOfURL is synchronous, meaning you don't get updates as the download is in process.
You can use a library like AFNetworking (https://github.com/AFNetworking/AFNetworking) which has callbacks to the progress of the download.
Actually a better solution is to use SDWebImage manager which will load the images in the background for you and cache them. Then the next time you use that image it will check the cache. Google it.
That way the user also doesn't have to sit around and wait while you're downloading stuff..
Then look at this other question that has some ideas on how to do a status:
How to show an activity indicator in SDWebImage
Do not use dataWithContentsOfURL, you are blocking the main thread until the data arrives.
Instead create your own connection with NSURLConnection and start listening to your delegate.
connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)response: get the total data size with [response expectedContentLength].
connection:(NSURLConnection *)connection didReceiveData:(NSData *)data: This is where you do your calculations and update your UIProgressView. Something like, loadedBytes/total data size.
Good luck.
So far I know how to stream a video and how to download it and afterwards stream it, but here's the tricky bit: streaming it once, storing it on the device and in the future play it from the device.
Is that possible?
Not quite sure here how you get your stream but look in to the AVAssetWriter, AVAssetWriterInput and AVAssetWriterPixelBufferAdaptor and as soon as you receive data you should be able to append the data to the to the pixel buffer adaptor using:
appendPixelBuffer:withPresentationTime:
not sure it will work for you but with some fiddling you should be able to adapt your input to match this method. There are lots of example code for setting up the writer
It's quite easy to save the video. Do something similar to this:
//Saving Movie
NSMutableData *data = [[NSMutableData alloc] init];
NSKeyedArchiver *archiver = [[NSKeyedArchiver alloc] initForWritingWithMutableData:data];
[archiver encodeObject:*MovieObject* forKey:#"MovieObjectDataKey"];
[archiver finishEncoding];
[[NSUserDefaults standardUserDefaults] setObject:data forKey:#"MovieObjectDefaultsDataKey"];
[archiver release];
[data release];
//Retrieving movie
NSData *savedMovieData = [[NSUserDefaults standardUserDefaults] objectForKey:#"MovieObjectDefaultsDataKey"];
if (savedMovieData != nil) {
NSKeyedUnarchiver *unarchiver = [[NSKeyedUnarchiver alloc] initForReadingWithData:savedMovieData];
*MovieObject* = [[unarchiver decodeObjectForKey:#"MovieObjectDataKey"] retain];
[unarchiver finishDecoding];
[savedMovieData release];
[unarchiver release];
} else {
//Download Stream of Your Movie
}
The only thing you really have to change there is * MovieObject *, once in each step.
I know what you want to achieve, I only got a workaround. I had to implement the same behavior and ended up with streaming the video from the server and downloading it next to streaming. Next time the user tries to stream the video determine whether it was downloaded to disk, otherwise stream it again. In a normal case the video was downloaded properly and could be reviewed offline.
BOOL fileExists = [[NSFileManager defaultManager] fileExistsAtPath:somePath];
and
fileURLWithPath:isDirectory:
Initializes and returns a newly created NSURL object as a file URL with a specified path.
+ (id)fileURLWithPath:(NSString *)path isDirectory:(BOOL)isDir
Parameters
path
The path that the NSURL object will represent. path should be a valid system path. If path begins with a tilde, it must first be expanded with stringByExpandingTildeInPath. If path is a relative path, it is treated as being relative to the current working directory.
Passing nil for this parameter produces an exception.
isDir
A Boolean value that specifies whether path is treated as a directory path when resolving against relative path components. Pass YES if the path indicates a directory, NO otherwise.
Return Value
An NSURL object initialized with path.
Availability
Available in iOS 2.0 and later.
You can't stream it and save it at the same time, especially with large video files as the Apple doc sais that you must use a transport stream for HTTP Live Streaming.
ASIHttpRequest might make your life easier.
ASIHTTPRequest *request = [ASIHTTPRequest requestWithURL:url];
[request setDownloadDestinationPath:#"video.m4v"]; // use [NSBundle mainBundle] to find a better place
From your delegate, handle this:
- (void)request:(ASIHTTPRequest *)request didReceiveData:(NSData *)data;
Do whatever data transcoding with data as you get it and push it off to your AVAssetWriter or movie player layer in real time, whatever you are using. When you're done, the asset should still be saved so you can get it later.
Our iPad app can show Documents and save them offline when needed.
I've got a QLPreviewController subclass named DocumentViewController (named DVC from now on) for showing them.
Workflow of the app:
- The user clicks a name of a document and the DVC is pushed on to show the document.
- The DVC downloads the file offline and shows it when done.
(So the HTTP URL is downloaded, stored offline, and an offline URL is returned)
The weird thing is, is that only PDF files are working with the offline URL, and the rest crashes.. (it works with online links though)
I did some tests and when I put file:// before the offline link the app does not crash but the DVC is ging me some information about the file (like that it is a excel 97-2004 document).
So some info is transferred, but I can't figure out what the problem is.
Here are some screenshots and after that some code.
code:
Note that Document is a model class with document properties like id, name, file type and url.
//DVC QLPreviewController dataSource method for returning url
- (id <QLPreviewItem>) previewController: (QLPreviewController *) controller previewItemAtIndex: (NSInteger)index
{
[SaveHelper saveDocumentFileAndPropertyWithDocument:document];
//[SaveHelper getDocumentFileWithDocument:document]; without file://
//if I return document.documentUrl it is working with all files except iworks files)
return [SaveHelper getDocumentFileAsPathWithDocument:document]; //with file://
}
//SaveHelper methods
+ (NSString *)documentFilePathWithDocument:(Document *)document
{
return [[self documentFilePath] stringByAppendingPathComponent:[NSString stringWithFormat:#"%#%d.%#", DOCUMENT_FILE_PREFIX, document.documentId, document.documentType]];
}
+ (NSURL *)saveDocumentFileAndPropertyWithDocument:(Document *)document
{
if([self saveDocumentPropertyWithDocument:document])
{
return [self saveDocumentFileWithDocument:document];
}
return nil;
}
+ (NSURL *)saveDocumentFileWithDocument:(Document *)document
{
NSData *data = [NSData dataWithContentsOfURL:document.documentURL];
NSString *fullPath = [self documentFilePathWithDocument:document];
if([[NSKeyedArchiver archivedDataWithRootObject:data] writeToFile:fullPath atomically:YES])
{
return [NSURL fileURLWithPath:fullPath];
}
return nil;
}
+ (NSURL *)getDocumentFileWithDocument:(Document *)document
{
return [NSURL fileURLWithPath:[self documentFilePathWithDocument:document]];
}
+ (NSURL *)getDocumentFileAsPathWithDocument:(Document *)document
{
return [NSURL fileURLWithPath:[#"file://" stringByAppendingPathComponent:[[self getDocumentFileWithDocument:document] absoluteString]]];
}
If more code needed, just say.
EDIT:
When logging the URL passed trough the 'getDocumentFileAsPathWithDocument' method:
url: file:/var/mobile/Applications/xx-xx/Documents/documentFiles/file_20.pdf
url: file:/var/mobile/Applications/xx-xx/Documents/documentFiles/file_80.docx
Where the PDF file is working and the docx not
When I try to load an image(jpg) from local storage I get a black screen with this error message:
warning: Unable to read symbols for /Developer/Platforms/iPhoneOS.platform/DeviceSupport/4.3.5 (8L1)/Symbols/System/Library/Frameworks/QuickLook.framework/DisplayBundles/Image.qldisplay/Image (file not found).
warning: No copy of Image.qldisplay/Image found locally, reading from memory on remote device. This may slow down the debug session.
EDIT:
The webview does not work either with the local urls. PDF is fine but the office files gives an message "Unable to read Document, the file format is invalid". The iWorks documents give the same error as the quicklook. I think its somewhere at the save and load of the format, I savve them as a NSDATA but after that there is no hint for the iPad to see if it is for example a word document (only the extension).
You haven't posted your download code, but I believe that the problem is there. Files from Pages (.pages extension) aren't actual files, they are bundles, i.e. directories that contain files and show as a single item in Finder (look up a .pages file in Finder, right-click it and select 'Show contents'). Downloading a .pages file is actually like downloading a directory: it depends on the web server what kind of result you get but it's most likely an error page.
You could detect that it's a .pages file and try to download all of its contents manually, but you'd have to study the structure of the files to see if that's possible because it's unlikely that you can request the contents of the directory from a web server.
The results for the .ppt and .xls files look normal to me; I think it unlikely that the iPad can preview MS Office documents at all.
Edit: apologies, I just read that iOS can preview MS Office documents. Perhaps the documents get somehow corrupted during download? Have you tried to set your download location to the app's documents folder and enable iTunes file sharing? That way you can download some documents, pull them off your device and then try to open it on your PC to see if that works.
We finally found the solution!
I was right that the problem was with saving the document.
I needed to change the save method in:
+ (NSURL *)saveDocumentFileWithDocument:(Document *)document
{
NSData *data = [NSData dataWithContentsOfURL:document.documentURL options:NSDataReadingUncached error:nil];
NSString *fullPath = [self documentFilePathWithDocument:document];
if([[NSFileManager defaultManager] createFileAtPath:fullPath contents:data attributes:nil])
{
return [NSURL fileURLWithPath:fullPath];
}
//OLD CODE
// if([[NSKeyedArchiver archivedDataWithRootObject:data] writeToFile:fullPath atomically:YES])
// {
// return [NSURL fileURLWithPath:fullPath];
// }
return nil;
}
SO saving it with the filemanager and not with a keyedarchiver.
Did you check if the size of the files is the same both online and offline? It is possible that the file download wasn't complete
Try using the URL of the MS Office documents with a normal NSURL object and opening in a UIWebView. Does it work then (so we know if its the document or your class)?
Try using NSURL's fileURLWithPath: method in the getDocumentFileAsPathWithDocument: It is possible that the URL being returned is incorrect (though doesn't look like it from the logs but doesn't hurt to try)
first of all, use this code to make sure your documents are there,because i think the error cause by your documents path.
NSFileManager *fileManager=[NSFileManager defaultManager];
if([fileManager fileExistsAtPath:fullPath]){
NSLog(#"%# exsit! ",fullPath);
}else{
NSLog(#"%# not exsit! ",fullPath);
}
If any of one have same problem even though you did everything suggestions above.
(I had same problem, when I downloaded some files from google drive.)
Try this!
Put 'x' end of your file extension to be recognized as a new version of format.
(it's working only for 'doc' and 'ppt' files, not for 'xls' files)
Yes, I know this is not a appropriate way to solve this problem, but
it's worth to try it.
Believe me I tried everything!
Hope you help.
I'm trying to create a folder inside the /sounds folder of my app.
-(void)productPurchased:(UAProduct*) product {
NSLog(#"[StoreFrontDelegate] Purchased: %# -- %#", product.productIdentifier, product.title);
NSFileManager *manager = [NSFileManager defaultManager];
NSString *bundleRoot = [[NSBundle mainBundle] bundlePath];
NSError *error;
NSString *dataPath = [NSString stringWithFormat:#"%#/sounds/%#", bundleRoot, product.title];
if (![manager fileExistsAtPath:dataPath isDirectory:YES]) {
[manager createDirectoryAtPath:dataPath withIntermediateDirectories:YES attributes:nil error:&error];
NSLog(#"Creating folder");
}
NSLog(#"%#", error);
}
But I get this error:
Error Domain=NSCocoaErrorDomain Code=513 "The operation couldn’t be completed. (Cocoa error 513.)" UserInfo=0x175120 {NSFilePath=/var/mobile/Applications/D83FDFF9-2600-4056-9047-05F82633A2E4/App.app/sounds/Test Tones, NSUnderlyingError=0x117520 "The operation couldn’t be completed. Operation not permitted"}
What am I doing wrong?
Thanks.
If you search Google on the error domain NSCocoaErrorDomain you find that the code 513 translates to the error NSFileWriteNoPermissionError.
This provides you with the critical clue for solving this problem:
This is the bundle directory containing the application itself. Because an application must be signed, you must not make changes to the contents of this directory at runtime. Doing so may prevent your application from launching later.
Specifically, you cannot modify the contents of a compiled app's bundle folder. This is because the bundle is the compiled application.
When you eventually distribute the app through the iTunes App Store, the application has a digital signature that validates the contents of the app. This signature is generated at compile time.
If you try to change the bundle after compilation, the app changes and the digital signature is no longer valid. This invalidates the application — who knows what code is in there, right? — and so Apple has set up iOS to throw an error if you try to modify the application.
Instead of writing to the bundle, your app can write to one of three accepted app-specific folders: <Application_Home>/Documents, <Application_Home>/tmp and <Application_Home>/Library/Caches.
Most likely, you will want to write to the <Application_Home>/Documents folder.
These folders are only accessible to your app. No other app can access the contents of these folders. (Likewise, your app cannot access another app's folders.)
You can set up your app to allow the end user to manage access to file data through iTunes, via desktop file sharing support.
This is because you should never modify the bundle of your application at runtime. Instead, you should have a folder elsewhere where you can add resources.
EDIT:
The error you are seeing is most likely because you cannot write to the bundle.
I encounter the same problem, when using a Log library. Finally, it's path format problem. Check the dataPath format. If it is Case 1, it is valid. In my case, it's Case 2, so I failed to create directory.
// Case 1
/var/mobile/Containers/Data/Application/5FB2CD2D-91DC-4FB2-8D6F-06369C70BB4A/Library/Caches/AppLogs
// Case 2, invalid format
file://var/mobile/Containers/Data/Application/5FB2CD2D-91DC-4FB2-8D6F-06369C70BB4A/Library/Caches/AppLogs
If the dataPath has a prefix, ex: file://, it is invalid.
As for an instance of NSURL, path will return the string like case 1, and absolutePath will return the string like case 2.
I'm still not totally clear on the meaning of the 513 error in my case, but I was getting it when just trying to read an opened file URL using [NSFileHandle fileHandleForReadingFromURL:theUrl error:&err ].
I realized from this answer that with iOS 13, I now need to use startAccessingSecurityScopedResource to access external files that are opened in the app. When I wrapped my file calls as follows, then the error 513 stopped occurring:
if( [myURL startAccessingSecurityScopedResource] )
{
NSFileHandle* myFile = [NSFileHandle fileHandleForReadingFromURL:myURL error:&err ];
// ...Do file reads here...
[theUrl stopAccessingSecurityScopedResource];
}
In my app i need to download several plist.
to download a plist i use the NSURLconnection
in my code i use an UIAlertView with a UIActivityIndicator then when the download is finished i add a button to the alert to dismiss it.
To download the plist i use somewhere in my code an NSURL set to the adresse where the plist is, next i set a NSURLRequest with the url cache policy and a timeout interval.
Then i set my NSMutableData to the NSURL connection with a NSURLRequest.
In the delegate didReceiveData: i append data to my mutable data object, in the didFailWithError: i handle error. And finaly in the connectionDidFinishLoading i serialize my data to a plist so i can write to file my plist, and release my alertview.
My problem is : how can i do if i have sevetal file to download because the connectionDidFinishLoading is called each time my NSURLConnection is finished but i want to release my UiAlert when everything is finished. But when the first plist is downloaded my code in the connectionDidFinishLoading will fire.
here is my code :
in the view did load :
// set the UiAlert in the view did load
NSURL *theUrl = [NSURL URLWithString:#"http://adress.com/plist/myPlist.plist"];
NSURLRequest *theRequest = [NSURLRequest requestWithURL:theUrl cachePolicy:NSURLRequestUseProtocolCachePolicy timeoutInterval:60.0];
self.plistConnection = [[ NSURLConnection alloc] initwithRequest:theRequest delegate:self startImmediatly:YES];
//plistConnection is a NSURLConnection
- (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data {
[incomingPListData appendData:data];
}
-(void)connection:(NSURLConnection *)connectionDidFailWithError:(NSError *)error {
// handle error here
}
-(void)connectionDidFinisloading:(NSURLConnection *) connection {
NSPropertyListFormat format;
NSString *serialErrorString;
NSData *plist = [NSPropertyListSerialisation propertyListFromData:incomingPlistData mutabilityOption:NSPropertyListImmutable format:&format errorDescription:&serialErrorString];
if (serialErrorString) {//error}
else { // create path and write plist to path}
// change message and title of the alert
so if i want todownload an another file where do i put the request the connection and how can i tell the didFinishLoading to fire code when all my file are downloaded.
thanks to all
You can iterate over an array of resources you wish to download, and allocate a request for each of them. It is possible to set a tag to a connection (e.g. the index of the URL in the array) which you can evaluate in the connectionDidFinishLoading. If you hold the information, which requests are sent and which are finished, you can easily see, if all files have been loaded.
I think unset provided a good answer. I understand that you don't get it (you will someday), as I remember myself being new to programming and such.
I therefore provide another, much simpler option to evaluate if all downloads did finish.
you simply use a counter that you define in your .h file,
int activeDownloads;
in your implementation (.m) File, wherever you start all your downloads, set activeDownloads to zero before any of your downloads start
activeDownloads = 0;
before you start a download you increase the number of activeDownloads
++activeDownloads;
if a download finishes or fails you decrease the same countervariable
- (void)connectionDidFinishLoading:(NSURLConnection *)connection { --activeDownloads;}
(i didn't write down the method that gets called if a download fails...
also everytime a connection finishes or fails you have to check if the one that finished or railed was the last one.
you do that by simply checking if activeDownloads is equal to zero. If that is the case, you can add the "close" Button to your AlertView.
The drawback of this solution is, that you're unable to track which connection succeeded and which failed. (well, you are, but activeDownloads don't help you with that)
hope i could help
cheers