Notificaciones a un webview con JavaScript

¡Hola a todos!

¡Espero que hayáis vuelto a la rutina con fuerza! ¿Qué propósitos os habéis puesto para este año?

Nosotros dar más guerra que nunca y ofreceros muchos más post.

¡Espero que éste os guste! ¡Allá vamos!

Definición del caso a contemplar:

Un caso muy común que nos encontramos cuando utilizamos un webview dentro de una app es la comunicación de la web que se muestra con el componente que la contiene, ya que se suele usar para un proceso específico y se debe saber cuándo éste acaba.

Para identificar el fin de este proceso se pueden utilizar dos métodos:

  1. Capturar la URL que nos indica el final
  2. Mandar una notificación a través de Javascript que es capturada por el webview

Cómo capturar una url: 

Este caso es el más sencillo.

Lo primero que hay que hacer es crear un control que herede de Xamarin.Forms.WebView, y en el constructor subscribirse al evento de navegación. Cada vez que el webview navegue pasará por ese evento y podremos comprobar que la URL es la que estamos esperando para verificar que estamos en el final del proceso.

Navigating += async (object sender, WebNavigatingEventArgs e) =>
{
var url = e.Url;
// verificamos la url y si es la esperado r ealizamos la lógica necesaria

}
};

En este caso la implementación del renderer por plataforma no afecta, por lo que no entro en detalle de cómo se implementaría.

Notificación por Javascript

Otra opción es que a través de Javascript se lance una notificación, y ésta sea recibida en el webview. Esta notificación se envía de manera distinta dependiendo de cada plataforma, y se puede mandar un dato simple. Un ejemplo sería:

function notify(notification){

var notificationString = notification;

try {

//Windows

window.external.notify(notificationString);

}

catch (e) { }

 

try {

//Android

Android.notifySSO(notificationString);

}

catch (e) { }

 

try {

//IOS

window.webkit.messageHandlers.observe.postMessage(notificationString);

}

catch (e) { }

}

 

Al igual que el caso anterior, se debe crear un control que herede de Xamarin.Forms.WebView. Este control no tiene nada de especial, la configuración para recibir las notificaciones se debe hacer en el renderer de cada plataforma.

Plataforma iOS

Nuestro renderer tiene que heredar de ViewRenderer<OauthWebView,WKWebView>, e implementar la interfaz IWKScriptMessageHandler.

En el OnElementChanged debemos crear un nuevo WKUserContentController, que debe subscribirse a través de un identificador a la notificación que se manda, que en nuestro ejemplo es «observe«.

A continuación, creamos un nuevo WKWebViewConfiguration al que le asignamos el WKUserContentController creado anteriormente.

Ahora creamos un WKWebView pasándole la configuración, le indicamos la URL y su posterior carga, y por último asignamos nuestro webview al control.

protected override void OnElementChanged(ElementChangedEventArgs<OauthWebView> e)
{
base.OnElementChanged(e);

if (Element != null)
{
var contentController = new WKUserContentController();

contentController.AddScriptMessageHandler(this, «observe»);

var config = new WKWebViewConfiguration();

config.UserContentController = contentController;

var webView = new WKWebView(this.NativeView.Frame, config);<<

OauthWebView oautWebView = (OauthWebView)Element;

var request = new NSMutableUrlRequest();

request.Url = new NSUrl(“url”);
string url = request.Url.ToString();

webView.LoadRequest(request);

SetNativeControl(webView);
}
}

Ya solo nos falta implementar la interfaz, que será por donde nos llegan las notificaciones.

En nuestro caso solo nos hemos subscrito a una notificación, pero si nos subscribimos a varias, el segundo parámetro de la función que implementa la interfaz tiene la propiedad «Name«, para identificar qué notificación es la que ha llegado. Ese mismo parámetro también tiene la propiedad «Body» para obtener el dato que se envía.

public void DidReceiveScriptMessage(WKUserContentController userContentController, WKScriptMessage message)
{
var msg = message.Body;
var name = message.Name;
}

 Plataforma Android

En Android el renderer tiene que heredar de Xamarin.Forms.Platform.Android.WebViewRenderer. También tenemos que crear una clase que herede de Java.Lang.Object e implementar una función que se llame igual que la notificación que se manda, en nuestro será «notifySSO» y como parámetro de entrada tiene un string que será el dato que se envía.

public class JSInterface : Java.Lang.Object
{

Context _cntx;
OauthWebView _webView;
public JSInterface() { }
public JSInterface()
{
}
[Export]
[JavascriptInterface]
public void notifySSO(string name)
{
//name contiene el dato enviado
}
}

Para que se llame a la función de la clase creada hay que configurar el webview en el OnElementChanged del renderer.

Primero hay que habilitar el JavaScript, y luego hay que asignar la clase creada llamando a la función AddJavascriptInterface.

Ya solo queda lanzar la URL y esperar la notificación: 

protected override void OnElementChanged(ElementChangedEventArgs<Xamarin.Forms.WebView> e)
{
base.OnElementChanged(e);

if (Element != null)
{
Control.Settings.JavaScriptEnabled = true;
Control.AddJavascriptInterface(new JSInterface(), «Android»);

Control.PostUrl(“url”);
}
}

Plataforma UWP

En UWP el renderer hereda de Xamarin.Forms.Platform.UWP.WebViewRenderer.

Aquí solo hay que subscribirse a «ScriptNotify» dentro de OnElementChanged. La función que se utiliza para la subscripción tiene que tener como primer parámetro un «object» y como segundo un «NotifyEventArgs«, el cual tiene la propiedad «Value«, que será el dato que se envía.

private async void ControlOnScriptNotify(object sender, NotifyEventArgs notifyEventArgs)
{
var v = notifyEventArgs.Value;

NOTA: Para que pase por la subscripción es importante que el parámetro que se le pasa cuando se envía la notificación sea un string.

Gracias por leer.

¡Nos vemos!