UIWebView and JavaScriptInterface in Swift - ios

How can I to create the JavascriptInterface channel from my web site to my UIWebView?
Example in Android:
webView.addJavascriptInterface(new WebAppInterface(this), "Android");
And from this JavascriptInterface I would like to draw the methods, as for example:
func webViewDidStartLoad(webView: UIWebView)
or
myActivityIndicator.startAnimating()
How can I do?

For WKWebView: source here
JavaScript
function callNativeApp () {
try {
webkit.messageHandlers.callbackHandler.postMessage("Hello from JavaScript");
} catch(err) {
console.log('The native context does not exist yet');
}
}
Swift
import WebKit
class ViewController: UIViewController, WKScriptMessageHandler {
#IBOutlet var containerView: UIView? = nil
var webView: WKWebView?
override func loadView() {
super.loadView()
let contentController = WKUserContentController()
contentController.addScriptMessageHandler(self, name: "callbackHandler")
let config = WKWebViewConfiguration()
config.userContentController = contentController
self.webView = WKWebView( frame: self.containerView!.bounds, configuration: config)
self.view = self.webView
}
override func viewDidLoad() {
super.viewDidLoad()
//I use the file html in local
let path = NSBundle.mainBundle().pathForResource("index", ofType: "html")
let url = NSURL(fileURLWithPath: path!)
let req = NSURLRequest(URL: url)
self.webView!.loadRequest(req)
}
func userContentController(userContentController: WKUserContentController, didReceiveScriptMessage message: WKScriptMessage) {// edit: changed fun to func
if (message.name == "callbackHandler"){
print("\(message.body)")
}
}
}
For UIWebView: source here
JavaScript in HTML
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.3/jquery.min.js"></script>
<script>
(function(){
$(window).load(function(){
$('.clickMe').on('click', function(){
window.location = "foobar://fizz?Hello_from_javaScript";
});
});
})(jQuery);
</script>
Swift
import UIKit
class ViewController: UIViewController, UIWebViewDelegate {
#IBOutlet weak var Web: UIWebView!
override func viewDidLoad() {
super.viewDidLoad()
let path = NSBundle.mainBundle().pathForResource("index", ofType: "html")
let url = NSURL(fileURLWithPath: path!)
let req = NSURLRequest(URL: url)
Web.delegate = self
Web.loadRequest(req)
}
func webView(webView: UIWebView, shouldStartLoadWithRequest request: NSURLRequest, navigationType: UIWebViewNavigationType) -> Bool {
if request.URL?.query != nil {
print("\(request.URL!.query!)")
}
return true
}
}

Here's a quick example:
Register a URL Scheme such as foobar in Xcode
Handle this URL Scheme in your web view
func webView(webView: UIWebView, shouldStartLoadWithRequest request: NSURLRequest, navigationType: UIWebViewNavigationType) -> Bool {
if request.URL?.query?.containsString("show_activity_indicator=true") {
myActivityIndicator.startAnimating()
}
}
Finally, call this from your JavaScript
// Show your activity indicator from JavaScript.
window.location = "foobar://fizz?show_activity_indicator=true"
Note: See my question here for more information on web view communication in iOS.

Related

How to Handle Image uploading in WKWebview?

I have use webkit to show url on my app.
when I click on image button(green button in ), it shows
camera or photo library.
issue is, when click Use photo, the webkit
refresh automatically instead showing on page
I have already give photo usage, media, camera permission in plist file
my code is
import UIKit
import WebKit
import Alamofire
class ContinueViewController: UIViewController, WKUIDelegate {
#IBOutlet weak var activityView: UIActivityIndicatorView!
#IBOutlet weak var webview: WKWebView!
func getPostString(params:[String:Any]) -> String{
var data = [String]()
for(key, value) in params
{
data.append(key + "=\(value)")
}
return data.map { String($0) }.joined(separator: "&")
}
var url: URL?
var traderCategoryId = 0, tradeSkillId = 0
override func viewDidLoad() {
super.viewDidLoad()
webview.navigationDelegate = self
self.navigationController?.navigationBar.isHidden = false
webview.uiDelegate = self
}
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
activityView.startAnimating()
guard let url = self.url else { return }
var req = URLRequest(url: url)
let params = ["id" : id,"trader_skills" : tradeSkillId]
let postString = self.getPostString(params: params)
req.addValue("application/x-www-form-urlencoded", forHTTPHeaderField: "Content-Type")
req.httpMethod = "POST"
req.httpBody = postString.data(using: .utf8)
self.webview.load(req)
}
}
extension ContinueViewController: WKNavigationDelegate {
func webView(_ webView: WKWebView, didFinish navigation: WKNavigation!) {
activityView.stopAnimating()
}
}
extension UIImagePickerController {
open override func viewDidLoad() {
super.viewDidLoad()
// self.modalPresentationStyle = .fullScreen
}
}
issue is, when click Use photo, the webkit refresh automatically instead showing on page
Because you self.webview.load(req) in
override func viewWillAppear(_ animated: Bool) {
shows camera or photo library, then return to this page
func viewWillAppear called again
self.webview.load(req) called again
do it simple ,
put self.webview.load(req) the relevant in
override func viewDidLoad() {

How to get JS event handler with URL in swift

class FeedBackFormViewController: UIViewController, WKUIDelegate, WKScriptMessageHandler {
#IBOutlet weak var webViewShowing: UIView!
var formWebView: WKWebView!
private let fileString = UserSingleton.shared.feedbackFormLink
override func viewDidLoad() {
super.viewDidLoad()
setupJSFile()
formWebView.navigationDelegate = self
previewFiles()
}
// show files in web view
private func previewFiles() {
if let fileString = fileString, fileString != "" {
let url = URL(string: fileString)
let myRequest = URLRequest(url: url!)
UserSingleton.shared.showHUD()
formWebView.load(myRequest)
}else {
Alerts.shared.show(alert: .error, message: "no file found", type: .error)
}
}
private func setupJSFile() {
let config = WKWebViewConfiguration()
let js = "document.addEventListener('submit', function(){ window.webkit.messageHandlers.clickListener.postMessage('My hovercraft is full of eels!'); })"
let script = WKUserScript(source: js, injectionTime: .atDocumentEnd, forMainFrameOnly: false)
config.userContentController.addUserScript(script)
config.userContentController.add(self, name: "submit")
formWebView = WKWebView(frame: UIScreen.main.bounds, configuration: config)
webViewShowing.addSubview(formWebView)
}
func userContentController(_ userContentController: WKUserContentController, didReceive message: WKScriptMessage) {
print(message.name)
}
#IBAction func dismissFeedbackFormVC(_ sender: UIButton) {
self.dismiss(animated: true, completion: nil)
}
}
extension FeedBackFormViewController: WKNavigationDelegate {
func webView(_ webView: WKWebView, didFinish navigation: WKNavigation!){
webView.evaluateJavaScript("document.readyState") { (result, error) in
if let result = result {
print(result)
}
}
}
}
only call wk navigation when the web view is loaded and I click anywhere no trigger call in userContentController any event
You'd need to add this script for execution, just append it at the end of the func setupJSFile:
formWebView.configuration.userContentController.addUserScript(script)
also, you'd need to change your JS script to
document.addEventListener('click', function(){ window.webkit.messageHandlers['iosListener'].postMessage('click clack!'); })

WKWebView evaluateJavaScript not returning html

I am trying to parse the html returned from a WKWebView load() with evaluateJavaScript but it never prints anything. Am I doing this right? Any other ways? didFinish does print.
import UIKit
import WebKit
class MyWebViewController: UIViewController, WKNavigationDelegate {
var webView: WKWebView!
override func viewDidLoad() {
super.viewDidLoad()
webView = WKWebView(frame: self.view.frame)
webView.navigationDelegate = self
let url = NSURL (string: "https://google.com");
let request = NSURLRequest(url: url! as URL)
webView.load(request as URLRequest)
self.view.addSubview(webView)
self.view.sendSubview(toBack: webView)
}
func webView(_ webView: WKWebView, didFinish navigation: WKNavigation!) {
webView.evaluateJavaScript("document.documentElement.outerHTML.toString()", completionHandler: { (html: AnyObject?, error: NSError?) in
print(html!)
} as? (Any?, Error?) -> Void)
print("didFinish")
}
}
Using evaluateJavaScript with a WKWebView is a bit tricky.
Since I think this answer would be useful to many people, rather than address your specific question with a short code snippet and a comment that you need to implement WKScriptMessageHandler, I'm going to post a full, complete example that you can use to see how everything works together.
To use this, create a "Single View Application" iOS project in Xcode and paste this over the default ViewController.swift file.
//
// WebViewController.swift
// WKWebViewExample
//
// Created by par on 4/2/17.
// Copyright © 2017 par. All rights reserved. MIT License.
//
import UIKit
import WebKit
class ViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
let webViewController = WebViewController()
// install the WebViewController as a child view controller
addChildViewController(webViewController)
let webViewControllerView = webViewController.view!
view.addSubview(webViewControllerView)
webViewControllerView.translatesAutoresizingMaskIntoConstraints = false
webViewControllerView.topAnchor.constraint(equalTo: view.topAnchor).isActive = true
webViewControllerView.bottomAnchor.constraint(equalTo: view.bottomAnchor).isActive = true
webViewControllerView.leftAnchor.constraint(equalTo: view.leftAnchor).isActive = true
webViewControllerView.rightAnchor.constraint(equalTo: view.rightAnchor).isActive = true
webViewController.didMove(toParentViewController: self)
}
}
class WebViewController: UIViewController, WKNavigationDelegate, WKScriptMessageHandler {
private var webView: WKWebView!
private var webViewContentIsLoaded = false
init() {
super.init(nibName: nil, bundle: nil)
self.webView = {
let contentController = WKUserContentController()
contentController.add(self, name: "WebViewControllerMessageHandler")
let configuration = WKWebViewConfiguration()
configuration.userContentController = contentController
let webView = WKWebView(frame: .zero, configuration: configuration)
webView.scrollView.bounces = false
webView.navigationDelegate = self
return webView
}()
}
required init?(coder aDecoder: NSCoder) { fatalError("init(coder:) has not been implemented") }
override func viewDidLoad() {
super.viewDidLoad()
view.addSubview(webView)
webView.translatesAutoresizingMaskIntoConstraints = false
webView.topAnchor.constraint(equalTo: view.topAnchor).isActive = true
webView.bottomAnchor.constraint(equalTo: view.bottomAnchor).isActive = true
webView.leftAnchor.constraint(equalTo: view.leftAnchor).isActive = true
webView.rightAnchor.constraint(equalTo: view.rightAnchor).isActive = true
}
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
if !webViewContentIsLoaded {
let url = URL(string: "https://stackoverflow.com")!
let request = URLRequest(url: url)
webView.load(request)
webViewContentIsLoaded = true
}
}
private func evaluateJavascript(_ javascript: String, sourceURL: String? = nil, completion: ((_ error: String?) -> Void)? = nil) {
var javascript = javascript
// Adding a sourceURL comment makes the javascript source visible when debugging the simulator via Safari in Mac OS
if let sourceURL = sourceURL {
javascript = "//# sourceURL=\(sourceURL).js\n" + javascript
}
webView.evaluateJavaScript(javascript) { _, error in
completion?(error?.localizedDescription)
}
}
// MARK: - WKNavigationDelegate
func webView(_ webView: WKWebView, didFinish navigation: WKNavigation!) {
// This must be valid javascript! Critically don't forget to terminate statements with either a newline or semicolon!
let javascript =
"var outerHTML = document.documentElement.outerHTML.toString()\n" +
"var message = {\"type\": \"outerHTML\", \"outerHTML\": outerHTML }\n" +
"window.webkit.messageHandlers.WebViewControllerMessageHandler.postMessage(message)\n"
evaluateJavascript(javascript, sourceURL: "getOuterHMTL")
}
// MARK: - WKScriptMessageHandler
func userContentController(_ userContentController: WKUserContentController, didReceive message: WKScriptMessage) {
guard let body = message.body as? [String: Any] else {
print("could not convert message body to dictionary: \(message.body)")
return
}
guard let type = body["type"] as? String else {
print("could not convert body[\"type\"] to string: \(body)")
return
}
switch type {
case "outerHTML":
guard let outerHTML = body["outerHTML"] as? String else {
print("could not convert body[\"outerHTML\"] to string: \(body)")
return
}
print("outerHTML is \(outerHTML)")
default:
print("unknown message type \(type)")
return
}
}
}
I know it has been a while since this was answered and I found that this didn't really work for me. I used the below instead.
I could not get the evaluateJavaScript to work until i added the setTimeout() function. For what it is worth here is my code below.
webView.evaluateJavaScript("setTimeout(function(){
// Do something here
},10);") { (result1, error) in
if error == nil {
print(result1 ?? "")
}
}

Meme Type Error on UiWebView

I have a iOS app which has a uiwebview where it opens a website, but any link clicked in the webview opens up in Safari. In my code bellow, it get a memetype error on the line "webView.load(request)". Can you give me some code so I can fix this? Thanks! (I need to use a UIWebView instead of a WKWebView)
class VideosViewController : UIViewController, UIWebViewDelegate {
#IBOutlet weak var webView : UIWebView!
override func viewDidLoad() {
super.viewDidLoad()
guard let url = URL(string: "http://example.com") else { return }
let request = URLRequest(url: url)
webView.load(request)
}
func webView(_ webView: UIWebView, shouldStartLoadWith request: URLRequest, navigationType: UIWebViewNavigationType) -> Bool {
if navigationType == .linkClicked {
guard let url = request.url else { return true }
UIApplication.shared.open(url, options: [:], completionHandler: nil)
return false
}
return true
}
}
Try to call webView.loadRequest(request). This function has only one URLRequest parameter.

Back button not working for my manually loaded UIWebView for external website

Technologies: Swift, iOS8.1.3, XCode 6, Maverick
I've added a "rewind" back button to my UIWebview via the main.storyboard and connected it here on my view with a backButton func:
class ViewController: UIViewController, UIWebViewDelegate {
#IBOutlet weak var webView: UIWebView!
var detailItem: NSURL?
var grabData = false
override func viewDidLoad() {
super.viewDidLoad()
webView.delegate = self
self.navigationController?.toolbarHidden = false;
}
#IBAction func backButton(sender: AnyObject) {
self.webView.goBack()
}
override func viewDidAppear(animated: Bool) {
super.viewDidAppear(animated)
if let url = self.detailItem {
webView.loadRequest(NSURLRequest(URL: url))
}
}
func webView(webView: UIWebView, shouldStartLoadWithRequest request:
NSURLRequest, navigationType: UIWebViewNavigationType) -> Bool {
if grabData {
grabData = false
return true
} else {
grabData = true
manuallyLoadPage(request)
return false
}
}
func manuallyLoadPage(request: NSURLRequest) {
let session = NSURLSession.sharedSession()
let task = session.dataTaskWithRequest(request) {
(data, response, error) invar html = NSString(data: data, encoding: NSUTF8StringEncoding) as String
html = html.stringByReplacingOccurrencesOfString("</head>", withString: "<styles><link rel='stylesheet' type='text/css' href='link-to-styles.css' media='all'></styles></head>", options: NSStringCompareOptions.CaseInsensitiveSearch, range: nil)
self.webView.loadHTMLString(html, baseURL: response.URL!)
}
task.resume()
}
My back button doesn't do anything when clicked and I want it to show the previous webpage. I think it has something to do with how I am intercepting the session and manually loading the WebView. Maybe it doesn't know what the original base url is? Something else?

Resources