usar steps requisitos programar principiantes primeros pasos para first español desarrollar con comenzar codigo aprender javascript ios webkit wkwebview

steps - Llamar a la función de JavaScript desde el código nativo en WKWebView



javascript primeros pasos (6)

Usando un WKWebView en iOS 8, ¿cómo puedo ejecutar una función de JavaScript desde el lado nativo o comunicarme desde el lado nativo a JavaScript? No parece haber un método similar al stringByEvaluatingJavaScriptFromString de stringByEvaluatingJavaScriptFromString:

(Puedo usar - addScriptMessageHandler:name: en el objeto configuration.userContentController para permitir que la comunicación de JS sea nativa, pero estoy buscando la dirección opuesta).


Detalles

Xcode 9.1, Swift 4

Descripción

La secuencia de comandos se inserta en la página que se mostrará en WKWebView. Este script devolverá la URL de la página (pero puede escribir otro código JavaScript). Esto significa que el evento del script se genera en la página web, pero se manejará en nuestra función:

func userContentController(_ userContentController: WKUserContentController, didReceive message: WKScriptMessage) {...}

Solución

extension WKUserScript { class func getUrlScript(scriptName: String) -> String { return "webkit.messageHandlers./(scriptName).postMessage(document.URL)" } } extension WKWebViewConfiguration { func addScript(script: String, scriptHandlerName:String, scriptMessageHandler: WKScriptMessageHandler, injectionTime:WKUserScriptInjectionTime) { let userScript = WKUserScript(source: script, injectionTime: injectionTime, forMainFrameOnly: false) userContentController.addUserScript(userScript) userContentController.add(scriptMessageHandler, name: scriptHandlerName) } }

Uso

Init WKWebView

let config = WKWebViewConfiguration() config.addScript(script: WKUserScript.getUrlScript(scriptName: getUrlAtDocumentStartScript), scriptHandlerName:getUrlAtDocumentStartScript, scriptMessageHandler: self, injectionTime: .atDocumentStart) config.addScript(script: WKUserScript.getUrlScript(scriptName: getUrlAtDocumentEndScript), scriptHandlerName:getUrlAtDocumentEndScript, scriptMessageHandler: self, injectionTime: .atDocumentEnd) webView = WKWebView(frame: UIScreen.main.bounds, configuration: config) webView.navigationDelegate = self

Atrapar eventos

extension ViewController: WKScriptMessageHandler { func userContentController(_ userContentController: WKUserContentController, didReceive message: WKScriptMessage) { switch message.name { case getUrlAtDocumentStartScript: print("start: /(message.body)") case getUrlAtDocumentEndScript: print("end: /(message.body)") default: break; } } }

Ejemplo de código completo

import UIKit import WebKit class ViewController: UIViewController, WKNavigationDelegate { var webView = WKWebView() let getUrlAtDocumentStartScript = "GetUrlAtDocumentStart" let getUrlAtDocumentEndScript = "GetUrlAtDocumentEnd" override func viewDidLoad() { super.viewDidLoad() let config = WKWebViewConfiguration() config.addScript(script: WKUserScript.getUrlScript(scriptName: getUrlAtDocumentStartScript), scriptHandlerName:getUrlAtDocumentStartScript, scriptMessageHandler: self, injectionTime: .atDocumentStart) config.addScript(script: WKUserScript.getUrlScript(scriptName: getUrlAtDocumentEndScript), scriptHandlerName:getUrlAtDocumentEndScript, scriptMessageHandler: self, injectionTime: .atDocumentEnd) webView = WKWebView(frame: UIScreen.main.bounds, configuration: config) webView.navigationDelegate = self view.addSubview(webView) } override func viewDidAppear(_ animated: Bool) { super.viewDidAppear(animated) webView.loadUrl(string: "http://apple.com") } } extension ViewController: WKScriptMessageHandler { func userContentController(_ userContentController: WKUserContentController, didReceive message: WKScriptMessage) { switch message.name { case getUrlAtDocumentStartScript: print("start: /(message.body)") case getUrlAtDocumentEndScript: print("end: /(message.body)") default: break; } } } extension WKWebView { func loadUrl(string: String) { if let url = URL(string: string) { load(URLRequest(url: url)) } } } extension WKUserScript { class func getUrlScript(scriptName: String) -> String { return "webkit.messageHandlers./(scriptName).postMessage(document.URL)" } } extension WKWebViewConfiguration { func addScript(script: String, scriptHandlerName:String, scriptMessageHandler: WKScriptMessageHandler, injectionTime:WKUserScriptInjectionTime) { let userScript = WKUserScript(source: script, injectionTime: injectionTime, forMainFrameOnly: false) userContentController.addUserScript(userScript) userContentController.add(scriptMessageHandler, name: scriptHandlerName) } }

Info.plist

agregue su configuración de seguridad de transporte Info.plist

<key>NSAppTransportSecurity</key> <dict> <key>NSAllowsArbitraryLoads</key> <true/> </dict>

Resultado

Recursos

Document Object Properties and Methods


(Archivé un Radar para esto poco después de hacer la pregunta aquí).

Recientemente se agregó un nuevo método hace unos días (gracias jcesarmobile por señalarlo):

Agregar -[WKWebView evaluateJavaScript:completionHandler:]
trac.webkit.org/changeset/169765

El método está disponible en iOS 8 beta 3 y posteriores. Aquí está la nueva firma de método:

/* @abstract Evaluates the given JavaScript string. @param javaScriptString The JavaScript string to evaluate. @param completionHandler A block to invoke when script evaluation completes or fails. @discussion The completionHandler is passed the result of the script evaluation or an error. */ - (void)evaluateJavaScript:(NSString *)javaScriptString completionHandler:(void (^)(id, NSError *))completionHandler;

Los documentos están disponibles aquí: https://developer.apple.com/documentation/webkit/wkwebview/1415017-evaluatejavascript .


Acabo de empezar a cavar alrededor de la API WKWebView, así que esta podría no ser la mejor manera, pero creo que podrías hacerlo con el siguiente código:

NSString *scriptSource = @"console.log(''Hi this is in JavaScript'');"; WKUserScript *userScript = [[WKUserScript alloc] initWithSource:scriptSource injectionTime:WKUserScriptInjectionTimeAtDocumentStart forMainFrameOnly:YES]; [myWKController addUserScript:userScript];

(de la charla de WWDC''14)


Aquí hay algo que me funciona:

Cree una extensión en WKWebView que defina un método ''runJavaScriptInMainFrame:''. En el método de extensión, use NSInvocationOperation para llamar al método ''_runJavaScriptInMainFrame:'' no documentado.

extension WKWebView { func runJavaScriptInMainFrame(#scriptString: NSString) -> Void { let selector : Selector = "_runJavaScriptInMainFrame:" let invocation = NSInvocationOperation(target: self, selector: selector, object: scriptString) NSOperationQueue.mainQueue().addOperation(invocation) } }

Para usar, llama:

webview.runJavacriptInMainFrame:(scriptString: "some javascript code")

Gracias a Larsaronen por proporcionar el enlace a la API privada para WKWebView.


Puede que no sea un método ideal, pero dependiendo de su caso de uso, puede volver a cargar WKWebView después de haber infectado el script del usuario:

NSString *scriptSource = @"alert(''WKWebView JS Call!'')"; WKUserScript *userScript = [[WKUserScript alloc] initWithSource:scriptSource injectionTime:WKUserScriptInjectionTimeAtDocumentEnd forMainFrameOnly:YES]; [wkWebView.configuration.userContentController addUserScript:userScript]; [wkWebView reload];


Se rumorea que esto es un error porque hay una función privada similar (?) A lo que está disponible públicamente en el UIWebView para evaluar javascript desde obj-C.