How to use webView.evaluateJavaScript in Swift - javascript

I need my swift class to interact with the html and javascript in a wkwebview that it references, in particular, feed it a variable.
I thought I would start simply by trying to get the webview to retrieve something using getElementByID following the code here. However, it is not working.
Here is code:
HTML file
<div id="username">#twostraws</div>
Swift File
let webView = WKWebView()
override func viewDidLoad() {
super.viewDidLoad()
if let url = Bundle.main.url(forResource: "myHTML", withExtension: "html") {
webView.loadFileURL(url, allowingReadAccessTo: url.deletingLastPathComponent())
}
webView.evaluateJavaScript("document.getElementById('username').innerText") { (result, error) in
if let result = result {
print("what is in div",result)
}
else {
print("didn't find it")
}
}
override func loadView() {
self.view = webView
}
The webview is loading, however nothing is getting retrieved by evaluateJavascript.
Edit:
This is error message:
A JavaScript exception occurred" UserInfo={WKJavaScriptExceptionLineNumber=1, WKJavaScriptExceptionMessage=TypeError: null is not an object (evaluating 'document.getElementById('username').innerText'), WKJavaScriptExceptionColumnNumber=32, WKJavaScriptExceptionSourceURL=about:blank, NSLocalizedDescription=A JavaScript exception occurred})
Thanks for any suggestions.

Related

JavascriptCore iOS Empty Parameter causes Thread 1: EXC_BAD_ACCESS

New to using JavascriptCore iOS Framework and need some help. I'm attempting to write a method in Swift that can import JS code into another JS file and I'm getting Thread 1: EXC_BAD_ACCESS error in Xcode. This is due to an empty parameter given in the file that I'm trying to evaluate. The error is happening specifically where I'm checking if the filePath type is String. What type would this be if there was no parameter given while handling this exception? Under my filePath variable I'm getting this: object = (Builtin.RawPointer)0x0
Here is my swift code:
let context: JSContext = JSContext.init()
var scriptPath: String = Bundle.main.path(forResource: "script", ofType: "js")!
var content: String = try! String(contentsOfFile: scriptPath, encoding: .utf8)
context.exceptionHandler =
{(context, exception) in
print(exception!.toString()!)
}
var importFile : #convention(block) (AnyObject) -> Void =
{ (filePath) in
if((filePath) is String != true)
{
print("Import Error: \(filePath) is not of type String.")
}
}
context.setObject(importFile, forKeyedSubscript: "importFile" as NSString)
context.evaluateScript(content)
And here is my JS file I'm trying to implement this in (script.js):
importFile("file.js")
importFile()
Any help would be appreciated. Thx.

How do I move from my html file to another html file to create an iOS hybrid app?

I'd like to replace a webView page with a Spring-MVC structure with a hybrid format So I'd like to replace the JSP file with an HTML file and put it in the ios project folder.
This part successfully inserted the html test file, and the html load was also successful.
The problem is the current page file should be moved to other html in a file that is. Previously, the address of the page controller file was managed by Java, and the page was moved to the address value, but now the html file must be displayed.
current PageController
public class PageController {
#RequestMapping("/index")
public String index(Map<String, Object> model) {
return "/index";
}
...
current move page
function pagemove(_url) {
location.href = _url; // url: "/nextPage"
}
curerent WebViewLoad
func loadWebPage(_ webUrl : String) {
guard let myUrl = URL(string: webUrl) else {
//report invalid URL
return
}
let myRequest = URLRequest(url: myUrl)
WKWebView.load(myRequest)
}
Now I'm moving the screen in this way. How can I move if I switch to HTML?
Loading Web Views from iOS(testing)
func loadWebPage(_ webUrl : String) {
let localFilePath = Bundle.main.url(forResource: "HTMLFolder/testweb", withExtension: "html")!
let myRequest = URLRequest(url: localFilePath)
WKWebView.load(myRequest)
}
I'm using Swift5
I want to go to the testwebTwo.HTML file from the testweb.HTML file.
Thanks in advance.
Currently, your project is managed through a server built in Java. But now you have to move your page through the html file inside the ios project.
There are two ways in order to so.
Firstly, if you load a webView into an html file, your webView looks at the path to the project file.
Therefore, when you move pages, you can also specify a path. An example is an example of how html files are in the same path and created.
function pagemove(_url) {
location.href = _url; // url: "./testwebTwo.html"
}
Second, you can load page data by sending and receiving messages through js and native communication.
js function
function testMovePage() {
var data = {};
data.movePage = "HTMLFolder/testwebTwo";
try {
webkit.messageHandlers.yourMessageKey.postMessage(data);
} catch (error) {
alert(error);
}
}
Receive From Swift
#available(iOS 8.0, *)
func userContentController(_ userContentController: WKUserContentController, didReceive message: WKScriptMessage) {
if message.name == "yourMessageKey" {
let sendmessage = message.body as! NSDictionary
guard sendmessage["movePage"] != nil else {
return
}
let urlString : String = sendmessage["movePage"] as! String
let filePath = Bundle.main.url(forResource: urlString, withExtension: "html")
let request = URLRequest(url: filePath)
WKWebView.load(request)
...

How to click a JavaScript button programmatically from WKWebView

I'm trying to get WKWebView to load this calendar page (csbcsaints.org/calendar) and immediately go to the next month by clicking that arrow button. Following along with WWDC 2014's 'Introducing the Modern WebKit API,' here's what I have so far:
class WebViewController: UIViewController {
let jsString = "document.getElementById('evcal_next').click()"
override func viewDidLoad() {
super.viewDidLoad()
let script = WKUserScript(source: jsString, injectionTime: .atDocumentEnd, forMainFrameOnly: true)
let contentController = WKUserContentController()
contentController.addUserScript(script)
let myConfiguration = WKWebViewConfiguration()
myConfiguration.userContentController = contentController
myConfiguration.preferences.javaScriptEnabled = true
let webView = WKWebView(frame: .zero, configuration: myConfiguration)
self.view = webView
if let url = URL(string: "https://csbcsaints.org/calendar") {
let request = URLRequest(url: url)
webView.load(request)
}
}
}
When that didn't work with either .atDocumentStart or .atDocumentEnd, I tried calling webView.evaluateJavaScript(jsString) after loading the page instead, but it only returned an error:
Error Domain=WKErrorDomain Code=4
"A JavaScript exception occurred"
UserInfo={
WKJavaScriptExceptionLineNumber=1,
WKJavaScriptExceptionMessage=
TypeError: null is not an object (evaluating 'document.getElementById('evcal_next').click'),
WKJavaScriptExceptionColumnNumber=38,
WKJavaScriptExceptionSourceURL=about:blank,
NSLocalizedDescription=A JavaScript exception occurred
}
It seems to say that the command I passed was invalid, but I'm not sure, since running jsString in Safari's console performed the effect I wanted. I'm relatively experienced with Swift and iOS, but new to JavaScript, so any help would be appreciated.
You should assign the delegate of the webView like:
webView.delegate = self
and do it inside didEnd... method:
extension WebViewController: WKNavigationDelegate {
func webView(_ webView: WKWebView, didFinish navigation: WKNavigation!) {
/* This is where you can perform DOM with JS in webview */
}
}

Get HTML element value using WKWebKit and Swift 3

Trying to get an html element name or value using preferred evaluate Javascript swift function using a WKWebView. I keep recieving nil. I think maybe my function is firing before the page is loaded, thus never finding the element. Right now just trying to get the element text by class name. Here is my code below in the ViewController
import UIKit
import WebKit
class ViewController: UIViewController, WKUIDelegate, WKNavigationDelegate {
var web: WKWebView!
override func loadView() {
let webConfiguration = WKWebViewConfiguration()
web = WKWebView(frame: .zero, configuration: webConfiguration)
web.uiDelegate = self
view = web
}
override func viewDidLoad() {
super.viewDidLoad()
let url = URL(string: "http://www.appcoda.com")
let myRequest = URLRequest(url: url!)
web.load(myRequest)
web.evaluateJavaScript("document.getElementByClassName('excerpt').innerText") {(result, error) in
if error != nil {
print(String(describing:result))
}
}
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
}
You are partially right. The website won't finish loading by the time you call evaluateJavascript. But there are also other errors:
You should set the navigationDelegate instead of uiDelegate
Due to App Transport Security, URL requests to http addresses are disallowed. Use https if the website supports it or configure your app appropriately.
You misspelled the function name (missing the the s after getElement)
getElementsByClassName returns an array so you have to pick what element you want to find the innerText of
Try this:
override func viewDidLoad() {
super.viewDidLoad()
web = WKWebView(frame: .zero)
web.navigationDelegate = self
let url = URL(string: "https://www.appcoda.com")
let myRequest = URLRequest(url: url!)
web.load(myRequest)
}
func webView(_ webView: WKWebView, didFinish navigation: WKNavigation!) {
web.evaluateJavaScript("document.getElementsByClassName('excerpt')[0].innerText") {(result, error) in
guard error == nil {
print(error!)
return
}
print(String(describing: result))
}
}

userContentController never called back from JS injection

I've got a WBWebview running into a NSPopover.
I've followed this guide in order to be able to send data back to my Swift application from JS.
Unfortunately, userContentController is never called in the Swift app.
Here's my Swift app View controller code:
class GSViewController: NSViewController, WKScriptMessageHandler, WKNavigationDelegate {
var webView: WKWebView?
var webConfig:WKWebViewConfiguration {
get {
// Create WKWebViewConfiguration instance
let webCfg:WKWebViewConfiguration = WKWebViewConfiguration()
// Setup WKUserContentController instance for injecting user script
let userController:WKUserContentController = WKUserContentController()
// Add a script message handler for receiving "buttonClicked" event notifications posted from the JS document using window.webkit.messageHandlers.buttonClicked.postMessage script message
userController.addScriptMessageHandler(self, name: "buttonClicked")
// Get script that's to be injected into the document
let js:String = buttonClickEventTriggeredScriptToAddToDocument()
// Specify when and where and what user script needs to be injected into the web document
let userScript:WKUserScript = WKUserScript(source: js, injectionTime: WKUserScriptInjectionTime.AtDocumentEnd, forMainFrameOnly: false)
// Add the user script to the WKUserContentController instance
userController.addUserScript(userScript)
// Configure the WKWebViewConfiguration instance with the WKUserContentController
webCfg.userContentController = userController;
return webCfg;
}
}
override func loadView() {
super.loadView()
}
override func viewDidLoad() {
super.viewDidLoad()
self.webView = WKWebView(frame: self.view.frame, configuration: webConfig)
self.webView!.navigationDelegate = self
self.view = webView!
let username = NSUserName()
let url = NSURL(string:"someUrl")
let req = NSURLRequest(URL:url!)
self.webView!.loadRequest(req)
self.view.frame = CGRectMake(0, 0, 440, 600)
}
func buttonClickEventTriggeredScriptToAddToDocument() ->String{
let script:String = "webkit.messageHandlers.callbackHandler.postMessage('Does it works?');"
return script;
}
// WKNavigationDelegate
func webView(webView: WKWebView, didFinishNavigation navigation: WKNavigation!) {
NSLog("%s", #function)
}
func webView(webView: WKWebView, didFailNavigation navigation: WKNavigation!, withError error: NSError) {
NSLog("%s. With Error %#", #function, error)
showAlertWithMessage("Failed to load file with error \(error.localizedDescription)!")
}
func userContentController(userContentController: WKUserContentController, didReceiveScriptMessage message: WKScriptMessage) {
print("called")
if(message.name == "callbackHandler") {
print("It does ! \(message.body)")
}
}
// Helper
func showAlertWithMessage(message:String) {
let myPopup:NSAlert = NSAlert()
myPopup.messageText = message
myPopup.alertStyle = NSAlertStyle.WarningAlertStyle
myPopup.addButtonWithTitle("OK")
myPopup.runModal()
}
}
I'm trying to inject directly the call to Swift callback webkit.messageHandlers.callbackHandler.postMessage('Does it works?');, but it seems not to work.
With different test, I've discovered that the injection actually works in the Swift -> JS way (I've been able to modify CSS of visible HTML elements via injection of JQuery code), but the JS -> Swift bridge just doesn't seem to make it.
Does anyone have an idea why?
I'm under OSX 10.11.6, running XCode 7.3.6.
I'm really new into Swift and OSX programming in general, so doesn't hesitate to point out any element I'm missing. I'm voluntary omitting the JS my page uses, since I'm not using any of it in the example.
Thanks.
Found it, was totally my bad.
I forgot to rename the message handler when I added my script to the webview configuration.
userController.addScriptMessageHandler(self, name: "callbackHandler") // was originally "ButtonClicked"

Categories