Get Javascript function response in iOS using Swift - javascript

I am trying to figure out how to implement JavaScript functions inside my iOS app using swift platform .
I have tried out the example code specified in the link
https://tetontech.wordpress.com/2014/07/15/swift-to-javascript-and-javascript-to-swift-a-round-trip/
But i want in the same way like android is doing as specified in the link http://android-er.blogspot.in/2011/10/call-javascript-inside-webview-from.html
Could you please help me out.
Any help will deeply appriciated.

If you are using WKWebView you can call javascript by using
webView.evaluateJavaScript("YourJavaScript",completionHandler : nil )
For UIWebView you can use
webview.stringByEvaluatingJavascriptFromString : #"YourJavaScript";

These two links (first, second) are helped to me solve my problem.
html/javascript code:
function bar(qq){
document.getElementById('foo').innerHTML = qq;
}
<div id="foo"></div>
My ViewController looks like this:
class ViewController: UIViewController, WKNavigationDelegate, WKUIDelegate {
var webView: WKWebView
required init(coder aDecoder: NSCoder) {
self.webView = WKWebView(frame: CGRect.zero)
super.init(coder: aDecoder)!
}
override func loadView() {
super.loadView()
let userContentController = WKUserContentController()
let source = "bar('Hello from swift');"
let userScript = WKUserScript(source: source, injectionTime: WKUserScriptInjectionTime.atDocumentEnd, forMainFrameOnly: true)
userContentController.addUserScript(userScript)
let configuration = WKWebViewConfiguration()
configuration.userContentController = userContentController
self.webView = WKWebView(frame: self.view.frame, configuration: configuration)
}
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
view.addSubview(webView)
webView.translatesAutoresizingMaskIntoConstraints = false
let height = NSLayoutConstraint(item: webView, attribute: .height, relatedBy: .equal, toItem: view, attribute: .height, multiplier: 1, constant: 0)
let width = NSLayoutConstraint(item: webView, attribute: .width, relatedBy: .equal, toItem: view, attribute: .width, multiplier: 1, constant: 0)
view.addConstraints([height, width])
let path = Bundle.main.path(forResource: "foldername/index", ofType: "html")!
let url = URL(fileURLWithPath: path)
webView.load(URLRequest(url: url))
webView.allowsBackForwardNavigationGestures = true
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
}

var AppiwebScript = function() {//in some_file.js
//TO DO
};
//below is code for xcode
let jScript = "AppiwebScript();"//name js function, in js file
let str_val = mainWebView.stringByEvaluatingJavaScript(from: jScript)//handeleng js function

Related

How to autofill credit/debit-card details in inputfield in amazon.in in wkwebview in ios app?

I'm developing iOS app in which there is webview(wkwebview), there will be website loaded in wkwebview like amazon.in
I have credit/debit-card details like card-number, exp date, etc. securely stored in my iOS app, i want to populate credit/debit-card details into amazon.in payment page inputfields.
Default safari browser in iPhone is able to autofill credit/debit cards if saved.
Please let me know the solution for the same.
This is amazon payment page:
class ViewController: UIViewController {
let scriptSource = "document.getElementsByName('addCreditCardNumber')[0].value = '1111 1111 1111 1111'"
var webConfig: WKWebViewConfiguration {
get {
let webCfg = WKWebViewConfiguration()
let controller = WKUserContentController()
let pre = WKPreferences()
pre.javaScriptEnabled = true
webCfg.preferences = pre
let script = WKUserScript(source: scriptSource, injectionTime: .atDocumentEnd, forMainFrameOnly: false)
controller.addUserScript(script)
webCfg.userContentController = controller
return webCfg;
}
}
lazy var webView: WKWebView = {
let webView = WKWebView(frame: .zero, configuration: self.webConfig)
webView.translatesAutoresizingMaskIntoConstraints = false
return webView
}()
override func viewDidLoad() {
super.viewDidLoad()
self.addWKWebView()
}
private func addWKWebView(){
self.view.addSubview(webView)
self.view.addConstraints(NSLayoutConstraint.constraints(withVisualFormat: "V:|[webView]|", options: [], metrics: nil, views: ["webView": webView]))
self.view.addConstraints(NSLayoutConstraint.constraints(withVisualFormat: "H:|[webView]|", options: [], metrics: nil, views: ["webView": webView]))
webView.load(URLRequest(url: URL(string: "https://www.amazon.in")!))
webView.uiDelegate = self
webView.navigationDelegate = self
}}
You can add a custom UserScript for a page with a WKUserScript-object. In it you can use standard JavaScript like document.getElementById("creditcard").defaultValue = "12345";
This tutorial should give you an idea of it: https://medium.com/capital-one-tech/javascript-manipulation-on-ios-using-webkit-2b1115e7e405. For more info on WKUserScript take a look at the official documentation (https://developer.apple.com/documentation/webkit/wkuserscript)
Here is What i was using for append my message in webView :
public static func appendNewMessage(chatListWebView : UIWebView, appendTemplet : String) {
do {
let temp = "document.getElementById('message_div').innerHTML +=\"" + appendTemplet + "\""
print(temp)
chatListWebView.stringByEvaluatingJavaScript(from: "document.getElementById('message_div').innerHTML +=\"" + appendTemplet + "\"");
} catch {
// JLog.error(appendTemplet);
// ex.printStackTrace();
}
}
You can update it according to you situation , Sorry but i do not have time to edit it
Hope it help to you!

Swift WKWebView interface with javascript is not run

I just build my app with WKWebView with swift 4.0 (iOS 12.1)
I need to run my javascript from online html,
However if I move url from index to another page,
my print message does not working at all.
let contentController = WKUserContentController()
contentController.add(self, name: "myHandler")
let configuration = WKWebViewConfiguration()
configuration.userContentController = contentController
webview = WKWebView(frame: self.view.frame, configuration: configuration)
webview.uiDelegate = self
webview.navigationDelegate = self
self.view = self.webview
override func viewDidLoad() {
super.viewDidLoad()
let url = URL(string: "example.com/index") // of course https://
let request = URLRequest(url: url!)
webview.load(request)
I missed userContentController when I move to another html page in the webView. Index page can call this one but another page do not call any function.
index.html(ok) -> link to a.html -> a.html (not ok) -> link to index.html -> index.html(ok)
Do not Call this one :
func userContentController(_ userContentController: WKUserContentController, didReceive message: WKScriptMessage) {
print("call print out")
if message.name == "myHandler" {
print("JS -> Native Call \(message.body)")
abc()
} else {
print("JS -> Native N/A")
}
}
I cannot get any message even "call print out" at all.
How can I active userContentController in my code?
Javascript must be enabled through WKPreferences. You can specify these in the preferences field of you WKWebViewConfiguration. Javascript is disabled by default.
You simply need to set the javaScriptEnabled field of the preferences object to true. There are several useful settings that can be changed by the preferences object. I would urge you to look at the documentation.
Specifically your code to initialize the WKWebView should look like this.
let contentController = WKUserContentController()
contentController.add(self, name: "myHandler")
let configuration = WKWebViewConfiguration()
configuration.userContentController = contentController
configuration.preferences = WKPreferences()
configuration.preferences.javaScriptEnabled = true
webview = WKWebView(frame: self.view.frame, configuration: configuration)
webview.uiDelegate = self
webview.navigationDelegate = self
self.view = self.webview
PS You may also want to have a look at this question. I'm not marking yours as a duplicate because your question asks why javascript isn't working, not how to enable it.
EDIT: JavaScript is now enabled by default. This answer is no longer accurate.
Correct usage as of iOS 14+
let contentController = WKUserContentController()
contentController.add(self, name: "myHandler")
let configuration = WKWebViewConfiguration()
configuration.userContentController = contentController
configuration.preferences = WKPreferences()
// Here's the new magic for iOS 14:
let webPageDefaultPrefs = WKWebpagePreferences()
webPageDefaultPrefs.allowsContentJavaScript = true
config.defaultWebpagePreferences = webPageDefaultPrefs
webview = WKWebView(frame: self.view.frame, configuration: configuration)
webview.uiDelegate = self
webview.navigationDelegate = self
self.view = self.webview

How to inject a javascript in WKWebView in Swift 3

I am trying to exclude some webpage elements like sidebar by injecting a new CSS rule throughout javascript, I created a new javascript file in xcode with the a new style tag to hide the sidebar that I inspected in Google chrome developer tool and ended with this code:
var styleTag = document.createElement("style");
styleTag.textContent = '.sidebar {display:none;}';
document.documentElement.appendChild(styleTag);
and created WKWebViewConfiguration object which holds some properties that allow the creation of the bridge between native code and the hosted web content as described here but still not getting this script running.
This is my complete ViewController code:
import UIKit
import WebKit
class scrapedBrowser: UIViewController, WKNavigationDelegate {
var webView: WKWebView!
var progressView: UIProgressView!
override func loadView() {
webView = WKWebView()
webView.navigationDelegate = self
view = webView
}
required init?(coder aDecoder: NSCoder) {
let config = WKWebViewConfiguration()
let scriptURL = Bundle.main.path(forResource: "hideSections", ofType: "js")
let scriptContent:String?
do {
scriptContent = try String(contentsOfFile: scriptURL!, encoding: String.Encoding.utf8)
}
catch _ {
scriptContent = nil
}
let script = WKUserScript(source: scriptContent!, injectionTime: .atDocumentStart, forMainFrameOnly: true)
config.userContentController.addUserScript(script)
super.init(coder: aDecoder)
self.webView?.navigationDelegate = self
}
override func viewDidLoad() {
super.viewDidLoad()
let url = URL(string: "http://yourwebsite.com/")!
let request = URLRequest(url: url)
webView.load(request)
webView.allowsBackForwardNavigationGestures = true
}
func webView(_ webView: WKWebView, didFinish navigation: WKNavigation!) {
title = webView.title
}
}
You need to assign config to the WKWebView.
webView = WKWebView(frame: CGRect.zero, configuration: config)
You have created config and added script to in init and you haven't kept reference of config. Later on loadView you are creating wkwebview and not setting configuration you created earlier on in init

WKWebView xmlhttprequest with file url

I am migrating from UIWebView to WKWebView with my local HTMLs in the app documents folder.
I can load the index page with all css and js files, but every ajax call (xmlhttprequest) fails because of allowed-access-origin.
I don´t want to use a webserver in my application, because I think it would be oversized. How can I make it possible? The app is a simple HTML5 application for an in house app. The device can´t get online or anything so security could be disabled completly.
This solved my problem:
let config = WKWebViewConfiguration()
config.userContentController = contentController
config.preferences.setValue(true, forKey: "allowFileAccessFromFileURLs")
webView = WKWebView(frame: .zero, configuration: config)
webView.uiDelegate = self
webView.navigationDelegate = self
view = webView
For those who came though SO to find this topic:
This isn't official to turn off the security in WKWebView, but we could use the private API to allow this just like this guy did for the Cordova project: cordova-plugin-wkwebviewxhrfix
The clue is to create the configuration for the WebView and set the allowFileAccessFromFileURLs property.
WKWebViewConfiguration* configuration = originalImpSend(_self, selector, settings);
// allow access to file api
#try {
[configuration.preferences setValue:#TRUE forKey:#"allowFileAccessFromFileURLs"];
}
#catch (NSException *exception) {}
#try {
[configuration setValue:#TRUE forKey:#"allowUniversalAccessFromFileURLs"];
}
#catch (NSException *exception) {}
return configuration;
But as I mentioned this is the private API and it could be a reason to reject your application in Apple's App Review. If you want to publish your app in App Store, please consider to run some lightweight HTTP server instead of violate overall security of the web view. Example: GCDWebServer.
So the accepted was not working for me when I first tried it (I was doing it wrong at the time) and the bug about (https://bugs.webkit.org/show_bug.cgi?id=154916) it strongly recommends against doing it. My solution was to implement a custom url scheme and use that to load all files. Looks like this.
First creating the WKWebView to which the url scheme is attached (you must create the WKWebView yourself, it can't be created on a storyboard).
override func viewDidLoad() {
let configuration = WKWebViewConfiguration()
configuration.setURLSchemeHandler(PrayerAssetHandler(), forURLScheme: "x-file")
webview = WKWebView(frame: webContentView.bounds, configuration: configuration)
self.webContentView.addSubview(webview!)
webview?.autoresizingMask = webContentView.autoresizingMask
webview!.navigationDelegate = self
super.viewDidLoad()
}
Then implement the handler.
import Foundation
import WebKit
import MobileCoreServices
class PrayerAssetHandler: NSObject, WKURLSchemeHandler {
func webView(_ webView: WKWebView, start urlSchemeTask: WKURLSchemeTask) {
let url = urlSchemeTask.request.url!
let pathArr = url.path.components(separatedBy: ".")
let forResource: String = pathArr[0]
let ofType: String? = pathArr.count > 1 ? pathArr[1] : nil
let bundlePath = Bundle.main.path(forResource: "data_sub_folder" + forResource, ofType: ofType)
let fileExtension: CFString = ofType! as CFString
guard
let extUTI = UTTypeCreatePreferredIdentifierForTag(kUTTagClassFilenameExtension,
fileExtension, nil)?.takeUnretainedValue()
else { return }
guard
let mimeUTI: NSString = UTTypeCopyPreferredTagWithClass(extUTI,
kUTTagClassMIMEType)?.takeRetainedValue()
else { return }
let mimeType: String = mimeUTI as String
do {
let data: Data = try NSData(contentsOfFile: bundlePath!) as Data
//Create a NSURLResponse with the correct mimetype.
let urlResponse = URLResponse(url: url, mimeType: mimeType,
expectedContentLength: data.count, textEncodingName: "utf8")
urlSchemeTask.didReceive(urlResponse)
urlSchemeTask.didReceive(data)
urlSchemeTask.didFinish()
} catch _ as NSError {
return
}
}
func webView(_ webView: WKWebView, stop urlSchemeTask: WKURLSchemeTask) {
}
}
Setting value in preferences is not working in Swift 5.
Set in 'WKWebViewConfiguration' as shown below.
// Configuration
let webConfiguration = WKWebViewConfiguration()
webConfiguration.preferences.setValue(true, forKey: "allowFileAccessFromFileURLs")
webConfiguration.setValue(true, forKey: "allowUniversalAccessFromFileURLs")
// Web View
let webView = WKWebView(frame: webviewContainer.frame, configuration: webConfiguration)

Inject a JavaScript code in Webview iOS

I created a iOS web browser with swift language code. And add an extra button to inject a script on that web page, but it always crash when I try this:
webView!.evaluateJavaScript("document.body.style.background = 'red';", nil)
Any idea how to fix this? And how to read the JavaScript code from a file, and then inject it to that webview element.
I use the code style as this example but with WKWebView:
https://github.com/rshankras/WebViewDemo
If you can solve this question I need a basic working code in the answer. And solution for how to load the JavaScript from a file. And inject that code in the WKWebView element.
I don't see the method you are using (evaluateJavaScript) in the current UIWebView API docs but it is in the WKWebView docs. Maybe you are using the wrong API? Perhaps try using stringByEvaluatingJavaScriptFromString(_:) instead:
let script = "document.body.style.background = 'red'"
if let result = webView.stringByEvaluatingJavaScriptFromString(script) {
println("result is \(result)")
}
Also, i'm not sure if the "!" is needed (a hunch tells me it's not), as there is no context around your code. So maybe try both versions.
Getting a string from a file is something like:
let path = NSBundle.mainBundle().pathForResource("jsFileName", ofType: "js")
if let content = String.stringWithContentsOfFile(path, encoding: NSUTF8StringEncoding, error: nil) {
println("js content is \(content)")
}
Loading from disk has a lot of variables around how your file is being copied and stored so your going to have to do some work to fit the path variable to your structure.
This will work with WKWebView. Dont forget to add WebKit frame work on top on your class definition
var webView: WKWebView!
func loadWebViewWithCustomJavaScript {
//Create Preferences
let preferences = WKPreferences()
preferences.javaScriptEnabled = true
//Initialise javascript file with user content controller and configuration
let configuration = WKWebViewConfiguration()
let scriptURL = NSBundle.mainBundle().pathForResource("Your File Name", ofType: "js")
var scriptContent = ""
do {
scriptContent = try String(contentsOfFile: scriptURL!, encoding: NSUTF8StringEncoding)
} catch{
print("Cannot Load File")
}
let script = WKUserScript(source: scriptContent, injectionTime: .AtDocumentStart, forMainFrameOnly: true)
configuration.userContentController.addUserScript(script)
configuration.preferences = preferences
//Create WebView instance
webView = WKWebView(frame: CGRectMake(0,0, self.view.frame.size.width, self.view.frame.size.height), configuration: configuration)
view.addSubview(webView)
//Finally load the url
let url = NSURL(string:"your URL")
let urlRequest = NSURLRequest(URL: url!)
webView.loadRequest(urlRequest)
}
Sample JavaScript code for injection
//Hides name of "background1" element on the page
var styleTag = document.createElement("style");
styleTag.textContent = 'div#background1, .after-post.widget-area {display:none;}';
document.documentElement.appendChild(styleTag);
Got it working using following. Used String instead of NSString to use native swift 2. Javascript code to inject must be in hidesections.js file
let jsPath = NSBundle.mainBundle().pathForResource("hidesections", ofType: "js");
let jsContent: String?
do
{
jsContent = try String(contentsOfFile: jsPath!, encoding: NSUTF8StringEncoding)
}
catch _
{
jsContent = nil
}
webView.stringByEvaluatingJavaScriptFromString(jsContent!)
If you are using WKWebView here is a solution.
if let scriptFile = NSBundle.mainBundle().pathForResource("script", ofType: "js") {
var error: NSError?
let scriptString = NSString(contentsOfFile: scriptFile, encoding: NSUTF8StringEncoding, error: &error)
if let error = error {
println("Error: Could not load script file => \(error)")
} else {
if let scriptString = scriptString {
let script = WKUserScript(source: scriptString, injectionTime: .AtDocumentEnd, forMainFrameOnly: true)
let controller = WKUserContentController()
controller.addUserScript(script)
let configuration = WKWebViewConfiguration()
configuration.userContentController = controller
let webView = WKWebView(frame: self.view.bounds, configuration: configuration)
self.webView = webView
self.view.addSubview(webView)
}
}
}

Categories

Resources