iOS Download html file from url - html

I need to download the html file from server url and replace to local html file. I'm using AFNetworking to download the file and store to Documents folder. It's downloading video & audio files. But when i try to download the html files i'm getting below error Error Domain=NSCocoaErrorDomain Code=3840 "The operation couldn’t be completed. (Cocoa error 3840.)" (JSON text did not start with array or object and option to allow fragments not set.) UserInfo=0x83d0730 {NSDebugDescription=JSON text did not start with array or object and option to allow fragments not set.}
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *documentsDirectory = [paths objectAtIndex:0]; // Get documents folder
NSString *dataPath = [documentsDirectory stringByAppendingPathComponent:#"solarhtml"];
if ([[NSFileManager defaultManager] fileExistsAtPath:dataPath])
[[NSFileManager defaultManager] createDirectoryAtPath:dataPath withIntermediateDirectories:NO attributes:nil error:&error];
AFHTTPRequestOperationManager *manager = [AFHTTPRequestOperationManager manager];
[manager.requestSerializer setValue:#"application/x-www-form-urlencoded"
forHTTPHeaderField:#"Content-Type"];
[manager setResponseSerializer:[AFJSONResponseSerializer serializer]];
manager.responseSerializer.acceptableContentTypes = [NSSet setWithObjects:#"text/html", nil];
[manager GET:#"http://server.net/projects/index.html"
parameters:nil
success:^(AFHTTPRequestOperation *operation, id responseObject) {
[operation.responseData writeToFile:[dataPath stringByAppendingPathComponent:#"index.html"] atomically:YES];
NSLog(#"Successfully downloaded file to %#", [NSURL fileURLWithPath:dataPath]);
NSLog(#"THE RESPONSE: %#", responseObject);
}
failure:^(AFHTTPRequestOperation *operation, NSError *error1) {
NSLog(#"%#", error1);
}];
Access html file:
-(void)estimatesavings:(id)sender{
if(updateclick==YES){
web_estimate=[[UIWebView alloc]initWithFrame:CGRectMake(0, 0, 1024, 768)];
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *documentsDirectory = [paths objectAtIndex:0];
NSString *filePath = [documentsDirectory stringByAppendingPathComponent:#"index.html"];
NSURL *targetURL = [NSURL fileURLWithPath:filePath];
NSURLRequest *request = [NSURLRequest requestWithURL:targetURL];
[web_estimate loadRequest:request];
web_estimate.delegate=self;
[self.view addSubview:web_estimate];
}else{
NSString *pathToBundle = [[NSBundle mainBundle] bundlePath];
NSURL *baseURL = [NSURL fileURLWithPath:pathToBundle];
NSString *htmlFile = [[NSBundle mainBundle] pathForResource:#"index" ofType:#"html"];
NSString *htmlString = [NSString stringWithContentsOfFile:htmlFile encoding:NSUTF8StringEncoding error:nil];
//CGRect fullScreenRect=[[UIScreen mainScreen]applicationFrame];
web_estimate=[[UIWebView alloc]initWithFrame:CGRectMake(0, 0, 1024, 768)];
[web_estimate loadHTMLString:htmlString baseURL:baseURL];
web_estimate.delegate=self;
[self.view addSubview:web_estimate];
}
}
ERROR:
copy failed: Error Domain=NSCocoaErrorDomain Code=4 "The operation couldn’t be completed. (Cocoa error 4.)" UserInfo=0x83c8b50 {NSSourceFilePathErrorKey=/Users/ranganathagv/Library/Application Support/iPhone Simulator/6.1/Applications/CEDFEBEB-2A5C-40A9-8965-761689FD83C2/ActewAGL.app/index.html, NSUserStringVariant=(
Copy
), NSFilePath=/Users/ranganathagv/Library/Application Support/iPhone Simulator/6.1/Applications/CEDFEBEB-2A5C-40A9-8965-761689FD83C2/ActewAGL.app/index.html, NSDestinationFilePath=/Users/ranganathagv/Library/Application Support/iPhone Simulator/6.1/Applications/CEDFEBEB-2A5C-40A9-8965-761689FD83C2/Documents/solarhtml/index.html, NSUnderlyingError=0x83c89c0 "The operation couldn’t be completed. No such file or directory"}c

There were two problems:
The attempt to use AFJSONResponseSerializer and changing the acceptableContentTypes will not work, because the AFJSONResponseSerializer will accept the response, but will still try to parse the JSON. Instead, you should just use AFHTTPResponseSerializer instead. See https://stackoverflow.com/a/21621530/1271826 for more information.
The other problem rests in your open routine. Rather than just opening the file from the bundle, you should open the file in the Documents folder. You might even want to copy the file from the bundle to the Documents folder (in case it hasn't been downloaded yet).
For example:
NSString *documentsFolder = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES)[0];
NSString *solarFolder = [documentsFolder stringByAppendingPathComponent:#"solarhtml"];
NSString *documentsPath = [solarFolder stringByAppendingPathComponent:#"index.html"];
NSString *bundlePath = [[NSBundle mainBundle] pathForResource:#"index" ofType:#"html"];
NSFileManager *fileManager = [NSFileManager defaultManager];
if (![fileManager fileExistsAtPath:documentsPath]) {
NSError *error;
if (![fileManager fileExistsAtPath:solarFolder]) {
if (![fileManager createDirectoryAtPath:solarFolder withIntermediateDirectories:YES attributes:nil error:&error]) {
NSLog(#"create folder failed: %#", error);
}
}
if (![fileManager copyItemAtPath:bundlePath toPath:documentsPath error:&error]) {
NSLog(#"copy failed: %#", error);
}
}
NSString *htmlString = [NSString stringWithContentsOfFile:documentsPath encoding:NSUTF8StringEncoding error:nil];
web_estimate=[[UIWebView alloc]initWithFrame:CGRectMake(0, 0, 1024, 768)];
[web_estimate loadHTMLString:htmlString baseURL:baseURL];
This way, if the file hasn't been downloaded at all, this will copy the file from the bundle before trying to open it, if necessary.

Related

url nsdata not replacing local writetofile JSON

JSON web file is not overwriting and replacing the local JSON file on the App.
-(void)writeJsonToFile
{
NSURL *fileJSON = [[NSBundle mainBundle] URLForResource:#"data" withExtension:#"json"];
NSString *filePath = [NSString stringWithFormat:#"%#",fileJSON];
NSString *stringURL = #"website.com/data.json";
NSURL *url = [NSURL URLWithString:stringURL];
NSData *urlData = [NSData dataWithContentsOfURL:url];
[urlData writeToFile:filePath atomically:YES];
}
The destination for urlData and filePath files match each other.
Been looking around the site and other places online, made sure the file path matched.
We can never change the NSBundle file but
you can save in to local then try this
-(void)writeJsonToFile
{
NSString * filePath=[NSString stringWithFormat:#"%#/Documents/data.json",NSHomeDirectory()];
NSString *stringURL = #"website.com/data.json";
NSURL *url = [NSURL URLWithString:stringURL];
NSData *urlData = [NSData dataWithContentsOfURL:url];
[urlData writeToFile:filePath atomically:YES];
}
This code replacing local JSON file when call.
Your JSON file save in your application directory/Documents/ and file name is data.json
website.com/data.json do not return json data, please check it and if needed to change your URL.

The images are missing for download html data

We use the code to download the wikipedia html data and save it.
NSString *urlString = #"http://en.wikipedia.org/wiki/Adele";
NSURL *url = [NSURL URLWithString:urlString];
NSURLRequest *urlRequest = [NSURLRequest requestWithURL:url];
NSOperationQueue *queue = [[NSOperationQueue alloc] init];
[NSURLConnection sendAsynchronousRequest:urlRequest queue:queue completionHandler:^(NSURLResponse *response,NSData *data, NSError *error)
{
if ([data length] >0 && error == nil){
NSString *html = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding];
[[NSUserDefaults standardUserDefaults] setObject:html forKey:urlString];
NSLog(#"Downloaded.");
}
else if ([data length] == 0 && error == nil){
NSLog(#"Nothing was downloaded.");
}
else if (error != nil){
NSLog(#"Error happened = %# /n %#", error, urlString);
[NSThread detachNewThreadSelector:#selector(updateTextFaild) toTarget:self withObject:nil];
}
}];
Then we turn the device or simulator offline and open the download html data with UIWebView. However, there are something missing (images) for the download wikipedia data.
We use the code to open the data:(the "_urlString" is "#"http://en.wikipedia.org/wiki/Adele"")
if ([[NSUserDefaults standardUserDefaults] objectForKey:_urlString])
{
[_webview loadHTMLString:[[NSUserDefaults standardUserDefaults] objectForKey:_urlString] baseURL:[NSURL URLWithString:_urlString]];
return;
}
It's so strange.

How to download html file from server (not local) in NSString

I have a html file on some server. The html file only contains text no photo's or anything else.
What I want to do is download the html file and put the text into a NSString.
Is this possible?
I found some code a few minutes ago:
NSURL *url = [NSURL URLWithString:#"http://www.example.com/file.html"];
NSString *text = [[NSString alloc] initWithContentsOfURL: url
encoding: NSUTF8StringEncoding
error: nil];
But did not work
Try this :
NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL: url
cachePolicy: NSURLRequestReloadIgnoringLocalAndRemoteCacheData
timeoutInterval: 20];
NSError *requestError;
NSHTTPURLResponse *response = nil;
// Send request synchronously.
NSData *responseData = [NSURLConnection sendSynchronousRequest: request
returningResponse: &response
error: &requestError];
// Get the json string reponse.
NSString *stringResponse = [[NSString alloc]initWithData: responseData
encoding:NSUTF8StringEncoding];

Using images and css file for a generated HTML UIWebView

I'm generating an HTML file for an app, and in this HTML file there's a link to a stylesheet and one to an image.
Here's what I tried so far:
NSMutableString *toReturn = [NSMutableString stringWithCapacity:100];
NSString *cssPath = [[NSBundle mainBundle] pathForResource:#"etapes.css" ofType:nil];
[toReturn appendFormat:#"<html><head><title></title><link href=\"%#\" media=\"all\" rel=\"stylesheet\" type=\"text/css\" /></head><body>", cssPath];
It generates the right full path to the right file, this is okay if I want to access it on my mac, but on the simulator or my iPhone it doesn't point to the right place at all...
Do you have any idea of how could I make this?
Thanks
I found this tutorial a while back. I ended up just using a <style> tag in the end though.
NSString *path = [[NSBundle mainBundle] bundlePath];
NSURL *baseURL = [NSURL fileURLWithPath:path];
[webView loadHTMLString:htmlString baseURL:baseURL];
Another way of doing this is like so:
-(NSString *)urlForFileNamed:(NSString *) name ofType:(NSString *) type {
NSString *filePath = [[NSBundle mainBundle] pathForResource: name ofType: type];
if (!filePath) { NSLog(#"No path found for file %#.%#", name, type); }
return filePath;
}
-(void)viewDidLoad {
[super viewDidLoad];
NSString *path = [[NSBundle mainBundle] bundlePath];
baseUrl = [NSURL fileURLWithPath: path];
[view loadHTMLString: [self html] baseURL: [NSURL fileURLWithPath: path]];
}
-(NSString *)html {
// Obviously the below's just a stub for proper HTML
return [NSString stringWithFormat: #"<img src=\"#\" />", [self urlForFileNamed: #"foo" ofType: #"png"]];
}
You may try to edit the line
NSString *cssPath = [[NSBundle mainBundle] pathForResource:#"etapes.css" ofType:nil];
to
NSString *cssPath = [[NSBundle mainBundle] pathForResource:#"etapes" ofType:#"css"];

How to save the content in UIWebView for faster loading on next launch?

I know that there are some caching classes introduced in the iphone sdk recently, and there is also a TTURLRequest from three20's library that allows you to cache a request to a URL. However, because I am loading the web page in UIWebView by calling UIWebView's loadRequest, those techniques are not really applicable.
Any ideas how I can save a web page so that on next app launch, I don't have to fetch from the web again for the full page? The page itself already have some ajax mechanism that updates parts of itself automatically.
There are a bunch of articles about the way the cache of the UIWebView works and the global feeling is that even if some mechanisms seems to work OK under MacOS X, the same approaches may have curious behavior under iPhone.
HOWEVER, I'm doing it by playing with the global cache that is accessed by any NSURLConnection, UIWebView included. And in my case, it works ;).
What you need to understand is the global flow:
YOU -> loadRequest on a UIWebView
This goes into NSURLCache to ask "is there something cached for this request?":
- (NSCachedURLResponse *)cachedResponseForRequest:(NSURLRequest *)request
From that, here's what I do to handle the cache on the disk, on my side, to speed up the load of a UIWebView:
Subclass the NSURLCache and override the get control over the -(NSCachedURLResponse *)cachedResponseForRequest:(NSURLRequest *)request selector
Reimplement this selector in such a way that if nothing has been written on the FS for this request (no cache), then do the request on your side and store the content on FS. Otherwise, return what has been previously cached.
Create an instance of your subclass and set it to the system so that it is used by your application
Now the code :
MyCache.h
#interface MyCache : NSURLCache {
}
#end
MyCache.m
#implementation MyCache
-(NSCachedURLResponse *)cachedResponseForRequest:(NSURLRequest *)request {
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSLog(#"CACHE REQUEST S%#", request);
NSString *documentsDirectory = [paths objectAtIndex:0];
NSArray* tokens = [request.URL.relativePath componentsSeparatedByString:#"/"];
if (tokens==nil) {
NSLog(#"ignoring cache for %#", request);
return nil;
}
NSString* pathWithoutRessourceName=#"";
for (int i=0; i<[tokens count]-1; i++) {
pathWithoutRessourceName = [pathWithoutRessourceName stringByAppendingString:[NSString stringWithFormat:#"%#%#", [tokens objectAtIndex:i], #"/"]];
}
NSString* absolutePath = [NSString stringWithFormat:#"%#%#", documentsDirectory, pathWithoutRessourceName];
NSString* absolutePathWithRessourceName = [NSString stringWithFormat:#"%#%#", documentsDirectory, request.URL.relativePath];
NSString* ressourceName = [absolutePathWithRessourceName stringByReplacingOccurrencesOfString:absolutePath withString:#""];
NSCachedURLResponse* cacheResponse = nil;
//we're only caching .png, .js, .cgz, .jgz
if (
[ressourceName rangeOfString:#".png"].location!=NSNotFound ||
[ressourceName rangeOfString:#".js"].location!=NSNotFound ||
[ressourceName rangeOfString:#".cgz"].location!=NSNotFound ||
[ressourceName rangeOfString:#".jgz"].location!=NSNotFound) {
NSString* storagePath = [NSString stringWithFormat:#"%#/myCache%#", documentsDirectory, request.URL.relativePath];
//this ressource is candidate for cache.
NSData* content;
NSError* error = nil;
//is it already cached ?
if ([[NSFileManager defaultManager] fileExistsAtPath:storagePath]) {
//NSLog(#"CACHE FOUND for %#", request.URL.relativePath);
content = [[NSData dataWithContentsOfFile:storagePath] retain];
NSURLResponse* response = [[NSURLResponse alloc] initWithURL:request.URL MIMEType:#"" expectedContentLength:[content length] textEncodingName:nil];
cacheResponse = [[NSCachedURLResponse alloc] initWithResponse:response data:content];
} else {
//trick here : if no cache, populate it asynchronously and return nil
[NSThread detachNewThreadSelector:#selector(populateCacheFor:) toTarget:self withObject:request];
}
} else {
NSLog(#"ignoring cache for %#", request);
}
return cacheResponse;
}
-(void)populateCacheFor:(NSURLRequest*)request {
NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init];
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
//NSLog(#"PATH S%#", paths);
NSString *documentsDirectory = [paths objectAtIndex:0];
NSArray* tokens = [request.URL.relativePath componentsSeparatedByString:#"/"];
NSString* pathWithoutRessourceName=#"";
for (int i=0; i<[tokens count]-1; i++) {
pathWithoutRessourceName = [pathWithoutRessourceName stringByAppendingString:[NSString stringWithFormat:#"%#%#", [tokens objectAtIndex:i], #"/"]];
}
NSString* absolutePath = [NSString stringWithFormat:#"%#/myCache%#", documentsDirectory, pathWithoutRessourceName];
//NSString* absolutePathWithRessourceName = [NSString stringWithFormat:#"%#%#", documentsDirectory, request.URL.relativePath];
//NSString* ressourceName = [absolutePathWithRessourceName stringByReplacingOccurrencesOfString:absolutePath withString:#""];
NSString* storagePath = [NSString stringWithFormat:#"%#/myCache%#", documentsDirectory, request.URL.relativePath];
NSData* content;
NSError* error = nil;
NSCachedURLResponse* cacheResponse = nil;
NSLog(#"NO CACHE FOUND for %#", request.URL);
//NSLog(#"retrieving content (timeout=%f) for %# ...", [request timeoutInterval], request.URL);
content = [NSData dataWithContentsOfURL:request.URL options:1 error:&error];
//NSLog(#"content retrieved for %# / error:%#", request.URL, error);
if (error!=nil) {
NSLog(#"ERROR %# info:%#", error, error.userInfo);
NSLog(#"Cache not populated for %#", request.URL);
} else {
NSURLResponse* response = [[NSURLResponse alloc] initWithURL:request.URL MIMEType:#"" expectedContentLength:[content length] textEncodingName:nil];
cacheResponse = [[NSCachedURLResponse alloc] initWithResponse:response data:content];
//the store is invoked automatically.
[[NSFileManager defaultManager] createDirectoryAtPath:absolutePath withIntermediateDirectories:YES attributes:nil error:&error];
BOOL ok;// = [[NSFileManager defaultManager] createDirectoryAtPath:absolutePath withIntermediateDirectories:YES attributes:nil error:&error];
ok = [content writeToFile:storagePath atomically:YES];
NSLog(#"Caching %# : %#", storagePath , ok?#"OK":#"KO");
}
[pool release];
}
#end
And the use of it in your application:
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString* documentsDirectory = [paths objectAtIndex:0];
NSString* diskCachePath = [NSString stringWithFormat:#"%#/%#", documentsDirectory, #"myCache"];
NSError* error;
[[NSFileManager defaultManager] createDirectoryAtPath:diskCachePath withIntermediateDirectories:YES attributes:nil error:&error];
MyCache* cacheMngr = [[MyCache alloc] initWithMemoryCapacity:10000 diskCapacity:100000000 diskPath:diskCachePath];
[NSURLCache setSharedURLCache:cacheMngr];
This code deserves a lot of cleanup.. but the main things should be in there. I had a lot of trouble to get this working, hope this helps.
I recently found this project under github :
http://github.com/rs/SDURLCache
The approach is quite the same as my previous answer described here How to save the content in UIWebView for faster loading on next launch? , but the code looks more polished so maybe it makes sense to give it a try.
If the page has AJAX already, why not store the JavaScript/HTML in the application bundle to start rather than downloading it on the first launch? Then load the page with the code Corey gave below and let the AJAX handle hitting the network for the updated parts of the page.
Take a look at: http://allseeing-i.com/ASIHTTPRequest/ASIWebPageRequest
You can save an HTML in the documents directory and load the page directly from the documents directory on launch.
To save the webview content:
Reading HTML content from a UIWebView
To load:
NSString* path = [[NSBundle mainBundle] pathForResource:#"about" ofType:#"html"];
NSURL* url = [NSURL fileURLWithPath:path];
NSURLRequest* request = [NSURLRequest requestWithURL:url];
[webView loadRequest:request];