Краткий обзор. Регистрация внутренних событий по пользователям, которые установили приложение, но совершили событие не в нем, а на мобильном сайте.
Введение
События в приложении, произошедшие внутри приложения, сообщаются с помощью 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, которые позволяют веб-представлениям вызывать нативный код.
- Android: JavascriptInterface
- iOS: JavaScriptCore
Преимущества использования нативного интерфейса JavaScript перед загрузкой URL-адреса:
- Не требуется реализация логики, отслеживающей загрузку URL-адреса.
- Нативные интерфейсы предпочтительнее других реализаций.
- Не требуется парсинг параметров из URL-адреса.
- Кода меньше, а значит его проще поддерживать.
Реализация состоит из следующих шагов:
- HTML-код для веб-представления или веб-страницы.
- Реализация нативного кода для веб-представления
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-адрес.
- Используйте эти параметры в нативном коде
Приведенная ниже реализация состоит из двух шагов:
- HTML-код для веб-представления или веб-страницы.
- Реализация нативного кода для загрузки 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
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;
}
Реализация iOS
В контроллере веб-представления добавьте следующий код в метод 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];
}
}
Приведенный выше код отслеживает загрузку любого URL-адреса. Если загружается URL-адрес с параметром "af-event", он вызывает нативный метод recordEvent
. Нативный метод recordEvent
извлекает необходимые параметры (eventName и eventValue) и передает их в метод SDK logEvent
.
В контроллере веб-представления добавьте следующий код в метод 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)
}
}
}
}
}
Приведенный выше код отслеживает загрузку любого URL-адреса. Если загружается URL-адрес с параметром "af-event", он извлекает необходимые параметры (eventName и eventValue) и передает их в нативный метод SDK logEvent
.