2

my app downloads a video from internet and saves it in the iPhone.

I'm using this code

NSMutableURLRequest *request = [[[NSMutableURLRequest alloc] init] autorelease];

[request setHTTPMethod:@"GET"];
NSError *error;
NSURLResponse *response;


NSString *documentFolderPath = [NSHomeDirectory() stringByAppendingPathComponent:@"Documents"];
NSFileManager *fileManager = [NSFileManager defaultManager];
NSString *videosFolderPath = [documentFolderPath stringByAppendingPathComponent:@"videos"]; 

//Check if the videos folder already exists, if not, create it!!!
BOOL isDir;
if (([fileManager fileExistsAtPath:videosFolderPath isDirectory:&isDir] && isDir) == FALSE) {
    [[NSFileManager defaultManager] createDirectoryAtPath:videosFolderPath withIntermediateDirectories:YES attributes:nil error:nil];
}

NSString *filePath = [videosFolderPath stringByAppendingPathComponent:@"name.mp4"];

if ([fileManager fileExistsAtPath:filePath ] == YES) {
    NSLog (@"File exists");
}
else {
    NSLog (@"File not found");
    NSData *urlData;
    NSString *downloadPath = @"http://www.mywebsite.com/name.mp4";
    [request setURL:[NSURL URLWithString:downloadPath]];
    urlData = [NSURLConnection sendSynchronousRequest:request returningResponse:&response error:&error];

    BOOL written = [urlData writeToFile:filePath atomically:NO];
    if (written)
        NSLog(@"Saved to file: %@", filePath);
    else {NSLog(@"problem");}
}

All works fine, but I want to add a progress View, to indicate the status of the download.

The problem (I think but I'm not sure) is that my NSURLConnection hasn't itself as delegate, so methods like

- (void)connection:(NSURLConnection *)theConnection didReceiveData:(NSData *)data 

or

-(void)connection:(NSURLConnection *)connection didSendBodyData:(NSInteger)bytesWritten totalBytesWritten:(NSInteger)totalBytesWritten totalBytesExpectedToWrite:(NSInteger)totalBytesExpectedToWrite{

are never called.

I tried to use

urlData = [[NSURLConnection alloc] initWithRequest:request delegate:self];

but the app crashes when I try to write the file

[urlData writeToFile:filePath atomically:NO];

What can I do? Thanks

JAA
  • 1,024
  • 3
  • 20
  • 34

2 Answers2

5

What you are doing is sending a "synchronous" request, meaning that the thread that is downloading the HTTP response will hang until all data has been fetched. It is never good to do this on a UI thread, even when you do not want display any indicator of the download's progress. I suggest using the NSURLConnection class, setting it's delegate, and responding to delegate methods. A small tutorial for this can be found at http://snippets.aktagon.com/snippets/350-How-to-make-asynchronous-HTTP-requests-with-NSURLConnection.

Once you are a connection's delegate, you can get the content length when the connection receives a response:

- (void)connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)response {
    NSHTTPURLResponse * httpResponse = (NSHTTPURLResponse *)response;   
    contentSize = [httpResponse expectedContentLength];
}

Then, to calculate the total progress of the download whenever new data arrives, do something like this:

- (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data {
    // download data is our global NSMutableData object that contains the
    // fetched HTTP data.
    [downloadData appendData:data];
    float progress = (float)[downloadData length] / (float)contentSize;
    [progressView setValue:progress];
}

UPDATE:

Another example on how to do async HTTP with Foundation: http://codewithchris.com/tutorial-how-to-use-ios-nsurlconnection-by-example/

Alex Nichol
  • 7,512
  • 4
  • 32
  • 30
  • Ok, thaks! it works! But now I have one question: using my old methods, I could get the video using the "document folder", it was easy... Now where is the video with I downloaded? How can I know the path (also for check if already exists etc...)? thanks! – JAA Jul 22 '11 at 14:45
  • sorry, I can use [nameFile writeToFile:Path automatically:NO] to write, and 'fileExists' to check if is available! thanks! – JAA Jul 22 '11 at 16:56
  • Very cool Method. In case you're looking to funk things up, I found this repo for custom progressview: https://github.com/danielamitay/DACircularProgress – Septronic Dec 04 '15 at 01:55
1

You are using synchronous request here.

The document says - A synchronous load is built on top of the asynchronous loading code made available by the class. The calling thread is blocked while the asynchronous loading system performs the URL load on a thread spawned specifically for this load request. No special threading or run loop configuration is necessary in the calling thread in order to perform a synchronous load.

Refer the class reference here

After you have read the class reference, you can either send asynchronous request or initialize the request, set delegate, and start.