iOS show webview offline with images - ios

I am trying to download a webpage (html) then display the local html that has been downloaded in a UIWebView. It is working, but the offline UIWebView doesn‘t show me the images.
This is my code:
#interface ViewController ()
#end
#implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *filePath = [NSString stringWithFormat:#"%#/%#", [paths objectAtIndex:0],#"index.html"];
NSURL *url = [NSURL URLWithString:#"https://www.mypage.com"];
NSData *urlData = [NSData dataWithContentsOfURL:url];
[urlData writeToFile:filePath atomically:YES];
[Website loadRequest:[NSURLRequest requestWithURL:[NSURL fileURLWithPath:filePath]]];
}
- (void)didReceiveMemoryWarning {
[super didReceiveMemoryWarning];
}
#end
Can anyone share some sample code to add the website content (the images)?
Any help would be appreciated. Thanks!

Most likely the HTML references images that you aren't also downloading. The first step would be to parse the HTML and download the images too. Then you may need to fix the local copy of HTML if the image references aren't relative to the base document. For example, if they refer to images on a different server, you'll have to fix those links internally. And if the original document uses Javascript to load images dynamically, it may never work the way you want.

Related

Open a pdf saved in my documents folder in Xcode project

I have made an app that creates a pdf and stores it in the apps documents folder. I would now like to open it and view it from within the app when a 'View pdf' UIButton is pressed.
I have already looked at a few questions on here and I have considered either a separate view controller or perhaps a scroll view.
What is the best method to use?
UPDATE:
I have followed advice and I am trying to use QLPreviewController. I have added QuickLook framework and now have the following, but I am stuck on how to get the path recognised in the pathForResource. Any suggestions?
- (NSInteger)numberOfPreviewItemsInPreviewController:(QLPreviewController *)controller
{
return 1;
}
- (id <QLPreviewItem>)previewController:(QLPreviewController *)controller previewItemAtIndex:(NSInteger)index
{
NSString *path=[[NSBundle mainBundle] pathForResource:[pdfPathWithFileName] ofType:nil];
return [NSURL fileURLWithPath:path];
}
- (IBAction)viewPdfButton:(id)sender {
NSString *filename= #"ObservationPDF.pdf";
NSArray *path = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *documnetDirectory = [path objectAtIndex:0];
NSString *pdfPathWithFileName = [documnetDirectory stringByAppendingPathComponent:filename];
[self generatePdf: pdfPathWithFileName];
QLPreviewController *previewController=[[QLPreviewController alloc]init];
previewController.delegate=self;
previewController.dataSource=self;
[self presentViewController:previewController animated:YES completion:nil];
}
If the PDF file is in the app documents folder then you shouldn't be thinking about passing it to another app, you should be looking to present the file inside the app. 2 general options:
Add a UIWebView and load the local file into it
Use QLPreviewController to show a new view containing the PDF
The web view is simple and requires no transition on the UI. The preview controller needs a transition but offers some sharing / printing support for free.
This line is confused (and invalid syntax by the looks of it):
NSString *path=[[NSBundle mainBundle] pathForResource:[pdfPathWithFileName] ofType:nil];
You only use NSBundle to get items out of the bundle, and that isn't what you have. You should just be creating the URL with the file path where you save the file:
[NSURL fileURLWithPath:pdfPathWithFileName];
(which you may store or you may need to recreate in the same way as when you save the file)

UIWebView Delegates Not Triggering ? iOS

I am loading a local HTML into a UIWebView as follows :
- (void)viewDidLoad
{
[super viewDidLoad];
//Setup our UIWebView
webView = [[UIWebView alloc] initWithFrame:self.view.bounds];
webView.delegate = self;
webView.scalesPageToFit = YES;
webView.userInteractionEnabled = YES;
[self.view addSubview:webView];
...
[self loadWebPage];
}
- (void)loadWebPage {
NSURL *url = [NSURL fileURLWithPath:[[NSBundle mainBundle] pathForResource:#"index" ofType:#"html"]];
[webView loadRequest:[NSURLRequest requestWithURL:url]];
}
And in the header :
#interface ViewController : UIViewController <UIWebViewDelegate>
However for some reason my webview delegates are not being called :
- (void)webViewDidStartLoad:(UIWebView *)webView {
NSLog(#"Starting HTML Load Process");
}
- (void)webViewDidFinishLoad:(UIWebView *)webViewRef {
NSLog(#"Web View Finished Loading Method");
}
- (void)webView:(UIWebView *)webView didFailLoadWithError:(NSError *)error {
NSLog(#"Error loading HTML ! %#", error);
}
Can anyone suggest why ?
Thanks !
Called the method [self loadWeb];
But on definition the method is - (void)loadWebPage;
Is it typo?
The only way I can reproduce is this by declaring the webView #property as weak. Can you confirm you are declaring it as a strong reference?
how did you declare your UIWebView instance variable, as a strong or weak? If weak make it strong.
The problem is that this call.
NSURL *url = [NSURL fileURLWithPath:[[NSBundle mainBundle] pathForResource:#"index" ofType:#"html"]];
Looks like you're loading the HTML from inside your bundle. This means you need to confirm that all the additional files (.js, .css, and any media files) also need to be present in your bundle.
So the first thing need to check fileURLWithPath: point either it's returning nil or not. If it's not then it means that .html page resolved, however the additional files not resolved as part of the request because it's not placed on the same "index.html" path.
First you need to confirm the directory structure of your "index.html" and all the additional files used inside the page. In order to do that, you have to right click on binary .app in your "build" directory in the finder and then select "Show Package contents".
binary (.app) > right click > Show Package contents
This shows what the app's main bundle contains. Look inside for your HTML file. If it's in a folder, that folder name needs to be in the inDirectory parameter of the pathForResource call. If it's on the top-level then you don't have a folder reference. Either delete & re-drag it into project or use the pathForResource version without inDirectory.
The best way to don't setup the base path or relative path for the resources inside .html page (.js, .css and images) because it don't work. Copy all the additional resources next to "index.html" file so it would be easy to resolve on runtime from bundle.
NSString *path = #"<Index.html Path>";
NSURL *url = [NSURL URLWithString: [path lastPathComponent]
relativeToURL: [NSURL fileURLWithPath: [path stringByDeletingLastPathComponent]
isDirectory: YES]];
Try this
NSURL *url = [NSURL fileURLWithPath:[[NSBundle mainBundle] pathForResource:#"index" ofType:#"html"]isDirectory:NO];

iOS dataWithContentsOfURL - what's happening behind the scenes

I've got some simple code (copy and pasted from SO) that loads a KLM (XML based) file into iOS's documents directory. I then display the loaded data on a map.
I realise that this is not a good way of downloading and saving the file - NSUrlConnection seems to be recommended so that the loading can be managed. But I'm new to all this and I'd like to understand what is happening in this case first.
Here's the code:
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *filePath = [NSString stringWithFormat:#"%#/%#", [paths objectAtIndex:0],#"index.kml"];
// Download and write to file
NSURL *url = [NSURL URLWithString:#"http://www.domain.co.uk/kml-resource..."];
NSData *urlData = [NSData dataWithContentsOfURL:url];
[urlData writeToFile:filePath atomically:YES];
NSURL *fileurl = [NSURL fileURLWithPath:filePath];
kmlParser = [[KMLParser alloc] initWithURL:fileurl];
.....
My questions are:
What happens while dataWithContentsOfURL is connecting/downloading - does the application just freeze and become unresponsive?
If I run my program in aeroplane mode the second time, it still seems to work. When does it decide it's OK to skip the download and writeToFile?
Does anyone know if it uses any caching between dataWithContentsOfURL and the server? ie. can I be sure that if I get a response, it is fresh data and hasn't just been sitting in safari/iOS's cache.
Many thanks
dataWithContentsOfURL is blocking method, so yeah, you shouldn't run that on main thread.
probably has internal timeout, but that is private... maybe 60 sec.
documentation doesn't say anything about caching, so I assume it doesn't cache at all.

stringByEvaluatingJavaScriptFromString Reference Local File

I have an image that I have in my bundle resources that I need to reference as a source file (src="") in my webView stringByEvaluatingJavaScriptFromString. I can reference external images from websites, but I do not know how to write the URL of the local image.
How could this be done?
An example to show how you could do this...
in my viewDidLoad, I am adding some example HTML into a UIWebView as shown below: (This could easily be a loadRequest: call to a remote URL)
NSString *html = #"<html><body><img id='theImage'></img></body></html>";
[self.webView loadHTMLString:html baseURL:nil];
You need to set the view controller as the delegate for the UIWebView, and wait until it has finished loading the HTML. You can then execute a script to amend the image URL:
- (void)webViewDidFinishLoad:(UIWebView *)webView {
// Get the path to the image in the bundle you wish to display, and create a URL from this path.
NSString *imagePath = [[NSString alloc] initWithString:[[[NSBundle mainBundle] resourcePath] stringByAppendingPathComponent:#"MyImage.png"]];
NSURL *imageURL = [NSURL fileURLWithPath:imagePath];
// Create a script to execute in the web view.
NSString *script = [[NSString alloc] initWithFormat:#"document.getElementById('theImage').src = \"%#\";", imageURL];
// Execute the script.
[self.webView stringByEvaluatingJavaScriptFromString:script];
// Tidy up.
[imagePath release];
}

Prevent an app crash due to a slow connection when retrieving a remote file

I am currently using a function in my app's didFinishLaunchingWithOptions that retrieves a file, saves it to the application directory.
I have found that when there is a weak connection the app will crash when this is happening. I read that there is a 20 second time limit Apple allows before crashing the app. Is this correct? If so, I believe this is causing my issue as the app works flawlessly with the exception of being on a very weak connection.
How could I modify my logic below to try and compensate for this?
- (void)writeJsonToFile
{
//applications Documents dirctory path
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *documentsDirectory = [paths objectAtIndex:0];
//live json data url
NSString *stringURL = #"http://link-to-my-data.json";
NSURL *url = [NSURL URLWithString:stringURL];
NSData *urlData = [NSData dataWithContentsOfURL:url];
//attempt to download live data
if (urlData)
{
NSString *filePath = [NSString stringWithFormat:#"%#/%#", documentsDirectory,#"data.json"];
[urlData writeToFile:filePath atomically:YES];
}
//copy data from initial package into the applications Documents folder
else
{
//file to write to
NSString *filePath = [NSString stringWithFormat:#"%#/%#", documentsDirectory,#"data.json"];
//file to copy from
NSString *json = [ [NSBundle mainBundle] pathForResource:#"data" ofType:#"json" inDirectory:#"html/data" ];
NSData *jsonData = [NSData dataWithContentsOfFile:json options:kNilOptions error:nil];
//write file to device
[jsonData writeToFile:filePath atomically:YES];
}
}
It's a very bad idea to run this sort of thing on the main thread: I assume you are - basically, you'll block the entire UI while you wait for the network operation to complete.
dataWithContentsOfURL is not a good idea for this sort of thing. It will be much better to use NSURLConnection or one of the wrapper libraries like AFNetworking, because you can handle cases like when the connection times out gracefully.
These libraries also have built-in methods to asynchronously download the data, which prevents the main UI thread from being locked.
When is this downloaded data needed?
Depending on the answer, maybe you can call the method inside a thread. This will prevent the main thread from blocking.
Even if the data is needed from the beginning, you can just create a loader and download the file in the background, then make the app active after the file is downloaded.
I think to be more independent from internal implementation of NSData *urlData = [NSData dataWithContentsOfURL:url]; you should implement you own download class based on NSURLConnection.
The links to read:
URL Loading System Programming Guide
NSURLConnection Class Reference
NSURLConnectionDelegate Protocol Reference
So you can catch all connection errors by your code and implement right behavior in this case.

Resources