はじめに
ハイブリッドアプリとは、ネイティブビューとHTMLコンテンツを組み合わせたアプリを指します。ハイブリッドアプリをお持ちの場合には、Javascriptを使用してAppsFlyerのSDK APIを呼び出すことで、アプリ内イベントを計測可能です。
このガイドでは、HTMLビューとネイティブビューのギャップを埋める方法について説明しています。これにより、HTMLまたはWebビュー内でのアプリ内イベントを効果的に計測できるようになります。
例
サブスクリプション(購読)モデルのサービスを提供するアプリを所有しているとします。 また、購読フォームは実際にWebサイトを読み込むWebビュー内に設置しています。
Webビューにおける購読のイベントを計測し、それに関連するデータ(購読タイプ、価格など)をネイティブコードに送信することが可能です。
ネイティブコードはそのデータを収集し、AppsFlyerのSDKを使用してアプリ内イベントをAppsFlyerのサーバーへ送信します。
ハイブリッドアプリでのアプリ内イベントの計測
このガイドでは、ハイブリッドアプリのアプリ内イベントを計測する方法を2つ提供します。
ネイティブ Javascript インターフェース
ネイティブ javascript インターフェース による計測が推奨です。 HTMLもしくはWebビューとネイティブコードの間の通信を確立するため、ネイティブのJavaScriptインターフェースを使用します。
これにより、Webビューからネイティブコードにアプリ内イベントのデータを送信でき、 ネイティブコードがデータを取得すると、AppsFlyerのSDKを使用してAppsFlyerへデータを送信します。
URL 読み込み メソッド
URL 読み込み メソッドでは, ネイティブコードがURLのイベントの読み込みをリスニングします。
特定のURLにおけるイベントの読み込みを監視し、URLパラメータからデータを抽出するようにネイティブコードを設定できます。その後、取得したデータはAppsFlyerのSDKに渡されます。
AndroidとiOSともに、Webビューからネイティブコードを呼び出すことを可能にするネイティブJavascriptインターフェースがあります。
- Android - JavascriptInterface
- iOS - JavaScriptCore
URL読み込みよりも、ネイティブJavascriptインターフェースを使用することにはいくつかの利点があります。
- URLの読み込みを追跡するロジックを実装する必要がありません。
- ネイティブインターフェースは他の実装よりも好ましいです。
- URLからパラメータを解析する必要がありません。
- コードが少なく、メンテナンスも少なくて済みます。
Android
HTML Code ( Android用 )
Webビューもしくは、Webページに以下のHTMLコードを追加し、適宜パラメータを取得したいイベントの内容に書き換えてください。
<h1>Tracking From Web View</h1>
<div id="main">
<button id="trackEvent" onclick="trackEvent()"> Track Event </button>
</div>
<script type="text/javascript">
function trackEvent(){
var eventName = "af_purchase"
var eventParams = "{\"af_revenue\":\"6.72\", \"af_content_type\": \"wallets\", \"af_content_id\": \"15854\"}";
app.trackEvent(eventName, eventParams)
}
</script>
Web Activity Class
以下のコードで、Web Activity Classを作成してください。
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");
}
}
上記のコードはWebビューとネイティブコードの橋渡しとして機能する app というオブジェクトを作成します。
Javascript インターフェース Class
JavascriptInterface
としてtrackEvent()
を実装するために、MainJsInterface classを作成してください。
public class MainJsInterface {
Context mContext;
MainJsInterface(Context c) {
mContext = c;
}
@JavascriptInterface
public void trackEvent(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().trackEvent(this.mContext, name, params);
}
}
上記のコードは、JavaScriptを使用してWebビュー内で呼び出すことのできるtrackEvent
method を定義します。
app.trackEvent(eventName, eventParams)
を使って呼び出されていることが分かります。 Webビューとネイティブコードの間の橋渡しとして機能するよう app を設定したことを思い出してください。これが、app がMainJsInterfaceクラスで定義した trackEvent
メソッドを呼び出すことができる理由です。iOS
HTML Code ( iOS用 )
Webビューもしくは、Webページに以下のHTMLコードを追加し、適宜パラメータを取得したいイベントの内容に書き換えてください。
<body>
<h1>Tracking From Web View</h1>
<div id="main">
<button id="trackEvent" onclick="trackEvent()"> Track Event </button>
</div>
<script type="text/javascript">
function trackEvent(){
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
を使用してネイティブコードへこれらを渡します。
注意
このHTMLコードは、Objective C とSwiftの両方で使用可能です。
Objective C
Web View Controller
Web View Controller内で、View Controllerに以下のコードを追加してください。
-(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 trackEvent:eventName eventValue:eventValue];
}
- (void)trackEvent:(NSString*)eventName eventValue:(NSString*)eventValue{
NSData *eventValuedata = [eventValue dataUsingEncoding:(NSUTF8StringEncoding)];
NSDictionary *eventValueDict = [NSJSONSerialization JSONObjectWithData:eventValuedata options:NSJSONReadingMutableContainers error:nil];
[[AppsFlyerTracker sharedTracker] trackEvent:eventName withValues:eventValueDict];
}
上記のコードはWebビューからメッセージを受け取ります。そのメッセージにはイベント名とイベント値が含まれています。 コードが受信メッセージを検出すると、メッセージの値を解析してAppsFlyer SDKの trackEvent
メソッドを呼び出す、 trackEvent
メソッドに渡します。
Swift
Web View Contoller
Web View Controller内で、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]
trackEvent(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 trackEvent(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)
}
AppsFlyerTracker.shared().trackEvent(eventName as String?, withValues: eventValueDict)
}
}
上記のコードはWebビューからメッセージを受け取ります。そのメッセージにはイベント名とイベント値が含まれています。コードが受信メッセージを検出すると、メッセージの値を解析してAppsFlyer SDKの trackEvent
メソッドを呼び出す、 trackEvent
メソッドに渡します。
AndroidとiOSで共に、URLのイベント読み込みをリスニングするネイティブメソッドを実装してください。 この方法では次のことが可能になります。
- 特定のURLにおけるイベント読み込みをリスニングする。
- URLに追加されているパラメータを抽出する。
- ネイティブコード内でこれらのパラメータを使用する。
Webviewの実装
HTML Code (Android WebView ・ iOS UIWebview 兼用)
<html>
<head>
</head>
<body>
<h1>Tracking From Web View</h1>
<div id="main">
<input id="button" type="button" value="Track Event" />
</div>
<script type="text/javascript">
function trackEvent(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\"}";
trackEvent(eventName, eventValue);
}
</script>
</body>
</html>
上記のコードは、ボタンがクリックされたときにトリガーされる関数を設定しています。 関数自体がカスタムURLを読み込むiframeの要素を作成します。 このURL読み込みが、ネイティブのAndroidまたはiOSメソッドを起動し、その後にAppsFlyer SDKの trackEvent
メソッドを呼び出します。
全ての必須パラメータ(イベント名とイベント値) はURLに追加されています。 以下のAndroidとiOSのコード例は、これらのパラメータを抽出してAppsFlyer SDKの trackEvent
メソッドに渡す方法を示しています。
注意
上記のコードは、実際のWebページの一部にすることも、Webビューの中で読み込むことも可能です。
重要!
イベント値は文字列化されたJSONとして渡すようにしてください。
Example
"{\"af_revenue\":\"6.72\", \"af_content_type\": \"wallets\", \"af_content_id\": \"15854\"}"
Native実装
Androidの実装
Android WebViewは、Webビューから通知を受け取るWebViewClientを使用します。 shouldOverrideUrlLoading
では、Webビューから来る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().trackEvent(getApplicationContext(),eventName,eventValue);
}
return true;
}
view.loadUrl(url);
return true;
}
iOSの実装
Web View Controllerで、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 trackEvent:(url)];
}
}
- (void)trackEvent:(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){
[[AppsFlyerTracker sharedTracker] trackEvent:eventName withValues:eventValue];
}
}
上記のコードはどんなURLの読み込みもリスニングします。 "af-event" を含むURLが読み込まれると、ネイティブの trackEvent
メソッドが起動され、ネイティブ trackEvent
メソッドは必要なパラメータ(イベント名とイベント値)を抽出し、それらをAppsFlyer SDKの trackEvent
メソッドに渡します。
Web View Controllerで, 以下コードを 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 {
// track event
AppsFlyerTracker.shared().trackEvent(eventName as String?, withValues: eventValue)
}
}
}
}
}
上記のコードはどんなURLの読み込みもリスニングします。 "af-event" を含むURLが読み込まれると、ネイティブの trackEvent
メソッドが起動され、ネイティブ trackEvent
メソッドは必要なパラメータ(イベント名とイベント値)を抽出し、それらをAppsFlyer SDKの trackEvent
メソッドに渡します。