iOS Unified Deep Linking

概要:Unified Deep Linking(UDL)により、アプリ起動時に新規および既存ユーザーを特定のアプリ内アクティビティ(例えば、アプリ内の特定のページなど)に誘導することができます。

📘

UDLのプライバシー保護

For new users, the UDL method only returns parameters relevant to deferred deep linking: deep_link_value and deep_link_sub1-10. If you try to get any other parameters (media_source, campaign, af_sub1-5など)、null を返します。

フロー

iOS UDL flow!

計測フローは次のとおりです。

  1. ユーザーがOneLinkのリンクをクリックします。
    • ユーザーがアプリをインストールしている場合は、ユニバーサルリンクまたはURIスキームでアプリを開きます。
    • ユーザーがアプリをインストールしていない場合は、アプリストアにリダイレクトされ、ダウンロード後、アプリを開きます。
  2. アプリ起動がAppsFlyer SDKをトリガーします。
  3. AppsFlyer SDKはUDL APIを実行します。
  4. UDL APIはOneLinkデータをAppsFlyerサーバーから取得します。
  5. The UDL API calls back the didResolveDeepLink() in the DeepLinkDelegate.
  6. The didResolveDeepLink() method gets a DeepLinkResult object.
  7. The DeepLinkResult object includes:
    • ステータス (Found/Not found/Failure)
    • A DeepLink object that carries the deep_link_value and deep_link_sub1-10 parameters that the developer uses to route the user to a specific in-app activity, which is the main goal of OneLink.

計画

  • UDL には、AppsFlyer iOS SDK V6.1以降が必要です。

OneLinkを設定する際は、マーケティング担当者はこのパラメーターをリンクを作成するために使用し、ディベロッパーは、受け取った値をもとにアプリの動作をカスタマイズします。アプリ内でのルーティングやリンク先でのデータのパーソナライズなど、アプリ内でパラメータが正しく処理されるように実装するのはディベロッパーの責任です。

OneLinkの計画方法:

  1. ユーザーがURLをクリックしたときに、どのような動作が発生し、どのような体験をするのかをマーケティング担当者からヒアリングしてください。
  2. Based on the desired behavior, plan the deep_link_value and other parameters that are needed to give the user the desired personal experience.
    • The deep_link_value is set by the marketer in the URL and used by the developer to redirect the user to a specific place inside the app. For example, if you have a fruit store and want to direct users to apples, the value of deep_link_value can be apples.
    • The deep_link_sub1-10 parameters can also be added to the URL to help personalize the user experience. For example, to give a 10% discount, the value of deep_link_sub1 can be 10.

実装

Let's save you some time >>

Set Deep Linking with our SDK integration wizard

UDL APIを選択したパラメーターと値に基づいて実装してください。

  1. Assign the AppDelegate using self to AppsFlyerLib.shared().deepLinkDelegate.
  2. アプリケーションの関数を実装して、以下を許可してください:
    • ユニバーサルリンクが以下に対応: continue.
    • URIが以下に対応: handleOpen.
  3. Create DeepLinkDelegate as an extension of AppDelegate.
  4. Add application functions to support Universal Links and URI schemes.
  5. In DeepLinkDelegate, make sure you override the callback function, didResolveDeepLink().
    didResolveDeepLink() accepts a DeepLinkResult object as an argument.
  6. Use DeepLinkResult.status to query whether the deep linking match is found.
  7. For when the status is an error, call DeepLinkResult.error and run your error flow.
  8. For when the status is found, use DeepLinkResult.deepLink to retrieve the DeepLink object.
    The DeepLink object contains the deep linking information arranged in public variables to retrieve the values from well-known OneLink keys, for example, DeepLink.deeplinkValue for deep_link_value.
  9. Use deepLinkObj.clickEvent["deep_link_sub1"] to retrieve deep_link_sub1. Do the same for deep_link_sub2-10 parameters, changing the string value as required.
  10. Once deep_link_value and deep_link_sub1-10 are retrieved, pass them to an in-app router and use them to personalize the user experience.

Supporting legacy OneLink links

従来のOneLinkリンクは、Unified Deep Linkingに推奨されるパラメーターを含まないリンクです: deep_link_value and deep_link_sub1-10.
Usually these are links that already exist in the field when migrating from legacy methods to UDL.
News users using legacy links are handled by onConversionDataSuccess 拡張ディファードディープリンクのコンテキストで
UDL は、既存のユーザーのディープリンクを処理します。UDLのコールバックにサポートを追加することをお勧めします didResolveDeepLink for legacy parameters.
Swift code example

Code example

func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
  // Replace 'appleAppID' and 'appsFlyerDevKey' with your Apple App ID (eg 69999999, without id prefix) and DevKey
  // The App ID and the DevKey must be set prior to the calling of the deepLinkDelegate
  AppsFlyerLib.shared().appleAppID = appleAppID  
  AppsFlyerLib.shared().appsFlyerDevKey = appsFlyerDevKey
  ...
  AppsFlyerLib.shared().deepLinkDelegate = self
  ...
}

// For Swift version < 4.2 replace function signature with the commented out code
// func application(_ application: UIApplication, continue userActivity: NSUserActivity, restorationHandler: @escaping ([Any]?) -> Void) -> Bool { // this line for Swift < 4.2
func application(_ application: UIApplication, continue userActivity: NSUserActivity, restorationHandler: @escaping ([UIUserActivityRestoring]?) -> Void) -> Bool {
  AppsFlyerLib.shared().continue(userActivity, restorationHandler: nil)
  return true
}

// Open URI-scheme for iOS 9 and above
func application(_ app: UIApplication, open url: URL, options: [UIApplication.OpenURLOptionsKey : Any] = [:]) -> Bool {
  AppsFlyerLib.shared().handleOpen(url, options: options)
  return true
}

extension AppDelegate: DeepLinkDelegate {
    func didResolveDeepLink(_ result: DeepLinkResult) {
        var fruitNameStr: String?
        switch result.status {
        case .notFound:
            NSLog("[AFSDK] Deep link not found")
            return
        case .failure:
            print("Error %@", result.error!)
            return
        case .found:
            NSLog("[AFSDK] Deep link found")
        }
        
        guard let deepLinkObj:DeepLink = result.deepLink else {
            NSLog("[AFSDK] Could not extract deep link object")
            return
        }
        
        if deepLinkObj.clickEvent.keys.contains("deep_link_sub2") {
            let ReferrerId:String = deepLinkObj.clickEvent["deep_link_sub2"] as! String
            NSLog("[AFSDK] AppsFlyer: Referrer ID: \(ReferrerId)")
        } else {
            NSLog("[AFSDK] Could not extract referrerId")
        }        
        
        let deepLinkStr:String = deepLinkObj.toString()
        NSLog("[AFSDK] DeepLink data is: \(deepLinkStr)")
            
        if( deepLinkObj.isDeferred == true) {
            NSLog("[AFSDK] This is a deferred deep link")
        }
        else {
            NSLog("[AFSDK] This is a direct deep link")
        }
        
        fruitNameStr = deepLinkObj.deeplinkValue
        walkToSceneWithParams(fruitName: fruitNameStr!, deepLinkData: deepLinkObj.clickEvent)
    }
}
// User logic
fileprivate func walkToSceneWithParams(deepLinkObj: DeepLink) {
    let storyBoard: UIStoryboard = UIStoryboard(name: "Main", bundle: nil)
    UIApplication.shared.windows.first?.rootViewController?.dismiss(animated: true, completion: nil)
    guard let fruitNameStr = deepLinkObj.clickEvent["deep_link_value"] as? String else {
         print("Could not extract query params from link")
         return
    }
    let destVC = fruitNameStr + "_vc"
    if let newVC = storyBoard.instantiateVC(withIdentifier: destVC) {
       print("AppsFlyer routing to section: \(destVC)")
       newVC.deepLinkData = deepLinkObj
       UIApplication.shared.windows.first?.rootViewController?.present(newVC, animated: true, completion: nil)
    } else {
        print("AppsFlyer: could not find section: \(destVC)")
    }
}
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
    // Set isDebug to true to see AppsFlyer debug logs
    [AppsFlyerLib shared].isDebug = YES;
    
    // Replace 'appsFlyerDevKey', `appleAppID` with your DevKey, Apple App ID
    [AppsFlyerLib shared].appsFlyerDevKey = appsFlyerDevKey;
    [AppsFlyerLib shared].appleAppID = appleAppID;
    
    [AppsFlyerLib shared].deepLinkDelegate = self;
    
    return YES;
}

- (BOOL)application:(UIApplication *)application continueUserActivity:(NSUserActivity *)userActivity restorationHandler:(void (^)(NSArray * _Nullable))restorationHandler {
    [[AppsFlyerLib shared] continueUserActivity:userActivity restorationHandler:nil];
    return YES;
}

- (BOOL)application:(UIApplication *)app openURL:(NSURL *)url options:(NSDictionary<UIApplicationOpenURLOptionsKey,id> *)options {
    [[AppsFlyerLib shared] handleOpenUrl:url options:options];
    return YES;
}

#pragma mark - DeepLinkDelegate

- (void)didResolveDeepLink:(AppsFlyerDeepLinkResult *)result {
    NSString *fruitNameStr;
   NSLog(@"[AFSDK] Deep link lowkehy");
    switch (result.status) {
        case AFSDKDeepLinkResultStatusNotFound:
            NSLog(@"[AFSDK] Deep link not found");
            return;
        case AFSDKDeepLinkResultStatusFailure:
            NSLog(@"Error %@", result.error);
            return;
        case AFSDKDeepLinkResultStatusFound:
            NSLog(@"[AFSDK] Deep link found");
            break;
    }
    
    AppsFlyerDeepLink *deepLinkObj = result.deepLink;
    
    if ([deepLinkObj.clickEvent.allKeys containsObject:@"deep_link_sub2"]) {
        NSString *referrerId = deepLinkObj.clickEvent[@"deep_link_sub2"];
        NSLog(@"[AFSDK] AppsFlyer: Referrer ID: %@", referrerId);
    } else {
        NSLog(@"[AFSDK] Could not extract referrerId");
    }
    
    NSString *deepLinkStr = [deepLinkObj toString];
    NSLog(@"[AFSDK] DeepLink data is: %@", deepLinkStr);
    
    if (deepLinkObj.isDeferred) {
        NSLog(@"[AFSDK] This is a deferred deep link");
        if (self.deferredDeepLinkProcessedFlag) {
            NSLog(@"Deferred deep link was already processed by GCD. This iteration can be skipped.");
            self.deferredDeepLinkProcessedFlag = NO;
            return;
        }
    } else {
        NSLog(@"[AFSDK] This is a direct deep link");
    }
    
    fruitNameStr = deepLinkObj.deeplinkValue;
    
    // If deep_link_value doesn't exist
    if (!fruitNameStr || [fruitNameStr isEqualToString:@""]) {
        // Check if fruit_name exists
        id fruitNameValue = deepLinkObj.clickEvent[@"fruit_name"];
        if ([fruitNameValue isKindOfClass:[NSString class]]) {
            fruitNameStr = (NSString *)fruitNameValue;
        } else {
            NSLog(@"[AFSDK] Could not extract deep_link_value or fruit_name from deep link object with unified deep linking");
            return;
        }
    }
    
    // This marks to GCD that UDL already processed this deep link.
    // It is marked to both DL and DDL, but GCD is relevant only for DDL
    self.deferredDeepLinkProcessedFlag = YES;
    
    [self walkToSceneWithParams:fruitNameStr deepLinkData:deepLinkObj.clickEvent];
}

- (void)walkToSceneWithParams:(NSString *)fruitName deepLinkData:(NSDictionary *)deepLinkData {
    UIStoryboard *storyboard = [UIStoryboard storyboardWithName:@"Main" bundle:nil];
    [[UIApplication sharedApplication].windows.firstObject.rootViewController dismissViewControllerAnimated:YES completion:nil];
    
    NSString *destVC = [fruitName stringByAppendingString:@"_vc"];
    DLViewController *newVC = [storyboard instantiateViewControllerWithIdentifier:destVC];
    
    NSLog(@"[AFSDK] AppsFlyer routing to section: %@", destVC);
    newVC.deepLinkData = deepLinkData;
    
    [[UIApplication sharedApplication].windows.firstObject.rootViewController presentViewController:newVC animated:YES completion:nil];
}

⇲ Github links: Swift
⇲ Github links: Objective-C

Deferred Deep Linking after network consent

In some cases the application might require consent from the user in order to connect to the network, in a dialog similar to this one:

In order to support deferred deep linking once the network consent is given we recommend:

  • Implement eDDL to allow UDL to handle the deferred deep linking

ディファードディプリンクのテスト

Prerequisites

  • UDL実装を完了してください。
  • テストデバイスの登録
  • アプリでデバッグモードを有効にしてください。
  • 端末にアプリがインストールされていないことを確認してください。
  • マーケティング担当者もしくはAppsFlyer管理画面からOneLinkテンプレートを取得してください。
    • リンクはこのようになります: https://onelink-basic-app.onelink.me/H5hv.
    • This example uses the OneLink subdomain onelink-basic-app.onelink.me and the OneLink template ID H5hv.

The test link

既存のOneLinkリンクを使用することも、テスト用にマーケティング担当者に新しいリンクを作成してもらうこともできます。OneLinkのURLは、ショートリンクとロングリンクのどちらでも使用できます。

既存のリンクに一時的にパラメータを追加する

  • リンクのドメインとOneLinkテンプレートのみを使用してください。
    例: https://onelink-basic-app.onelink.me/H5hv.
  • Add OneLink parameters deep_link_value and deep_link_sub1-10 アプリケーションで期待どおり。パラメータは、クエリパラメータとして追加する必要があります。

    • 例: https://onelink-basic-app.onelink.me/H5hv?pid=my_media_source&deep_link_value=apples&deep_link_sub1=23

Perform the test

  1. 端末でリンクをクリックします。
  2. AppsFlyer管理画面で設定されたOneLinkの設定内容を基に、App StoreまたはWebサイトのいずれかにリダイレクトします。
  3. アプリをインストールします。

    重要

    • アプリがまだ開発中で、ストアにアップロードされていない場合、このような画像が表示されます:
      drawing
    • Xcodeからアプリをインストールします。
  4. UDL detects the deferred deep linking, matches the install to the click, and retrieves the OneLink parameters to didResolveDeepLink callback.

Expected logs results

📘

📘 次のログは、デバッグモードが有効になっている場合にのみ使用できます。

  • SDKが初期化された場合:
    [AppsFlyerSDK] [com.apple.main-thread] AppsFlyer SDK version 6.6.0 started build
    
  • UDL APIの開始:
    D/AppsFlyer_6.9.0: [DDL] start
    
  • UDLがAppsFlyerにクエリを送信し、このインストールとの一致を問い合わせる:
    [AppsFlyerSDK] [com.appsflyer.serial] [DDL] URL: https://dlsdk.appsflyer.com/v1.0/ios/id1512793879?sdk_version=6.6&af_sig=efcecc2bc95a0862ceaa7b62fa8e98ae1e3e022XXXXXXXXXXXXXXXX
    
  • UDL は応答を受け取り、呼び出します didResolveDeepLink コールバック status=FOUND およびOneLinkリンクデータ:
    [AppsFlyerSDK] [com.appsflyer.serial] [DDL] Calling didResolveDeepLink with: {"af_sub4":"","click_http_referrer":"","af_sub1":"","click_event":{"af_sub4":"","click_http_referrer":"","af_sub1":"","af_sub3":"","deep_link_value":"peaches","campaign":"","match_type":"probabilistic","af_sub5":"","campaign_id":"","media_source":"","deep_link_sub1":"23","af_sub2":""},"af_sub3":"","deep_link_value":"peaches","campaign":"","match_type":"probabilistic","af_sub5":"","media_source":"","campaign_id":"","af_sub2":""}
    

ディープリンクのテスト (Universal Links)

Prerequisites

  • UDL実装を完了してください。
  • テストデバイスの登録
  • アプリでデバッグモードを有効にしてください。
  • アプリがすでにデバイスにインストールされていることを確認してください。
  • マーケティング担当者もしくはAppsFlyer管理画面からOneLinkテンプレートを取得してください。
    • リンクはこのようになります: https://onelink-basic-app.onelink.me/H5hv.
    • This example uses the OneLink subdomain onelink-basic-app.onelink.me and the OneLink template ID H5hv
  • Configure Universal Links.

Create the test link

ディファードディープリンクと同じ方法を使用します。

Perform the test

  1. 端末でリンクをクリックします。
  2. UDL detects the Universal Link and retrieves the OneLink parameters to didResolveDeepLink callback.

Expected logs results

📘

📘 次のログは、デバッグモードが有効になっている場合にのみ使用できます。

  • リンクがOneLinkショートリンクの場合(例:https://onelink-basic-app.onelink.me/H5hv/apples
    [AppsFlyerSDK] [com.apple.main-thread] NSUserActivity `webpageURL`: https://onelink-basic-app.onelink.me/H5hv/apples
    [AppsFlyerSDK] [com.appsflyer.serial] UniversalLink/Deeplink found:
    https://onelink-basic-app.onelink.me/H5hv/apples
    [AppsFlyerSDK] [com.appsflyer.serial] Shortlink found. Executing: https://onelink.appsflyer.com/shortlink-sdk/v2/H5hv?id=apples
    ...
    [AppsFlyerSDK] [com.appsflyer.serial]                        
    [Shortlink] OneLink:{
      c = test1;
      campaign = test1;
      "deep_link_sub1" = 23;
      "deep_link_value" = peaches;
      "is_retargeting" = true;
      "media_source" = SMS;
      pid = SMS;
    } 
    
  • UDL 呼び出し didResolveDeepLink コールバック status=FOUND およびOneLinkリンクデータ:
    [AppsFlyerSDK] [com.appsflyer.serial] [DDL] Calling didResolveDeepLink with: {"af_sub4":null,"click_http_referrer":null,"af_sub1":null,"click_event":{"campaign":"test1","deep_link_sub1":"23","deep_link_value":"peaches","media_source":"SMS"},"af_sub3":null,"deep_link_value":"peaches","campaign":"test1","match_type":null,"af_sub5":null,"media_source":"SMS","campaign_id":null,"af_sub2":null}