WKWebView Check if web view support to load a file - ios

I am create a iOS Application. I am using WKWebView at some stage to show some files of any extension types. For Audio, Video, html, text, pdf, documents files its working well. But files like .zip , .csv and many others its not showing proper data or show blank screen without any error I am getting in its navigations delegates methods.
My Question is how can I check if my WKWebView is able to load/support that file? So that if it's not able to load or support that file, I can show another view where I will direct download that file to File manager and give user option to save or share that file.
Like In Safari browser when I am trying to load zip file its not show any preview , its show an alert to download (in iOS 13) or show Save and share file action (in iOS 13). How Safari is detecting that its support that file or not?
Please do not suggest to use direct String matching for mime types or extensions because there can be 1000+ files extensions in the world.

You need to make your controller as webView.navigationDelegate and define the navigation response delegate callback as follows:
func webView(_ webView: WKWebView, decidePolicyFor navigationResponse: WKNavigationResponse,
decisionHandler: #escaping (WKNavigationResponsePolicy) -> Void) {
if navigationResponse.canShowMIMEType {
decisionHandler(.allow)
} else {
decisionHandler(.cancel)
// do something else with `navigationResponse.response`
}
}

Related

Setting local image on WKWebview using evaluateJavascript method

I have a HTML file which contains local resource files such as css, js files inside its content. These local resource files are in zip format. My app use WKWebView to display this html file. I m trying to upload an image in one of the html image element on WKWebview which is saved locally and html is not updated with the image. I have a html image tag in local html like this.
<img id='image' style='display: block; height: 100%; width:100%; margin:0 auto;' />
And this image to load in this image tag is generated after the local html file is loaded in the WKWebview. Once the image is generated, I am trying to update the image tag with the evaluate javascript method like this.
NSString *js = #"document.getElementById('image').src='document directory/Library/images/generatedImage.png';"
[self.wkWebView evaluateJavaScript:js completionHandler:nil];
Am I doing something wrong here. I also read that we can't update the image locally which was 2 years ago, still the same? Is there any workaround solution for my scenario. Thanks in advance.
I can think of two ways to do what you want.
Custom URL Scheme
Convert the image to base64 then set it via evaluate Javascript
Custom URL Scheme
This is supported from iOS Version 11, if you are supporting version below 11 then you are better off with second approach. The basic idea is mention your URL with custom protocol like my-scheme://your-path then intercept that path and provide the response to WKWebView. In this you have to create a class that extends WKSchemeHandler and override it's required methods. e.g.
import WebKit
import Foundation
class CustomeSchemeHandler : NSObject,WKURLSchemeHandler {
func webView(_ webView: WKWebView, start urlSchemeTask: WKURLSchemeTask) {
}
func webView(_ webView: WKWebView, stop urlSchemeTask: WKURLSchemeTask) {
}
}
In the start method you can load the content from your desired source and then return the response via
urlSchemeTask.didReceive(response)
urlSchemeTask.didReceive(data)
urlSchemeTask.didFinish()
After implementing your logic, you need to register it with WKWebView via setURLSchemeHandler(_:forURLScheme:)
After seeing the above you can mention image URL with your custom scheme and leave the work to your CustomSchemeHandler.
This approach is very mature and can be really helpful in the long run. I found a very good and brief article that covers the implementation in details, do check it out: https://medium.com/#kumarreddy_b/custom-scheme-handling-in-uiwebview-wkwebview-bbeb2f3f6cc1
Convert the image to base64 then set it via evaluating Javascript
Create UIImage from your path e.g. https://stackoverflow.com/a/43006566/2299040
Convert the image to base64
Get the Id of your HTML image element and load the base64 String
e.g.
if let image = UIImage(contentsOfFile: ""){
//Now use image to create into NSData format
if let imageData = image.jpegData(compressionQuality: 0.8){
let strBase64 = imageData.base64EncodedString(options: .lineLength64Characters)
webView.evaluateJavaScript("document.getElementById('your-image-element-id').setAttribute('src', '\(strBase64)');")
}
}
For your example, I was able to load an image from the internet by setting that URL as the src.
I guess you might have not given read access to your local image file directory while loading the webview.
Utilize loadFileURL method of wkwebview to load an HTML file by giving access to a particular directory
For EG (in Swift),
let imgDir = "<your image directory>"
let imgDirUrl = URL(fileURLWithPath: String(format: "%#", imgDir), isDirectory: true)
webView.loadFileURL(<your HTML file URL path>, allowingReadAccessTo: imgDirUrl)
(In objc)
NSString *imgDir = "<your image directory>"
NSURL *imgDirUrl = [NSURL fileURLWithPath:[NSString stringWithFormat:#"%#",imgDir] isDirectory:true];
[webView loadFileURL:<your HTML file URL path> allowingReadAccessToURL:imgDirUrl];

How WKWebView runs JS to control window.scrollTo(x,y) when loading document

When I use WKWebView, I hope that ONLoad can load JS. In the previous UIwebView, this method is possible. Now I must monitor whether the HTML is loaded and then execute the ONLoad parameter. JS cannot be executed synchronously, which is slower than before. A lot.
I control window.scrollTo(x,y) through JS to realize that when he enters the document reading, it will automatically restore to the last reading progress. In the previous UIwebView, he was triggered synchronously.
At present, I use WKWebView, he must first determine the completion of loading HTML, and then trigger this JS, I tried to execute when HTML is loaded, but this is invalid, WKWebView control loading JS has become very slow, I hope it can I found a solution, I hope someone can help me, I don’t know much about English, so this is the result of Google Translate.
You can inject a script that should be executed on load like this:
NSString *scriptString = #"window.scrollTo(0,1000);";
WKUserScript *script = [[WKUserScript alloc] initWithSource:scriptString injectionTime:WKUserScriptInjectionTimeAtDocumentEnd forMainFrameOnly:YES];
[self.webView.configuration.userContentController addUserScript:script];
Here you can find the documentation of WKUserScript: https://developer.apple.com/documentation/webkit/wkuserscript?language=objc
If the injection time is still too early you can try to add a WKNavigationDelegate and call your script like below (sorry for the Swift code, maybe someone can edit and convert to objective-c)
func webView(_ webView: WKWebView, didFinish navigation: WKNavigation!) {
self.webView.evaluateJavaScript("document.readyState", completionHandler: { (complete, error) in
if complete != nil {
self.webView.evaluateJavaScript("window.scrollTo(0,1000);")
}
})
}

How to inject/evaluate JS in multiple pages of an iOS webview?

Seems like android's WebViewClient has methods like: onProgressChanged()
Anything for ios' wkwebview? (or any other iOS webview)
Webview's onNavigationStateChangedoesn't seem to help here.
Would like to evaluate JS on www.example.com & then onwww.example.com/about.
What exactly do you mean with onNavigationStateChange? This doesn't sound familiar to me.
You should implement WKNavigationDelegate. [https://developer.apple.com/documentation/webkit/wknavigationdelegate].
If you want to load JS after loading an url, you can implement webView(_, didFinish: WKNavigation). WKNavigation offers the api to check request / url.
To load some JS before navigation finished, you can try with webView(WKWebView, decidePolicyFor: WKNavigationAction) [https://developer.apple.com/documentation/webkit/wknavigationdelegate/1455641-webview]
or webView(WKWebView, decidePolicyFor: WKNavigationResponse) [https://developer.apple.com/documentation/webkit/wknavigationdelegate/1455643-webview].
See WKUserScript, which is designed specifically for this purpose. When creating the WKWebView, you'll add a WKUserContentController, which can add scripts using addUserScript.

Controlls missing on Google Maps API3 in Phonegap iOS App

I am working on an app that is built using jQuery mobile and Phonegap. It has been working fine for over a year. Then a month or two ago, the controls (specifically map type, zoom, and street view) went missing without the app being updated, and only on iOS.
I have tried running the app through the phone's browser and it works fine. It is only after it has been compiled by phonegap (build) that the issue exists. I've attempted to manually force the UI settings to true, but only the map type control comes up. I have also tried modifying CSS to give the controls a crazy high z-index, and that made no difference either.
I have not been able to find any relevant information online. How can I get the controls to show up again?
In my case I had a missing street view icon. Google Maps iframe didn't trigger onload event because Cordova rejected empty url of iframe.
ERROR Internal navigation rejected - <allow-navigation> not set for url='about:blank'
I set <allow-navigation href="*" /> in config.xml
I've been in contact with Google, and it turns out a bug was introduced in version 3.32 that's causing this. They're working on it as far as I know, but came up with a workaround in the meantime.
First, you need to specify version 3.31 in the script tag:
<script type="text/javascript" src="https://maps.googleapis.com/maps/api/js?key=YOUR_KEY_HERE&v=3.31"></script>
And then when creating the mapOptions, manually set the controls you want to true. In my case:
var mapOptions = {
center: latlng,
zoom: 16,
mapTypeControl: true,
streetViewControl: true,
zoomControl: true
};
For those who do not use Phonegap but native WKWebView and you by default disabled any scheme except "file"... you must also enable "about" scheme in webView() func in ViewController.swift
func webView(_ webView: WKWebView, decidePolicyFor navigationAction: WKNavigationAction, decisionHandler: #escaping (WKNavigationActionPolicy) -> Void) {
var url = navigationAction.request.url;
let scheme = url!.scheme;
// open local scheme in WebView
if (scheme == "file") {
decisionHandler(.allow);
}
// needed to show google maps icons for controls like zoomControl and streetViewControl
else if (scheme == "about") {
decisionHandler(.allow);
}
// other schemes like fb/http/https/mailto open in external application
else {
UIApplication.shared.openURL(url!);
decisionHandler(.cancel);
}
}

How to open a Link to a PDF with wkwebview

I created a simple iOS app, which opens a URL with WKWebView. On the website, there is a link to a PDF Document. When I open the site on my browser, I can click onto the Link, and the PDF document opens. But on my App, nothing happens when I click onto the link.
How can I fix it? Do I have to put something into my info.plist?
SWIFT 3.* & 4.* *
First you have to download that pdf file into your app, after downloading you have to get that file path, then that file path should be use like following way in WKWebView.
let fileURL = URL(fileURLWithPath: filePathURLData as! String)
//print(fileURL)
webView.loadFileURL(fileURL, allowingReadAccessTo: fileURL)
Here filePathURLData is your actual file path which you have downloaded into your app, you have to convert this into URL, then you need to load that file into WKWebView
Thanks
Hope this will help you.
This will show any file in the WKWebView (doc, docx, xlsx, pdf, google doc, pages & Any textfile)
Likely, you are using target="_blank" in your anchor tag. That opens up a new window to display the link. WKWebView is blocking your attempt to open a new window (at least by default).
The code below still does not create a new window, but instead opens the PDF, etc link in the current WKWebView.
The other option seems to be to create a new WKWebView and return it, so the ios and open the link in that, but I don't want extra Views being created by every click on a website inside the WKWebView.
In your ViewController.viewDidLoad
webView.uiDelegate = self
Then add the extension for the delegate
extension ViewController: WKUIDelegate {
/**
* Force all popup windows to remain in the current WKWebView.
* By default, WKWebView is blocking new windows from being created
* ex text.
* This code catches those popup windows and displays them in the current WKWebView.
*/
func webView(_ webView: WKWebView, createWebViewWith configuration: WKWebViewConfiguration, for navigationAction: WKNavigationAction, windowFeatures: WKWindowFeatures) -> WKWebView? {
// open in current view
webView.load(navigationAction.request)
// don't return a new view to build a popup into (the default behavior).
return nil;
}
}
I have same issue, and found out the pdf file is using unsecured http. Therefore the app refuse to open it.
Try to check with https link and see if it works.
Also with WKWebView, you don't need to download the pdf file before open it. Just load the url directly, i.e
webView.load(URLRequest(url: URL(string: "https-pdf-link")!))
Objective C:
NSURL *fileUrl = [[NSURL alloc] initWithString:filePathURLData];
[webView loadFileURL:fileUrl allowingReadAccessToURL:fileUrl];
Another way to open PDF saved in document directory:
NSData *data = [NSData dataWithContentsOfFile:fileURL];
[webView loadData:data MIMEType:#"application/pdf" characterEncodingName:#"" baseURL:[NSURL URLWithString:fileURL]];

Resources