Внутренние события гибридных приложений

Краткий обзор. Регистрация внутренних событий по пользователям, которые установили приложение, но совершили событие не в нем, а на мобильном сайте.

Введение

События в приложении, произошедшие внутри приложения, сообщаются с помощью SDK. Как быть с событиями, происходящими за пределами приложения?

События происходят вне контекста приложения в следующих сценариях:

  • События, происходящие на вашем сайте: Внедрите атрибуцию People-Based (PBA), чтобы получить единое представление о пути клиентов по каналам, платформам, включая веб-сайт, и устройствам. Это позволит вам анализировать переходы с сайта в приложение, пути конверсии и сырые данные. 
  • Внутренние серверы: События происходят независимо от действий пользователя на сайте или в приложении. Например, автоматическое продление подписки.
  • Гибридные:  Событие происходит на вашем мобильном сайте на устройстве с приложением, как описано в этой статье. Внутренние события регистрируются путем вызова API SDK AppsFlyer с использованием JavaScript.

В этом руководстве рассматривается гибридный сценарий. Узнайте, как преодолеть разрыв между HTML-представлением и нативными представлениями, чтобы регистрировать события в HTML-представлении и отправлять их в приложение.  

 Пример

У вас есть гибридное приложение, которое предлагает подписку. Вы реализуете форму подписки в веб-представлении, которое загружается с вашего сайта.

Вы можете зарегистрировать внутреннее событие подписки в веб-представлении и отправить связанные с ним данные, такие как тип или стоимость подписки, в нативный код.

Нативный код собирает эти данные и использует SDK AppsFlyer для отправки внутреннего события подписки.

Запись внутренних событий в гибридных приложениях

В этом руководстве мы предлагаем два метода записи событий в гибридных приложениях:

  • [Рекомендуется] Интерфейс JavaScript: Использует собственный интерфейс JavaScript для установления связи между HTML или веб-представлением и собственным кодом. Такм образом, можно отправлять данные, связанные с внутренними событиями, из веб-представления в нативный код. Получив данные, нативный код с помощью SDK отправляет их в AppsFlyer.
  • Загрузка URL: Нативный код отслеживает события загрузки URL-адреса.  Вы можете настроить отслеживание событий загрузки определенных URL-адресов и извлечение данных из параметров URL. Затем данные передаются в SDK.

Интерфейс JavaScript

И в Android, и в iOS есть нативные интерфейсы Javascript, которые позволяют веб-представлениям вызывать нативный код.

Преимущества использования нативного интерфейса JavaScript перед загрузкой URL-адреса:

  • Не требуется реализация логики, отслеживающей загрузку URL-адреса.
  • Нативные интерфейсы предпочтительнее других реализаций.
  • Не требуется парсинг параметров из URL-адреса.
  • Кода меньше, а значит его проще поддерживать.

Реализация состоит из следующих шагов:

  1. HTML-код для веб-представления или веб-страницы.
  2. Реализация нативного кода для веб-представления

Android

HTML-код для Android

Добавьте следующий HTML-код в веб-представление или на веб-страницу:

<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>

Передавая значение события, помните о следующем:

  • Обязательно передавайте значение события в виде строкового JSON.
    "{\"af_revenue\":\"6.72\", \"af_content_type\": \"wallets\", \"af_content_id\": \"15854\"}"
  • Во избежание возможных проблем AppsFlyer рекомендует для имен внутренних событий приложений использовать только строчные буквенно-цифровые символы (a-z и 0-9).

Класс WebActivity

Создайте класс веб-активности со следующим кодом:

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");
 }
}

Приведенный выше код создает объект под названием app, который служит мостом между веб-представлением и нативным кодом.

Класс интерфейса JavaScript

Создайте класс MainJsInterface, чтобы реализовать recordEvent() как 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);
 }
}

В приведенном выше коде объявлен метод recordEvent, который может быть вызван в веб-представлении с помощью Javascript.

В HTML-коде видно, что метод вызывается с помощью app.recordEvent(eventName, eventParams). Напомним, что настроили приложение как мост между веб-представлением и нативным кодом. Поэтому app может вызвать метод recordEvent, который мы определили в классе MainJsInterface.

iOS

HTML-код для iOS

Добавьте следующий HTML-код в веб-представление или на веб-страницу:

<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>

Приведенный выше код задает функцию, которая будет срабатывать при нажатии на кнопку. Функция устанавливает переменные eventName и eventParams и передает их нативному коду с помощью webkit.messageHandlers.

Передавая значение события, помните о следующем:

  • Обязательно передавайте значение события в виде строкового JSON.
    "{\"af_revenue\":\"6.72\", \"af_content_type\": \"wallets\", \"af_content_id\": \"15854\"}"
  • Во избежание возможных проблем AppsFlyer рекомендует для имен внутренних событий приложений использовать только строчные буквенно-цифровые символы (a-z и 0-9).

 Примечание

HTML-код подходит и для Objective C, и для Swift.

Objective c

Контроллер веб-представления

Добавьте в контроллер веб-представления следующий код.

-(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];

}

Приведенный выше код получает сообщение из веб-представления. Сообщение содержит имя и значение события. Когда код обнаруживает входящее сообщение, он передает его методу recordEvent, который разбирает значения и вызывает собственный метод SDK logEvent.

Контроллер Swift WebView

В контроллере веб-представления добавьте следующий код:

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)
 }

}

Приведенный выше код получает сообщение из веб-представления. Сообщение содержит имя и значение события. Когда код обнаруживает входящее сообщение, он передает его методу recordEvent, который разбирает значения и вызывает метод AppsFlyer SDK logEvent.

Загрузка URL

И в Android, и в iOS реализован нативный метод, который прослушивает события загрузки url. Этот метод позволяет сделать следующее:

  • Отслеживать событие загрузки конкретного URL-адреса.
  • Извлекать параметры, добавленные в URL-адрес.
  • Используйте эти параметры в нативном коде

Приведенная ниже реализация состоит из двух шагов:

  1. HTML-код для веб-представления или веб-страницы.
  2. Реализация нативного кода для загрузки URL

Реализация веб-представления (WebView)

HTML-код (для Android WebView и 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>

Приведенный выше код может находиться на реальной веб-странице или загружаться нативно в веб-представление.

В коде задана функция, вызываемая при нажатии кнопки. Сама функция создает элемент iframe, который загружает настраиваемый URL-адрес. Именно загрузка этого URL запускает нативный метод Android или iOS, который в свою очередь вызывает метод logEvent SDK AppsFlyer.

Все обязательные параметры (имя и значения события) добавляются в URL-адрес. В следующих примерах кода для Android и iOS показано, как извлечь и передать эти параметры в метод logEvent SDK AppsFlyer.

Передавая значение события, помните о следующем:

  • Обязательно передавайте значение события в виде строкового JSON.
    "{\"af_revenue\":\"6.72\", \"af_content_type\": \"wallets\", \"af_content_id\": \"15854\"}"
  • Во избежание возможных проблем AppsFlyer рекомендует для имен внутренних событий приложений использовать только строчные буквенно-цифровые символы (a-z и 0-9).

Нативная реализация

Android iOS

Реализация Android

Android WebView использует WebViewClient, который получает уведомления от веб-представления. В shouldOverrideUrlLoading вы можете обрабатывать события загрузки URL, которые приходят из веб-представления:

@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;

}