I am new to iPhone,
I am currently developing an iPhone app and would like to implement the ability to download file from the internet. I have created the UIWebView, but want to know the best way of capturing the files when they are linked to in the webview and then download them to a specified folder in the documents directory.
Here is my code snippet,
- (void)connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)response
{
[self.fileData setLength:0];
}
- (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data1
{
[self.fileData appendData:data1];
}
- (void)connectionDidFinishLoading:(NSURLConnection *)connection
{
[activityIndicator stopAnimating];
activityIndicator.hidden=TRUE;
}
- (BOOL)webView:(UIWebView*)webView shouldStartLoadWithRequest:(NSURLRequest*)request navigationType:(UIWebViewNavigationType)navigationType {
url = [request URL];
//CAPTURE USER LINK-CLICK.
NSString *file = [NSString stringWithString:[url absoluteString]];;
NSURL *fileURL = [NSURL URLWithString:file];
NSURLRequest *req = [NSURLRequest requestWithURL:fileURL];
[NSURLConnection connectionWithRequest:req delegate:self];
data = [NSData dataWithContentsOfURL:url];
//Saving file at downloaded path.
DirPath = [DestPath stringByAppendingPathComponent:[url lastPathComponent]];
[data writeToFile:DirPath atomically:YES];
UIAlertView* Alert = [[UIAlertView alloc] initWithTitle:#"Download Complete !"
message:nil delegate:nil
cancelButtonTitle:#"OK"
otherButtonTitles:nil];
[Alert show];
[Alert release];
return YES;
}
Problem is where to write condition, if my downloading gets failed and also i am getting Warning in my log shows : “wait_fences: failed to receive reply: 10004003”
It seems like you're doing the same thing multiple times in this code. For example, you create a new NSURLRequest object even though one was already passed inside of the delegate method? You also run the synchronous dataWithContentsOfURL method after creating a new NSURLConnection? You also append some data into a property only to do nothing with that property?
What you probably want to do is create a new asynchronous NSURLConnection when the UIWebView should load. From there, allow the UIWebView to load that page. Inside of your delegate methods, instead of appending the data to some property, append the downloaded data to a file. When the connection finishes downloading, present your alert informing the user that the data was downloaded and saved.
Related
I currently have an ap that attempts to open a webview based on certain servers I am communicating with.
However, I allow the user the capability to type in their own server IPs in case both the iphone/ipad and server(or other device) are not on the same network. However, I am attempting to use NSURLConnection to detect if I can open a connection with the given IP however NSURLConnection never returns an error, even if the server address(or even a random web address) is completely bogus.
the .h
#interface DetailViewController : UIViewController <UIPopoverControllerDelegate, UISplitViewControllerDelegate, UITableViewDataSource, UITableViewDelegate,UIWebViewDelegate, NSURLConnectionDataDelegate> {
the relevant code in the .m
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:
(NSIndexPath *)indexPath
{
dev_ip = #"http://www.asdfasfdfa.com/";
//dev_ip = (random ip)
NSMutableURLRequest* request = [NSURLRequest requestWithURL:[NSURL URLWithString:dev_ip]];
NSURLConnection *conn = [[NSURLConnection alloc] initWithRequest:request delegate:self];
if (conn) {
NSLog(#"Connection established");
}
else{
UIAlertView *alert = [[UIAlertView alloc] initWithTitle:#"Error" message:[NSString stringWithFormat:#"No Device at designated IP"]
delegate:self cancelButtonTitle:#"Ok" otherButtonTitles:nil, nil];
[alert show];
}
}
this if/else always outputs 'Connection established.' Is this something NSURLConnection should not be used for? If so, what can I use to detect devices at given IP's for connectivity. I need to stop the user from attempting to connect to bad IP's so whats the best method in doing so?
NSURLConnection, when used with delegation, will call delegate methods when it connects, fails to connect and receive data. You should look into NSURLConnectionDelegate.
Here's a quick example:
// In your .h
#interface MyClass : NSObject <NSURLConnectionDelegate, NSURLConnectionDataDelegate>
#end
EDIT You actually need both delegates.
// In your .m
#implementation MyClass
- (void)myMethodToConnect {
NSString *dev_ip = #"http://74.125.137.101"; // Google.com
NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:[NSURL URLWithString:dev_ip]];
NSURLConnection *conn = [[NSURLConnection alloc] initWithRequest:request delegate:self];
}
- (void)connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)response {
switch ([(NSHTTPURLResponse *)response statusCode]) { // Edited this!
case 200: {
NSLog(#"Received connection response!");
break;
}
default: {
NSLog(#"Something bad happened!");
break;
}
}
}
- (void)connection:(NSURLConnection *)connection didFailWithError:(NSError *)error {
NSLog(#"Error connecting. Error: %#", error);
}
#end
Also, just throwing it out there too, you don't necessarily have to use an asynchronous call. You can send a synchronous call which doesn't require you to implement a delegate. Here's how:
NSString *dev_ip = #"http://www.asdfasdfasdf.com/";
NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:[NSURL URLWithString:dev_ip]];
NSURLResponse *response = nil;
NSError *error = nil;
NSData *connectionData = [NSURLConnection sendSynchronousRequest:request returningResponse:&response error:&error];
You can check the response value and errors.
There's a better way to test whether a server is valid or not. The Reachability class provides good API for this purpose.
I saw there is a method for synchronous, like if I wanted to do something like:
-(IBAction)doNSURLConnSync {
NSURLRequest *request = [NSURLRequest requestWithURL:url];
NSError *error = nil;
NSURLResponse *response = nil;
[NSURLConnection sendSynchronousRequest:request returningResponse:&response error:&error];
}
How does it perform differently than if I did asynchronous:
-(IBAction)doNSURLConnASync {
NSURLRequest *request = [NSURLRequest requestWithURL:url];
NSURLConnection *connection = [[NSURLConnection alloc] initWithRequest:request
delegate:self];
if (connection) {
responseData = [[NSMutableData alloc] init];
[webview loadHTMLString:#"" baseURL:nil];
[UIApplication sharedApplication].networkActivityIndicatorVisible = YES;
}
else {
UIAlertView *alert = [[UIAlertView alloc] initWithTitle:#"Error"
message:#"Network error occured"
delegate:self
cancelButtonTitle:#"OK"
otherButtonTitles:nil];
[alert show];
[alert release];
}
}
- (void)connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)response {
NSLog(#"%s", __FUNCTION__);
}
-(void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data {
[responseData appendData:data];
}
-(void)connectionDidFinishLoading:(NSURLConnection *)connection {
[UIApplication sharedApplication].networkActivityIndicatorVisible = NO;
[connection release]; // got passed in as a param. we are done with it now.
[webview loadData:responseData
MIMEType:nil
textEncodingName:nil
baseURL:nil];
[responseData release];
}
Also, with my doNSURLConnSync method, I am just trying to load a UIWebView. Is there a reason why it doesn't? The button just sits there and stays highlighted, while it tries to access the webpage, but does nothing in the end, compared to the asynchronous version.
Also, for networkactivityindicator in my asynchronous code, I wanted to set my UIWebView to blank, have the indicator on while my webview loads, and then turn off the network activity indicator once the page loads. However, if I delete the loadHTMLString method, the network activity indicator works as it's supposed to, but with the loadHTMLString, the UIWebView goes blank, but the network activity indicator does not. Any thoughts? Thanks.
First, for the syncrhonous:
+ (NSData *)sendSynchronousRequest:(NSURLRequest *)request returningResponse:(NSURLResponse **)response error:(NSError **)error
This is the method signature, when you call the synchronous request, it wil return the data for you to display on the UIWebView and you have to call the UIWebView to display the data. However, the synchronous calling will block your UI until all the data come back. So, be careful with UX.
NSUrlConnection sendSynchronousRequest
For the asynchronous, it will not block your UI, user can still do everything they want with it, like go back to the previous screen. So, usually, it is recommended for big and long network
I don't know why it doesn't show your indicator. But why do you need this line : [webview loadHTMLString:#"" baseURL:nil]; . You only need to call it after you got your HTML response
A synchronous request ties up the main thread, which you should reserve for UI widget updates.
Doing an asynchronous request on a background thread frees up the main thread to update the UI.
Pull your UI update code (indicator view and web view) into separate methods, calling them on the main thread with -performSelectorOnMainThread:withObject:waitUntilDone:
[self performSelectorOnMainThread:#selector(updateWebview) withObject:nil waitUntilDone:YES];
I've been fooling around with code for ages on this one, I would be very grateful if someone could provide a code sample that downloaded this file from a server http://www.archive.org/download/june_high/june_high_512kb.mp4, (By the way it's not actually this file, it's just a perfect example for anyone trying to help me) and then play it from the documents directory. I know it seems lazy of me to ask this but I have tried so many different variations of NSURLConnection that it's driving me crazy.
Also, if I did manage to get the video file downloaded would I be correct in assuming this code would then successfully play it:
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *documentsDirectory = [paths objectAtIndex:0];
NSString *path = [documentsDirectory stringByAppendingPathComponent:#"june_high_512kb.mp4"];
NSURL *movieURL = [NSURL fileURLWithPath:path];
self.theMovie = [[MPMoviePlayerController alloc] initWithContentURL:movieURL];
[_theMovie play];
If the above code would work in playing a video file from the document directory, then I guess the only thing I would need to know is, how to download a video file from a server. Which is what seems to be my major problem. Any help is greatly appreciated.
Your code will work to play a movie file.
The simplest way to download is synchronously:
NSData *data = [NSData dataWithContentsOfURL:movieUrl];
[data writeToURL:movieUrl atomically:YES];
But it is better (for app responsiveness, etc) to download asynchronously:
NSURLRequest *theRequest = [NSURLRequest requestWithURL:movieUrl cachePolicy:NSURLRequestReloadIgnoringLocalCacheData timeoutInterval:60];
receivedData = [[NSMutableData alloc] initWithLength:0];
NSURLConnection *connection = [[NSURLConnection alloc] initWithRequest:theRequest delegate:self startImmediately:YES];
This requires implementing the informal NSURLConnection protocol:
- (void) connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)response {
[UIApplication sharedApplication].networkActivityIndicatorVisible = YES;
[receivedData setLength:0];
}
- (void) connection:(NSURLConnection *)connection didReceiveData:(NSData *)data {
[receivedData appendData:data];
}
- (void) connection:(NSURLConnection *)connection didFailWithError:(NSError *)error {
[UIApplication sharedApplication].networkActivityIndicatorVisible = NO;
[connection release];
}
- (NSCachedURLResponse *) connection:(NSURLConnection *)connection willCacheResponse:(NSCachedURLResponse *)cachedResponse {
return nil;
}
- (void) connectionDidFinishLoading:(NSURLConnection *)connection {
[UIApplication sharedApplication].networkActivityIndicatorVisible = NO;
[connection release];
[self movieReceived];
}
and then saving (and playing) the movie file in the movieReceived method.
If you want to download videos asynchronous I would suggest using the ASI library, so your app doesn't lock up while you wait for the download to finish.
http://allseeing-i.com/ASIHTTPRequest/
I want to download a wav file from a web service, cache it on the iphone and playback it using AVAudioPlayer. Using NSFileHandle and NSURLConnection seems a viable solution when dealing with relatively large files. However, after running the app in the simulator I don't see any saved file under the defined directory (NSHomeDirectory/tmp). Below is my basic code. Where am I doing wrong? Any thoughts are appreciated!
#define TEMP_FOLDER [NSHomeDirectory() stringByAppendingPathComponent:#"tmp"]
- (void)downloadToFile:(NSString*)name
{
NSString* filePath = [[NSString stringWithFormat:#"%#/%#.wav", TEMP_FOLDER, name] retain];
self.localFilePath = filePath;
// set up FileHandle
self.audioFile = [[NSFileHandle fileHandleForWritingAtPath:localFilePath] retain];
[filePath release];
// Open the connection
NSURLRequest* request = [NSURLRequest
requestWithURL:self.webURL
cachePolicy:NSURLRequestUseProtocolCachePolicy
timeoutInterval:60.0];
NSURLConnection* connection = [[NSURLConnection alloc] initWithRequest:request delegate:self];
}
#pragma mark -
#pragma mark NSURLConnection methods
- (void)connection:(NSURLConnection *)connection didReceiveData:(NSData*)data
{
[self.audioFile writeData:data];
}
- (void)connection:(NSURLConnection *)connection didFailWithError:(NSError*)error
{
NSLog(#"Connection failed to downloading sound: %#", [error description]);
}
- (void)connectionDidFinishLoading:(NSURLConnection *)connection
{
[connection release];
[audioFile closeFile];
}
NSFileHandle fileHandleForWritingAtPath: requires the file to already exist. How are you creating the file?
Where is your
- (void)connectionDidFinishLoading:(NSURLConnection *)connection
delegate?
this is where you should write/save the file.
- (void)connection:(NSURLConnection *)connection didReceiveData:(NSData*)data
this is where you append the data you receive.
I encounter a problem by following your comment. I would like to download different file at same time with different delegate:
.h:
NSMutableData *fileData;
.m:
NSString *imgfile = [NSString stringWithFormat:#"http://xxxx/01.jpg"];
NSURL *fileURL1 = [NSURL URLWithString:imgfile];
NSString *audiofile = [NSString stringWithFormat:#"http://xxxx/01.mp3"];
NSURL *fileURL2 = [NSURL URLWithString:audiofile];
NSURLRequest *request1 = [NSURLRequest requestWithURL:fileURL1 cachePolicy:NSURLRequestUseProtocolCachePolicy timeoutInterval:10.0 ];
NSURLRequest *request2 = [NSURLRequest requestWithURL:fileURL2 cachePolicy:NSURLRequestUseProtocolCachePolicy timeoutInterval:10.0 ];
NSArray *connections = [[NSArray alloc] initWithObjects:
[[NSURLConnection alloc] initWithRequest:request1 delegate:self ],
[[NSURLConnection alloc] initWithRequest:request2 delegate:self ],
nil];
- (void)connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)response {
fileData = [[NSMutableData alloc] init];
}
- (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data {
[fileData appendData:data];
}
- (void)connection:(NSURLConnection *)connection didFailWithError:(NSError *)error {
NSLog(#"Unable to fetch data");
}
ok, the download process works, but, the file size of jpg and mp3 are incorrect, the only correct thing is the total file size (jpg+mp3), please could you have a look on the code, what is missing?
Another question is, I put the file in a NSMutableArray, my question is, how to check which index of array is the correct file type (jpg and mp3)? because I need to save them to the device folder.
It looks like both your connections write to the same fileData object and all your problems follow from that.
How to deal with multiple connections you can see relevant question asked here on SO, there's also nice blog post containing NSURLConnection subclass that addresses this issue.
Here is what I am doing, I set a counter to identify the file type to save:
- (void)connectionDidFinishLoading:(NSURLConnection *)connection {
NSMutableData *dataForConnection = [self dataForConnection:(CustomURLConnection*)connection];
[connection release];
// Do something with the dataForConnection.
downloadCount ++;
// Copy Image Data to ur location using FileManager
//UIImage *img = [UIImage imageWithData:fileData];
NSString *saveDir;
if (downloadCount ==1)
saveDir = [NSString stringWithFormat:#"001.jpg"];
if (downloadCount == 2)
saveDir = [NSString stringWithFormat:#"001.mp3"];
//NSLog(#"saveDir=%#",saveDir);
NSArray * sysPaths = NSSearchPathForDirectoriesInDomains( NSDocumentDirectory, NSUserDomainMask, YES );
NSString * filePath = [[NSString alloc] initWithFormat: #"%#/%#", [sysPaths objectAtIndex: 0], saveDir];
NSLog(#"filePath=%#",filePath);
if([dataForConnection writeToFile:filePath atomically:YES])
NSLog(#"DONE COPYING");
else
NSLog(#"Write to device error");
//[fileData setLength:0];
if (downloadCount == 2)
{
downloadCount = 0;
play the MP3 and display the image file;
}
}
even I run the app on simulator, the file was download without error reported, but sometimes the file can not be opened, even I tried to open it from the folder, I was told the file was corrupted, I do not understand what was wrong, hope you do understand what I am saying.