mobilegame-qadeep-linksattributiontesting

Тестирование диплинков: deep links, Universal Links, App Links и матрица состояний

Диплинки — это то, на чём держатся реклама, рефералы, шеринг и push: ссылка должна открыть нужный экран внутри приложения, а не главную и не браузер. Звучит просто, но именно тут прячется матрица состояний, из-за которой «иногда работает, иногда нет». Разберём, как тестировать диплинки по-взрослому.

Три типа ссылок — и что у каждого ломается

  • Кастомная схема (myapp://route): простая, но если приложение не установлено — ссылка никуда не ведёт (ошибка/пусто). Не годится как единственный механизм.
  • iOS Universal Links (обычный https://): открывают приложение, если установлено, иначе — сайт. Требуют файла apple-app-site-association (AASA) на домене.
  • Android App Links (тоже https://): аналог для Android, требуют assetlinks.json и верификации. Без верификации система покажет «диалог выбора» или откроет браузер.

Матрица состояний — главный источник багов

Один и тот же диплинк должен корректно отработать во всех состояниях приложения:

  • Приложение запущено (foreground) → переход на нужный экран без перезагрузки.
  • В фоне (background) → возврат и навигация в нужное место.
  • Убито (killed) / холодный старт → запуск, и после инициализации — переход (частый баг: диплинк теряется, открывается главная).
  • Не установлено → стор или веб-фоллбэк (см. deferred ниже).

Минимальный тест-набор для каждого диплинка = 4 состояния × (валидный / битый линк). Большинство багов — именно в killed + cold start.

Deferred deep linking

Сценарий: пользователь кликнул ссылку, приложения нет → ушёл в стор → после установки должен попасть на тот экран, который был в ссылке (товар, промо, приглашение в группу). Это deferred deep linking — навигация «доезжает» после установки. Проверяем, что:

  • Целевой экран открывается при первом запуске после установки по ссылке.
  • Атрибуция корректна: трекер (Adjust/AppsFlyer) понял, какая кампания/реферал привели — что такое deferred deep linking.
  • Если данные не доехали — вменяемый дефолтный экран, не пустота и не краш.

Fallback и битые ссылки

  • Приложения нет → открыть стор (правильный для платформы) или веб-версию страницы.
  • Устаревший/битый линк (товар удалён, акция закончилась) → понятный экран («не найдено»/главная раздела), не белый экран и не вылет.
  • Неизвестный путь в ссылке → graceful-обработка, дефолтный роутинг.

Безопасность: не доверяй ссылке слепо

Диплинк — это ввод от пользователя, которым управляет кто угодно. Проверяем:

  • Параметры из ссылки не ведут в приватные экраны в обход авторизации (открыл myapp://account/settings без логина).
  • Нет открытого редиректа: ?next=https://evil.com не уводит пользователя/токен наружу.
  • Инъекции в параметры (id, search) экранируются, не ломают экран и не лезут в запросы.
  • Чувствительные действия (оплата, смена данных) не выполняются «по ссылке» без подтверждения.

Верификация домена — почему «открывается в браузере»

Классическая жалоба: «Universal Link открывает сайт, а не приложение». Причина почти всегда — файл связи домена:

  • iOS: apple-app-site-association (AASA) доступен по https://домен/.well-known/apple-app-site-association, отдаётся как JSON, без редиректов, с правильным appID. См. Associated Domains и Universal Links.
  • Android: assetlinks.json в /.well-known/, верификация App Links прошла (verify Android App Links, App Links).
  • Важно: Universal Link надо тапнуть (из Заметок/мессенджера), а не вставить в адресную строку Safari — там он откроется как обычный сайт. Это не баг, это поведение системы.

Атрибуция и аналитика

Маркетинг платит за переходы, поэтому параметры кампании (utm/referral/ad id) должны долетать в трекер ровно один раз и привязываться к правильному источнику. Проверяем: дубли событий, потерю параметров при cold start, корректность при deferred-сценарии.

Инструменты

  • Android: adb shell am start -W -a android.intent.action.VIEW -d "<url>" <package> — открыть диплинк на устройстве/эмуляторе.
  • iOS-симулятор: xcrun simctl openurl booted "<url>".
  • Проверка AASA/assetlinks и редиректов — через перехват HTTP (Proxyman/Charles): что реально отдаёт домен.
  • Реальный тап: ссылка в Заметках/чате (не в адресной строке), на чистом устройстве для deferred.

Чек-лист по диплинкам (12 пунктов)

  1. Каждый диплинк проверен в 4 состояниях: foreground / background / killed / не установлено.
  2. Killed + cold start ведёт на нужный экран (не на главную).
  3. Deferred: после установки по ссылке открывается целевой экран.
  4. Атрибуция кампании/реферала корректна и без дублей.
  5. Fallback при отсутствии приложения → стор/веб.
  6. Битый/устаревший линк → вменяемый экран, не краш.
  7. Кастомная схема не единственный механизм (есть UL/App Links).
  8. AASA доступен, JSON, без редиректа, правильный appID (iOS).
  9. assetlinks.json на месте, App Links верифицированы (Android).
  10. Ссылка не открывает приватные экраны в обход авторизации.
  11. Нет открытого редиректа и инъекций через параметры.
  12. Проверено на реальном тапе (Заметки/чат), не только из адресной строки.

Итог

Диплинк кажется «просто ссылкой», но его качество — это матрица состояний × фоллбэки × верификация домена × безопасность × атрибуция. Большинство багов живут в killed + cold start и в deferred, а «открывается в браузере» — почти всегда непроверенный AASA/assetlinks. Системный чек-лист по состояниям ловит это до того, как сломается рекламная кампания.

Источники