De un vistazo: Atribuye los eventos in-app de los usuarios que tienen tu aplicación instalada, pero realiza un evento en tu sitio web móvil, y no en 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 basada en personas (PBA) para obtener una vista unificada de las trayectorias del cliente en todos los canales, plataformas, incluidos el sitio web y los dispositivos. Esto te proporciona análisis de rutas de conversión de la web a la aplicació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 JavaScript: utiliza la interfaz JavaScript nativa 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 JavaScript
Tanto Android como iOS tienen interfaces Javascript nativas que permiten que las vistas web llamen a código nativo.
- Android: JavascriptInterface
- iOS: JavaScriptCore
Ventajas de usar la interfaz JavaScript nativa frente a la carga de URL:
- No requiere la implementación de lógica que escuche 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">
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 posibles problemas, AppsFlyer recomienda usar únicamente caracteres alfanuméricos en minúsculas (a-z y 0-9) para tus nombres de eventos in-app.
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 interfaz JavaScript
Crea una clase MainJsInterface para implementar recordEvent()
como JavascriptInterface
public class MainJsInterface {
Context mContext;
MainJsInterface(Context c) {
mContext = c;
}
@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 recordEvent
al que se puede llamar en la vista web con Javascript.
En el código HTML, puedes ver que el método se llama utilizando app.recordEvent(eventName, eventParams)
. Recuerda que hemos configurado la aplicación para que actúe como puente entre la vista web y el código nativo. Esta es la razón por la que la aplicación puede llamar al recordEvent
que definimos 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">
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 configura las variables eventName
y eventParams
y las pasa al 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 posibles problemas, AppsFlyer recomienda usar únicamente caracteres alfanuméricos en minúsculas (a-z y 0-9) para tus nombres de eventos in-app.
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 el mensaje entrante, lo pasa al método recordEvent
que analiza los valores y llama al método logEvent
del SDK nativo.
Controlador Swift WebView
En el controlador de vista web, agrega el siguiente código en el método viewDidLoad
.
import WebKit
import UIKit
import AppsFlyerLib
import JavaScriptCore
class JSViewController: UIViewController, WKNavigationDelegate, WKScriptMessageHandler {
func userContentController(_ userContentController: WKUserContentController, didReceive message: WKScriptMessage) {
let messageBody = message.body as? String
let eventName = messageBody?.components(separatedBy: "+")[0]
let eventValue = messageBody?.components(separatedBy: "+")[1]
recordEvent(eventName: eventName!, eventValue: eventValue!)
}
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 el mensaje entrante, lo pasa al 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 shouldOverrideUrlLoading
, puedes manejar los eventos de carga de URL que proceden 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 escucha cualquier carga de URL. Si se carga una URL con "af-event", activa el método recordEvent
nativo. El recordEvent
nativo extrae los parámetros necesarios (eventName y eventValue) y los pasa al método logEvent
nativo de SDK.
En el controlador de vista web, agrega el siguiente código en el método webView
.
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 escucha 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 logEvent
.