En resumen: Registra eventos in-app a partir de vistas web integradas en tu aplicación móvil, lo que permite la medición completa de eventos incluso cuando la interacción ocurre dentro del contenido web de la aplicación.
Introducción
Los eventos in-app que ocurren dentro de la aplicación se reportan mediante el SDK. ¿Qué pasa con los eventos que ocurren fuera de la aplicación?
Hay algunos escenarios en los que ocurren eventos fuera del contexto de la aplicación:
- Eventos que tienen lugar en tu sitio web: Implementa la Atribución people-based (PBA) para obtener una vista unificada de los viajes de los clientes a través de canales, plataformas, incluido el sitio web y dispositivos. Esto te ofrece web-to-app, análisis de rutas de conversión y raw data.
- Servidores backend: Los eventos ocurren independientemente de la acción del usuario en el sitio web o la aplicación. Por ejemplo, renovación automática de suscripción
- Híbrido: El evento se lleva a cabo en tu sitio web móvil en un dispositivo que tiene la aplicación como se describe en este artículo. Los eventos in-app se atribuyen llamando a la API del SDK de AppsFlyer usando JavaScript.
Esta guía se refiere al escenario híbrido. Aprende a cerrar la brecha entre la vista HTML y las vistas nativas, lo que te permite atribuir eventos en la vista HTML y enviarlos a la aplicación.
Ejemplo
Tienes una aplicación híbrida que ofrece suscripciones. Implementa el formulario de suscripción en una vista web que realmente se carga desde tu sitio web.
Puedes atribuir el evento in-app de suscripción en la vista web y enviar datos relacionados con él, como el tipo de suscripción o el precio, al código nativo.
El código nativo recopila los datos y utiliza el SDK de AppsFlyer para enviar el evento in-app de suscripción.
Registro de eventos in-app en aplicaciones híbridas
En esta guía proporcionamos dos métodos para registrar eventos in-app en aplicaciones híbridas:
- [Recomendado] Interfaz de JavaScript: Utiliza la interfaz nativa de JavaScript para establecer la comunicación entre la vista HTML o web y el código nativo. De esta manera, puedes enviar datos relacionados con eventos in-app desde la vista web al código nativo. Una vez que el código nativo obtiene los datos, los envía a AppsFlyer usando el SDK.
- Carga de URL: En este método, el código nativo escucha los eventos de carga de URL. Puedes establecer el código nativo para escuchar los eventos de carga de URL específicas y extraer datos de los parámetros de URL. Los datos se transfieren al SDK.
Interfaz de JavaScript
Tanto Android como iOS tienen interfaces nativas de Javascript que permiten que las vistas web llamen al código nativo.
- Android: JavaScriptInterface
- iOS: JavaScriptCore
Ventajas de usar la interfaz nativa de JavaScript vs la carga de URL:
- No requiere la implementación de lógica que detecte la carga de URL.
- Las interfaces nativas son preferibles a otras implementaciones.
- No es necesario analizar los parámetros de la URL.
- Hay menos código y, por lo tanto, se requiere menos mantenimiento.
La implementación consta de los siguientes pasos:
- Código HTML para vista web o página web
- Implementación de código nativo para vista web
Android
Código HTML para Android
Agrega el siguiente código HTML a la vista web o página web:
<h1>Recording Event From Web View</h1>
<div id="main">
<button id="recordEvent" onclick="recordEvent()"> Record Event </button>
</div>
<script type="text/JavaScript">
<!-- Set Customer User ID -->
function setCustomerUserId(){
app.setCustomerUserId(customerUserId)
}
function recordEvent(){
var eventName = "af_purchase"
var eventParams = "{\"af_revenue\":\"6.72\", \"af_content_type\": \"wallets\", \"af_content_id\": \"15854\"}";
app.recordEvent(eventName, eventParams)
}
</script>
Cuando pases el valor del evento, ten presente lo siguiente:
-
Asegúrate de pasar el valor del evento como un JSON en formato de secuencia.
"{\"af_revenue\":\"6.72\", \"af_content_type\": \"wallets\", \"af_content_id\": \"15854\"}" - Para evitar cualquier posible problema, AppsFlyer recomienda usar sólo caracteres alfanuméricos en minúscula (a-z y 0-9) dentro de la aplicación para los nombres de los eventos.
Clase WebActivity
Crea una clase de actividad web con el siguiente código:
public class WebActivity extends AppCompatActivity {
WebView web;
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_web);
web = (WebView) findViewById(R.id.webView);
web.getSettings().setJavaScriptEnabled(true);
web.addJavaScriptInterface(new MainJsInterface(getApplicationContext()), "app");
web.loadUrl("https://yourwebsite.com");
}
}
El código anterior crea un objeto llamado app que actúa como puente entre la vista web y el código nativo.
Clase de la interfaz de JavaScript
Crea una clase MainJsInterface para implementar recordEvent()
como JavaScriptInterface
public class MainJsInterface {
Context mContext;
MainJsInterface(Context c) {
mContext = c;
}
//Set Customer User ID
@JavaScriptInterface
public void setCustomerUserId(String customerUserId){
AppsFlyerLib.getInstance().setCustomerUserId(customerUserId);
}
@JavaScriptInterface
public void recordEvent(String name, String json){
Map<String, Object> params = null;
if(json!=null) {
try {
JSONObject jsonObject = new JSONObject(json);
params = new HashMap<>();
Iterator keys = jsonObject.keys();
while (keys.hasNext()) {
String key = keys.next();
Object value = jsonObject.opt(key);
params.put(key, value);
}
} catch (JSONException e) {
e.printStackTrace();
}
}
AppsFlyerLib.getInstance().logEvent(this.mContext, name, params);
}
}
El código anterior declara un método de recordEvent que
puede
llamarse en la vista web mediante JavaScript.
En el código HTML, puedes ver que se llama al método usando
app.recordEvent(eventName, eventParams). Recuerda
que
configuramos la aplicación para que actúe como puente entre la
vista de la web
y el código nativo. Es por eso que la aplicación puede llamar
al
método recordEvent que definamos en
la clase MainJsInterface.
iOS
Código HTML para iOS
Agrega el siguiente código HTML a la vista web o página web:
<body>
<h1>Recroding Event From Web View</h1>
<div id="main">
<button id="recordEvent" onclick="recordEvent()"> Record Event </button>
</div>
<script type="text/JavaScript">
<!-- Set Customer User ID -->
function setCustomerUserId(){
customerUserIdwebkit.messageHandlers.cuid.postMessage(customerUserId);
}
function recordEvent(){
var eventName = "af_purchase"
var eventParams = "{\"af_revenue\":\"6.72\", \"af_content_type\": \"wallets\", \"af_content_id\": \"15854\"}";
webkit.messageHandlers.event.postMessage(eventName + "+" + eventParams);
}
</script>
</body>
El código anterior establece una función que se activará cuando
se haga clic en un botón.
La función establece las variables eventName y
eventParams y las pasa al archivo
de código nativo usando webkit.messageHandlers.
Cuando pases el valor del evento, ten presente lo siguiente:
-
Asegúrate de pasar el valor del evento como un JSON en formato de secuencia.
"{\"af_revenue\":\"6.72\", \"af_content_type\": \"wallets\", \"af_content_id\": \"15854\"}" - Para evitar cualquier posible problema, AppsFlyer recomienda usar sólo caracteres alfanuméricos en minúscula (a-z y 0-9) dentro de la aplicación para los nombres de los eventos.
Nota
El código HTML es tanto para Objective C como para Swift.
Objective C
Controlador de vista web
En tu controlador de vista web, agrega el siguiente código en el controlador de vista.
-(void)loadView{
[super loadView];
WKWebViewConfiguration *configuration =
[[WKWebViewConfiguration alloc] init];
[configuration.userContentController
addScriptMessageHandler:self name:@"event"];
_webView = [[WKWebView alloc] initWithFrame:self.view.frame
configuration:configuration];
[self.view addSubview:_webView];
}
- (void)viewDidLoad {
[super viewDidLoad];
NSString* page = @"https://yourwebsite.com";
NSURL *url = [NSURL URLWithString:page];
NSURLRequest *request = [NSURLRequest requestWithURL:url];
[_webView loadRequest:request];
}
- (void)userContentController:(WKUserContentController *)userContentController
didReceiveScriptMessage:(WKScriptMessage *)message {
NSString* messageBody = message.body;
NSString* eventName = [messageBody componentsSeparatedByString:@"+"][0];
NSString* eventValue = [messageBody componentsSeparatedByString:@"+"][1];
[self recordEvent:eventName eventValue:eventValue];
}
- (void)recordEvent:(NSString*)eventName eventValue:(NSString*)eventValue{
NSData *eventValuedata = [eventValue dataUsingEncoding:(NSUTF8StringEncoding)];
NSDictionary *eventValueDict = [NSJSONSerialization JSONObjectWithData:eventValuedata options:NSJSONReadingMutableContainers error:nil];
[[AppsFlyerLib shared] logEvent:eventName withValues:eventValueDict];
}
El código anterior recibe el mensaje de la vista web. El mensaje
contiene el nombre y el valor del evento. Cuando el código detecta
al
mensaje entrante, lo pasa al método recordEvent
que analiza los valores y llama al método nativo
logEvent del SDK.
Controlador Swift WebView
En el controlador de vista web, agrega el siguiente código:
import WebKit
import UIKit
import AppsFlyerLib
import JavaScriptCore
class JSViewController: UIViewController, WKNavigationDelegate, WKScriptMessageHandler {
func userContentController(_ userContentController: WKUserContentController, didReceive message: WKScriptMessage) {
if message.name == "event" {
let messageBody = message.body as? String
let eventName = messageBody?.components(separatedBy: "+")[0]
let eventValue = messageBody?.components(separatedBy: "+")[1]
recordEvent(eventName: eventName!, eventValue: eventValue!)
} else if message.name == "cuid" {
AppsFlyerLib.shared().customerUserID = message.body as? String
}
}
var webView: WKWebView!
override func loadView(){
webView = WKWebView()
webView.navigationDelegate = self
view = webView
}
override func viewDidLoad() {
super.viewDidLoad()
let url = URL(string: "https://yourwebsite.com")!
webView.load(URLRequest(url: url))
webView.configuration.userContentController.add(self, name:"event")
}
func recordEvent(eventName: String, eventValue: String) {
var eventValueDict = [String: String]()
let eventValuesData = eventValue.data(using: String.Encoding.utf8)
do {
eventValueDict = try (JSONSerialization.jsonObject(with: eventValuesData!, options:[]) as? [String: String])!
} catch let error as NSError{
print(error)
}
AppsFlyerLib.shared().logEvent(eventName as String?, withValues: eventValueDict)
}
}
El código anterior recibe el mensaje de la vista web. El mensaje
contiene el nombre y el valor del evento. Cuando el código detecta
al
mensaje entrante, lo pasa al método recordEvent
que analiza los valores y llama al método
logEvent del SDK de AppsFlyer.
Carga de URL
Tanto Android como iOS implementan un método nativo que escucha los eventos de carga de URL. Este método te permite hacer lo siguiente:
- Escuchar un evento de carga de una URL específica
- Extraer parámetros que se anexan a la URL
- Usar estos parámetros en el código nativo
La implementación a continuación consta de dos pasos:
- Código HTML para vista web o página web
- Implementación de código nativo para la carga de URL
Implementación de WebView
Código HTML (tanto para Android WebView como para iOS WKWebView)
<html>
<head>
</head>
<body>
<h1>Recording From Web View</h1>
<div id="main">
<input id="button" type="button" value="Record Event" />
</div>
<script type="text/JavaScript">
function recordEvent(eventName,eventValue){
var iframe = document.createElement("IFRAME");
iframe.setAttribute("src", "af-event://inappevent?eventName="+eventName+"&eventValue="+eventValue);
document.documentElement.appendChild(iframe);
iframe.parentNode.removeChild(iframe);
iframe = null;
}
var button = document.getElementById("button");
button.onclick = function(event) {
var eventName = "af_purchase";
var eventValue = "{\"af_revenue\":\"6.72\", \"af_content_type\": \"wallets\", \"af_content_id\": \"15854\"}";
recordEvent(eventName, eventValue);
}
</script>
</body>
</html>El código anterior puede ser parte de una página web real o cargada de forma nativa en la vista web.
El código anterior establece una función que se activará cuando se haga clic en un botón. La función en sí crea un elemento iframe que carga una URL personalizada. Es esta carga de URL la que activa el método nativo de Android o iOS que luego llama al método logEvent.
Todos los parámetros requeridos (nombre del evento y valores del evento) se agregan a la URL. Los ejemplos de código de Android e iOS a continuación demuestran cómo extraer estos parámetros y pasarlos al método logEvent del SDK de AppsFlyer.
Cuando pases el valor del evento, ten presente lo siguiente:
- Asegúrate de pasar el valor del evento como un JSON en formato de secuencia.
"{\"af_revenue\":\"6.72\", \"af_content_type\": \"wallets\", \"af_content_id\": \"15854\"}" - Para evitar posibles problemas, AppsFlyer recomienda usar únicamente caracteres alfanuméricos en minúsculas (a-z y 0-9) para tus nombres de eventos in-app.
Implementación nativa
Implementación de Android
Android WebView utiliza un WebViewClient que recibe notificaciones de la vista web. En el shouldOverrideUrlLoading, puedes manejar los eventos de carga de URL que provienen de la vista web:
@Override
public boolean shouldOverrideUrlLoading(WebView view, String url) {
if (url.startsWith("af-event://")) {
String[] urlParts = url.split("\\?");
if (urlParts.length > 1) {
String query = urlParts[1];
String eventName = null;
HashMap<String, Object> eventValue = new HashMap<>();
for (String param : query.split("&")) {
String[] pair = param.split("=");
String key = pair[0];
if (pair.length > 1) {
if ("eventName".equals(key)){
eventName = pair[1];
} else if ("eventValue".equals(key)){
JSONObject event;
JSONArray keys;
try {
event = new JSONObject(pair[1]);
keys = event.names();
for (int i = 0; i < keys.length(); i++){
eventValue.put(keys.getString(i), event.getString(keys.getString(i)));
}
} catch (JSONException e) {
e.printStackTrace();
}
}
}
}
AppsFlyerLib.getInstance().logEvent(getApplicationContext(),eventName,eventValue);
}
return true;
}
view.loadUrl(url);
return true;
}
Implementación de iOS
En el controlador de vista web, agrega el siguiente código en el método shouldStartLoadWithRequest.
- (void)viewDidLoad {
[super viewDidLoad];
NSString *page = @"https://yourwebsite.com";
NSURL *url = [NSURL URLWithString:page];
NSURLRequest *request = [NSURLRequest requestWithURL:url];
_webView = [[WKWebView alloc] initWithFrame:self.view.frame];
_webView.navigationDelegate = self;
[_webView loadRequest:request];
_webView.frame = CGRectMake(self.view.frame.origin.x,self.view.frame.origin.y, self.view.frame.size.width, self.view.frame.size.height);
[self.view addSubview:_webView];
}
-(void)webView:(WKWebView *)webView decidePolicyForNavigationAction:(WKNavigationAction* )navigationAction decisionHandler:(void (^)(WKNavigationActionPolicy))decisionHandler {
decisionHandler(WKNavigationActionPolicyAllow);
NSString *url = navigationAction.request.URL.absoluteString;
if([url containsString:(@"af-event")]){
[self recordEvent:(url)];
}
}
- (void)recordEvent:(NSString*)url{
NSString *eventNameAndEventValueString = [url componentsSeparatedByString: @"?"][1];
NSArray *eventNameAndEventValueArray = [eventNameAndEventValueString componentsSeparatedByString: @"&"];
NSString *eventName = [eventNameAndEventValueArray[0] componentsSeparatedByString: @"="][1];
NSString *eventValueParams = [eventNameAndEventValueArray[1] componentsSeparatedByString: @"="][1];
NSString *decodedValueParams = [eventValueParams stringByRemovingPercentEncoding];
NSData *eventValuedata = [decodedValueParams dataUsingEncoding:(NSUTF8StringEncoding)];
NSDictionary *eventValue = [NSJSONSerialization JSONObjectWithData:eventValuedata options:NSJSONReadingMutableContainers error:nil];
if (eventName != nil){
[[AppsFlyerLib shared] logEvent:eventName withValues:eventValue];
}
}
El código anterior detecta cualquier carga de URL. Si se carga una URL con "af-event", activa el método nativo recordEvent. El método nativo recordEvent extrae los parámetros necesarios (eventName y eventValue) y los pasa al método nativo logEvent de SDK.
En el controlador de vista web, agrega el siguiente código en el método webView(_:decidePolicyFor:decisionHandler:).
import WebKit
import UIKit
import AppsFlyerLib
class WebViewController: UIViewController, WKNavigationDelegate {
var webView: WKWebView!
override func loadView(){
webView = WKWebView()
webView.navigationDelegate = self
view = webView
}
override func viewDidLoad() {
super.viewDidLoad()
let url = URL(string: "https://yourwebsite.com")!
webView.load(URLRequest(url: url))
}
func webView(_ view: WKWebView, decidePolicyFor navigationAction: WKNavigationAction, decisionHandler: ((WKNavigationActionPolicy) -> Void)) {
decisionHandler(.allow)
let pageUrl = navigationAction.request.url?.absoluteString
if(pageUrl?.hasPrefix("af-event://") ?? false){
let scanner = Scanner(string: pageUrl!)
scanner.charactersToBeSkipped = CharacterSet(charactersIn: "&?")
scanner.scanUpTo("?", into: nil)
var tempString, eventName : NSString?
var eventValue: [String: String] = Dictionary()
while(scanner.scanUpTo("&", into: &tempString)){
if let tempString = tempString {
if(tempString.hasPrefix("eventName=") && tempString.length > 10){
eventName = tempString.substring(from: 10) as NSString
}
if(tempString.hasPrefix("eventValue=") && tempString.length > 11){
let eventValues = tempString.components(separatedBy: "=")[1].removingPercentEncoding
let eventValuesData = eventValues?.data(using: String.Encoding.utf8)
do {
eventValue = try (JSONSerialization.jsonObject(with: eventValuesData!, options:[]) as? [String: String])!
} catch let error as NSError{
print(error)
}
}
}
}
if(eventName != nil){
if let eventName = eventName {
// record event
AppsFlyerLib.shared().logEvent(eventName as String?, withValues: eventValue)
}
}
}
}
}
El código anterior detecta cualquier carga de URL. Si se carga una URL con "af-event", extrae los parámetros necesarios (eventName y eventValue) y los pasa al método nativo logEvent del SDK.