Elido
10 мин чтенияВозможности

Диплинки для мобильных приложений без SDK

Universal Links + Android App Links покрывают 80% сценариев диплинкинга без платных SDK. Два файла ассоциации, компромиссы и руководство к действию

Marius Voß
DevRel · edge infra
Phone icon showing a deep-link flow from a tapped short link to an opened app with the iOS Universal Links and Android App Links logos juxtaposed

Диплинк (deep link) - это просто URL, который операционная система передает приложению вместо браузера. Нажмите на ссылку на устройстве, где установлено приложение, - и оно откроется на нужном экране. Нажмите на нее там, где приложения нет, - и браузер перейдет по запасному пути (fallback) на веб-страницу. Один URL, два исхода, никаких JavaScript-прослоек и сторонних SDK для большинства случаев.

Команды часто переплачивают за это. Маркетинговые материалы Branch.io, Adjust, AppsFlyer делают упор на отложенный диплинкинг (deferred deep linking): вы нажимаете на ссылку до установки приложения, устанавливаете его, и оно открывается именно на том контенте, по которому вы перешли. Эта функция действительно сложна и требует серверного фингерпринтинга или трюков с буфером обмена, так как маршрутизация ссылок в ОС не активна до установки приложения. Но отложенный диплинкинг - это лишь часть проблемы. Более распространенный случай - «ссылка открывается в уже установленном приложении» - полностью решается нативными примитивами ОС, которые Apple и Google выпустили в 2015 и 2015–16 годах соответственно. Для этого не нужно ничего, кроме вашего домена и двух JSON-файлов.

Этот пост посвящен именно этим двум JSON-файлам.

Кратко#

  • Apple Universal Links (iOS 9+) и Android App Links (Android 6.0+) обеспечивают сценарий «открыть в приложении, если установлено, иначе в браузере» без сторонних SDK.
  • Оба метода требуют контроля над доменом, работы через HTTPS и наличия проверенного файла ассоциации в директории /.well-known/. ОС загружает и кэширует этот файл при установке приложения, а не при каждом нажатии.
  • Сокращатель ссылок с кастомным доменом, например go.acme.example, отдает оба файла и становится точкой входа для маршрутизации - короткая ссылка сама по себе является диплинком.
  • Что добавляет SDK по сравнению с нативными средствами ОС: отложенный диплинкинг, вероятностную атрибуцию установок и связывание личностей между платформами. Если вам это нужно, SDK оправдывает свою стоимость. Если нет - вы платите за функции, которыми не пользуетесь.

Нативные механизмы ОС#

Apple представила Universal Links в iOS 9 (2015). Android выпустил App Links в Android 6.0 Marshmallow (также в 2015 году). Оба решения следуют одной концептуальной модели: ОС подтверждает проверенную связь между доменом и приложением, и когда происходит переход по URL на этом домене, ОС направляет его в приложение, а не в браузер.

Проверка является взаимной и работает по принципу offline-first. При установке приложения ОС загружает файл ассоциации с вашего домена и кэширует его. Механизм загрузки Apple документирован на developer.apple.com/ios/universal-links/ (доступ от 12.05.2026); эквивалент от Google находится на developer.android.com/training/app-links (доступ от 12.05.2026). Ни одна из этих загрузок не происходит в момент нажатия на ссылку на уже настроенном устройстве - кэширование означает, что решение о маршрутизации не требует сетевых запросов.

Сам редирект - это стандартный HTTP 302. ОС перехватывает его до того, как загрузится браузер, проверяет локальный кэш и передает URL приложению, если найдено соответствие. Как только кэш прогрет, всё решение принимается локально. Край (edge), обслуживающий короткую ссылку, выдает редирект, и ОС берет управление на себя.

Два файла#

apple-app-site-association#

Файл AASA должен обслуживаться по адресу https://yourdomain.example/.well-known/apple-app-site-association (Apple также проверяет путь в корне https://yourdomain.example/apple-app-site-association для обратной совместимости, но стандартным сейчас является путь .well-known). Он должен отдаваться через HTTPS с валидной цепочкой сертификатов и заголовком Content-Type: application/json. Загрузчик CDN Apple отклоняет файлы с неверным Content-Type - это одна из самых частых ошибок конфигурации.

Полный справочник формата доступен на developer.apple.com/documentation/xcode/supporting-associated-domains.

Минимальная структура AASA:

{
  "applinks": {
    "details": [
      {
        "appIDs": ["ABCDE12345.com.example.acme"],
        "components": [
          {
            "/": "/spring-*",
            "comment": "Match any path starting with /spring-"
          },
          {
            "/": "/campaigns/*"
          }
        ]
      }
    ]
  }
}

appIDs - это конкатенация вашего Apple Team ID и bundle identifier вашего приложения, разделенные точкой. Массив components управляет тем, какие пути запускают маршрутизацию в приложение; всё, что не соответствует компоненту, открывается в браузере. Вы можете зарегистрировать несколько приложений в массиве details - это полезно, если у вас есть потребительская и корпоративная версии одного и того же продукта на одном домене.

Один важный нюанс: "/" с шаблоном вроде /spring-* - это сопоставление префикса пути. Парсер AASA от Apple поддерживает синтаксис шаблонов, определенный в документации Xcode, включая * (любая подстрока), ? (любой одиночный символ) и объекты исключения. Если вы хотите сопоставить все пути на домене, используйте "/" : "/*". Если вы хотите исключить конкретный путь из маршрутизации - скажем, страница /account/delete всегда должна открываться в браузере - добавьте объект исключения перед универсальным шаблоном:

{
  "/": "/account/delete",
  "exclude": true
}

Правила вычисляются сверху вниз. Ставьте исключения перед шаблонами с подстановочными знаками.

assetlinks.json#

Файл Digital Asset Links для Android живет по адресу https://yourdomain.example/.well-known/assetlinks.json. Спецификация поддерживается Google на developers.google.com/digital-asset-links/v1/getting-started.

[
  {
    "relation": ["delegate_permission/common.handle_all_urls"],
    "target": {
      "namespace": "android_app",
      "package_name": "com.example.acme",
      "sha256_cert_fingerprints": [
        "AA:BB:CC:DD:EE:FF:00:11:22:33:44:55:66:77:88:99:AA:BB:CC:DD:EE:FF:00:11:22:33:44:55:66:77:88:99"
      ]
    }
  }
]

package_name - это application ID вашего приложения в Play Store. sha256_cert_fingerprints - это SHA-256 отпечаток сертификата, использованного для подписи APK (именно SHA-256, не SHA-1 и не MD5). Вы можете найти отпечаток в Play Console в разделе App Integrity или запустив команду keytool -list -v -keystore your.keystore. Если вы выпускаете и debug, и production сборки, включите оба отпечатка в массив.

В отличие от файла AASA, файл ассоциации Android не поддерживает фильтрацию путей на уровне файла. Сопоставление путей для App Links настраивается в AndroidManifest.xml через <intent-filter> с использованием android:pathPrefix, android:pathPattern или более нового android:pathAdvancedPattern (доступно с Android 12). Файл assetlinks.json подтверждает владение доменом; манифест декларирует, какие пути обрабатывает приложение.

Последовательность разрешения AASA: нажатие на короткую ссылку, загрузка ОС при первой установке, попадание в кэш при последующих нажатиях

Как вписывается сокращатель ссылок#

Короткая ссылка вроде go.acme.example/spring-launch - это просто URL на домене. С точки зрения ОС, если у go.acme.example есть валидный файл AASA или assetlinks.json, любое нажатие на ссылку под этим доменом может привести к открытию приложения.

Эту конфигурацию мы поддерживаем напрямую для кастомных доменов в Elido. Когда вы регистрируете go.acme.example как кастомный домен в своем рабочем пространстве, Elido обслуживает HTTPS-редирект для каждого слага (slug) на этом домене. Вы отдаете два файла ассоциации с того же домена - либо со своего собственного бэкенда за прокси-путем, либо через собственный HTTPS-сервер домена. Срабатывает редирект на крае; ОС перехватывает его до загрузки браузера, обращается к кэшу AASA/App Links и открывает приложение, если найдено соответствие.

Архитектура более подробно описана в посте о кастомных доменах для коротких ссылок - принципы выпуска TLS и настройки CNAME применяются здесь точно так же. Слой диплинков является аддитивным: тот же домен, тот же редирект, плюс два JSON-файла.

Для продуктовых команд, использующих короткие ссылки для онбординга в мобильных приложениях - реферальные коды, инвайт-ссылки, функции «поделиться рецептом» - этот паттерн покрывает почти всё без добавления зависимостей SDK в бинарный файл приложения.

Что дает использование SDK#

Три возможности, которые не предоставляют нативные примитивы ОС:

Отложенный диплинкинг. Пользователь нажимает на вашу ссылку до установки приложения. При первом запуске после установки приложение открывается именно на том контенте, по которому он перешел. iOS Universal Links и Android App Links бездействуют, если приложение не установлено - URL открывается в браузере, и намерение теряется. Его восстановление требует серверного сопоставления отпечатков (IP + User-Agent + временная метка, вероятностный метод) или трюка с буфером обмена в iOS. Branch, Adjust и AppsFlyer реализуют это; нюансы вокруг запросов App Tracking Transparency и поведения Safari делают самостоятельную реализацию нетривиальной.

Атрибуция установок в масштабе. Нативный путь ОС дает вам факт открытия приложения с URL-путем, но если приложение не было установлено в момент первого нажатия, цепочка атрибуции разрывается. Сопоставление кликов с установками через SKAN на iOS и Play Install Referrer на Android возможно без платных SDK, но требует интеграционной работы, которую вендоры атрибуции уже проделали.

Кросс-платформенное связывание личностей. Привязка нажатия к адресу электронной почты, контакту в CRM или веб-сессии. Нативный путь ОС анонимен с точки зрения сервиса ссылок. Вендоры SDK поддерживают постоянный граф устройств. Создание такого графа своими силами - это масштабный проект по созданию инфраструктуры данных.

Если ни один из этих трех пунктов не является критичным, нативных средств ОС вам будет достаточно. Если что-то из этого важно, четко определите область применения - возможно, вам нужен только API для отложенных диплинков, а не весь SDK целиком.

Матрица возможностей: нативные средства ОС и платный SDK оба открывают приложение и имеют веб-запасной вариант, но только SDK добавляет отложенный диплинкинг, атрибуцию установок по каждому клику и граф идентичностей между платформами.

Рецепты настройки#

Требования к DNS и HTTPS#

Оба файла должны отдаваться через HTTPS с того домена, ссылки с которого вы хотите сделать глубокими. Сертификат должен иметь цепочку к доверенному корневому удостоверяющему центру; самоподписанные сертификаты приведут к тихой ошибке валидаторов Apple и Google. Сертификаты Let's Encrypt подходят отлично.

TLS домена также не должен перенаправлять путь /.well-known/ до выдачи файла. Если ваш сервер выдает редирект на www. до того, как загрузчик Apple сможет добраться до https://yourdomain.example/.well-known/apple-app-site-association, загрузка не удастся. Загрузчик Apple следует максимум по одному редиректу, но загрузчик assetlinks от Google вообще не следует по редиректам - файл должен быть по точному адресу.

iOS: Настройка Associated Domains#

В Xcode, в разделе Signing & Capabilities вашей цели (target), добавьте Associated Domains со значением applinks:go.acme.example. Если вы тестируете на разрабатываемой сборке (не распространяемой через TestFlight или App Store), добавьте ?mode=developer к значению: applinks:go.acme.example?mode=developer. Это заставляет ОС заново загружать AASA при каждом запуске вместо использования кэша времени установки - удобно для отладки шаблонов путей без переустановки приложения.

CDN Apple, который загружает ваш файл AASA, - это собственная инфраструктура Apple, а не само устройство. Apple предварительно загружает и кэширует AASA-файлы с вашего домена и раздает их устройствам во время установки приложения. Это означает, что файл должен быть доступен для краулеров Apple, а не только для устройства конечного пользователя. Также это подразумевает задержку распространения - изменения в файле AASA могут достигать всех устройств через кэш Apple в течение нескольких часов. Для разработчиков, которым нужна быстрая итерация по шаблонам путей, параметр ?mode=developer позволяет обходить CDN Apple и загружать файл напрямую с вашего сервера.

В AndroidManifest.xml внутри activity, которая должна обрабатывать диплинки:

<intent-filter android:autoVerify="true">
  <action android:name="android.intent.action.VIEW" />
  <category android:name="android.intent.category.DEFAULT" />
  <category android:name="android.intent.category.BROWSABLE" />
  <data
    android:scheme="https"
    android:host="go.acme.example"
    android:pathPrefix="/campaigns/" />
</intent-filter>

android:autoVerify="true" сообщает Android о необходимости попытки верификации домена через assetlinks.json. Без этого пользователь будет видеть окно выбора приложения при каждом нажатии вместо прямого открытия в приложении. Верификация происходит при установке; устройство обращается к https://go.acme.example/.well-known/assetlinks.json и проверяет наличие отпечатка сертификата установленного приложения.

Валидаторы и типичные ошибки#

Apple предоставляет валидатор на search.developer.apple.com/appsearch-validation-tool/, который проверяет, может ли CDN Apple загрузить и распарсить ваш файл AASA. Введите свой домен, и он вернет либо результат парсинга, либо конкретную ошибку. Типичные сбои:

  • Неверный Content-Type. Если ваш сервер возвращает text/plain или application/octet-stream, валидатор сообщит, что файл не читается, даже если JSON корректен. Явно установите Content-Type: application/json.
  • Отсутствующий или неверный appIDs. Префикс Team ID в appIDs должен точно соответствовать Team ID в вашем аккаунте Apple Developer, включая регистр. Ошибка в одном символе приведет к тихой неудаче при нажатии.
  • Проблемы с цепочкой сертификатов. Если домен использует сертификат, который не ведет к публичному корню (часто бывает в стейджинг-средах с локальными корнями CA), загрузчик Apple отклонит файл.

Валидатор Google - это Digital Asset Links API: https://digitalassetlinks.googleapis.com/v1/statements:list?source.web.site=https://go.acme.example&relation=delegate_permission/common.handle_all_urls. JSON-ответ содержит список всех утверждений, которые Google подтвердил для вашего домена. Если ответ пуст или в нем нет имени вашего пакета, Android не подтвердит приложение автоматически при установке. Типичные сбои:

  • Редирект на пути к assetlinks. Как уже отмечалось: загрузчик Google не следует по редиректам.
  • Неверный отпечаток сертификата. Debug APK и release APK подписаны разными ключами. Если вы указали только отпечаток для релиза, отладочные сборки не пройдут проверку. Укажите оба.
  • Файл отдается с CORS, но неверным заголовком для проверочного запроса. Загрузчику не важен CORS, но некоторые конфигурации CDN возвращают 403 при GET запросе из диапазонов IP Google, если путь не в белом списке кэширования. Проверьте, что /.well-known/assetlinks.json возвращает 200 через внешний HTTP-клиент, а не только в вашем браузере.

Честный взгляд на компромиссы#

Отказ от SDK для диплинков экономит примерно 150–250 КБ сжатого размера бинарного файла и исключает один API-вызов к серверам вендора атрибуции при каждой установке. Это убирает пункт о передаче данных из вашей политики конфиденциальности и может упростить отчетность по GDPR. Это реальные, хотя и скромные победы.

Ценой является то, что атрибуция на момент установки становится приблизительной или отсутствует вовсе. Если пользователь нажмет на ссылку до установки приложения, вы увидите установку в App Store Connect или Play Console, но не сможете связать ее с конкретным нажатием. Вы всё еще сможете проводить эксперименты со смарт-ссылками, чтобы сравнивать, какие кампании приносят больше установок - относительные сигналы сохраняются, - но атрибуция на уровне конкретного устройства и клика требует слоя фингерпринтинга, который есть в SDK.

Для команд на ранних этапах масштабирования, где CPI по каналам еще не определяет бюджетные решения, использование нативных средств ОС - разумный путь. Добавить SDK для атрибуции можно позже, когда эти данные станут действительно необходимы для действий.

Сравнение до и после отказа от SDK для диплинков: экономия 150–250 КБ размера бинарного файла и сокращение потоков данных справа, в противовес потере атрибуции установок по каждому клику, которая становится приблизительной.

Для разработчиков в руководстве по настройке диплинков в Elido описаны полная схема процесса верификации домена, конфигурация прокси AASA и варианты обслуживания assetlinks.json. Страница решений для продуктовых команд охватывает более широкие сценарии использования смарт-ссылок и мобильного взаимодействия.


Мариус Фосс - DevRel + edge infra в Elido. Он отвечает за сервисы edge-redirect и domain-manager.

Попробуйте Elido

Вставьте URL - получите короткую ссылку

Без регистрации. Ссылка живёт 30 дней. Зарегистрируйтесь, чтобы оставить её навсегда.

Бесплатно, без регистрации · 2 в день

Попробуйте Elido

URL-сокращатель с хостингом в ЕС: собственные домены, глубокая аналитика, открытый API. Бесплатный тариф - без банковской карты.

Теги
mobile deep linking
universal links
app links android
deep linking without sdk
apple app site association
digital asset links

Читать дальше