Унифицированный диплинкинг для iOS

Краткий обзор. Унифицированный диплинкинг (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. При открытии приложения запускается SDK AppsFlyer.
  3. SDK AppsFlyer запускает API UDL.
  4. API UDL получает данные 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:
    • Статус (найдено / не найдено / ошибка)
    • 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 нужен SDK AppsFlyer для iOS версии 6.1+.

При настройке ссылок OneLink маркетолог использует параметры для создания ссылок, а разработчик настраивает поведение приложения на основе полученных значений. Задача разработчика — убедиться, что эти параметры корректно обрабатываются в приложении, как с точки зрения внутренней маршрутизации, так и с точки зрения персонализации данных в ссылке.

Для планирования ссылок OneLink:

  1. Выясните у маркетолога, какими должны быть поведение и персонализация пользовательского интерфейса при клике.
  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

Внедрите логику API UDL на основе выбранных параметров и значений.

  1. Assign the AppDelegate using self to AppsFlyerLib.shared().deepLinkDelegate.
  2. Реализуйте функцию application, чтобы разрешить:
    • поддержку универсальных ссылок с помощью 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 — это ссылки, которые не содержат рекомендованные для унифицированного диплинкинга параметры: deep_link_value and deep_link_sub1-10.
Обычно это ссылки, которые уже используются при переходе от устаревших методов к UDL.
Новые пользователи, использующие устаревшие ссылки, обрабатываются посредством onConversionDataSuccess в контексте расширенного отложенного диплинкинга.
UDL обрабатывает диплинкинг для имеющихся пользователей. Рекомендуется добавить поддержку в обратный вызов UDL didResolveDeepLink для устаревших параметров.
Пример кода Swift

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

⇲ Ссылки на Github: Swift

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

The test link

Вы можете использовать имеющуюся ссылку OneLink или попросить своего маркетолога создать новую для тестирования. Можно использовать как короткие, так и длинные URL-адреса OneLink.

Добавление специальных параметров к имеющейся ссылке

  • Используйте только домен и шаблон OneLink вашей ссылки. Например: https://onelink-basic-app.onelink.me/H5hv.
  • Добавьте параметры OneLink 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. OneLink в соответствии с настройкой ссылки направляет вас либо в App Store, либо на сайт.
  3. Установите приложение.

    Важно

    • Если приложение находится в разработке и еще не загружено в магазин, вы увидите это изображение:
      drawing
    • Установите приложение из Xcode.
  4. UDL распознает отложенный диплинкинг, сопоставляет установку с кликом и получает параметры OneLink в обратный вызов didResolveDeepLink .

Expected logs results

📘

Следующие журналы доступны, только если включен отладочный режим.

  • SDK инициализирован:
    [AppsFlyerSDK] [com.apple.main-thread] AppsFlyer SDK version 6.6.0 started build
    
  • Запускается API UDL:
    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":""}
    

Тестирование диплинкинга (универсальные ссылки)

Prerequisites

Create the test link

Используйте тот же метод, что и в отложенном диплинкинге.

Perform the test

  1. Перейдите по ссылке на вашем устройстве.
  2. UDL распознает универсальную ссылку и получает параметры OneLink в обратный вызов didResolveDeepLink .

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}