09 июн. 2025 г.·6 мин

Глубокие ссылки для нативных мобильных приложений: маршруты, токены, «открыть в приложении»

Узнайте про глубокие ссылки для нативных мобильных приложений: спланируйте маршруты, настройте «открыть в приложении» и безопасную передачу одноразовых кодов для Kotlin и SwiftUI без громоздкого кастомного маршрутизатора.

Глубокие ссылки для нативных мобильных приложений: маршруты, токены, «открыть в приложении»

Что должна делать глубокая ссылка, простыми словами

Когда кто‑то нажимает ссылку на телефоне, он ожидает один результат: попасть именно туда, куда нужно, сразу. Не куда‑то рядом. Не на главный экран с поиском. Не на экран входа, который забывает, зачем человек пришёл.

Хороший опыт глубоких ссылок выглядит так:

  • Если приложение установлено, оно открывается на том экране, который подразумевает ссылка.
  • Если приложение не установлено, нажатие всё равно помогает (например, открывает веб‑падение или страницу в магазине и может вернуть человека к тому же месту после установки).
  • Если нужно войти, пользователь входит один раз и попадает на нужный экран, а не на стартовое окно приложения.
  • Если ссылка несёт действие (принять приглашение, посмотреть заказ, подтвердить e‑mail), действие ясно и безопасно.

Большая часть раздражения приходит от ссылок, которые «как бы работают», но ломают поток. Люди видят неправильный экран, теряют контекст или застревают в цикле: нажали ссылку, вошли, попали на панель, снова нажали — и снова требуют входа. Даже один лишний шаг может заставить пользователя сдаться, особенно для одноразовых действий: приглашения, сброс пароля и т. п.

Прежде чем писать код на Kotlin или SwiftUI, решите, что именно вы хотите, чтобы ссылки означали. Какие экраны можно открыть извне? Что меняется, если приложение закрыто или уже запущено? Что делать, если пользователь не в системе?

Планирование предотвращает большую часть проблем: понятные маршруты, предсказуемое поведение «открыть в приложении» и безопасная передача одноразовых кодов без хранения секретов прямо в URL.

Типы глубоких ссылок и где «открыть в приложении» идёт не так

Не каждая «ссылка, которая открывает приложение» ведёт себя одинаково. Если считать их взаимозаменяемыми, вы получите классические ошибки: ссылка откроет не то место, откроет браузер вместо приложения или будет работать только на одной платформе.

Три распространённых варианта:

  • Кастомные схемы (например, myapp:). Просто настроить, но многие браузеры и приложения относятся к ним осторожно.
  • Universal Links (iOS) и App Links (Android). Это обычные веб‑ссылки, которые открывают приложение, если оно установлено, или падают на сайт, если нет.
  • Ссылки, открытые во встроенном браузере. Ссылки, открытые внутри почтового клиента или мессенджера, часто ведут себя иначе, чем в Safari или Chrome.

«Открыть в приложении» может означать разное в зависимости от того, где нажали ссылку. Ссылка, нажата в Safari, может сразу перейти в приложение. Та же самая ссылка в письме или мессенджере может сначала открыть встроенный веб‑вью, и пользователю придётся нажать дополнительную кнопку «открыть» (или он её вовсе не увидит). На Android Chrome может уважать App Links, тогда как встроенный браузер социальной сети может их игнорировать.

Ещё одна ловушка — холодный старт vs приложение уже запущено.

  • Холодный старт: ОС запускает приложение, приложение инициализируется, и только потом вы получаете глубокую ссылку. Если ваш стартовый поток показывает сплеш, проверяет аутентификацию или загружает удалённый конфиг, ссылка может «потеряться», если вы сразу её не сохраните и не воспроизведёте после готовности.
  • Приложение запущено: вы получаете ссылку во время сессии. Навигационный стек уже есть, поэтому одно и то же целевое место может требовать разной обработки (например, пушить экран в стек или полностью сбрасывать навигацию).

Простой пример: приглашение, нажато из Telegram, часто открывается внутри встроенного браузера. Если вы предполагаете, что ОС всегда передаст управление напрямую, пользователи увидят веб‑страницу и подумают, что ссылка сломана. Планируйте такие окружения заранее и вы сократите платформенно‑специфичную «склейку» в коде.

Спланируйте маршруты до того, как начнёте реализовывать

Большинство багов с глубокими ссылками — это не проблемы Kotlin или SwiftUI. Это проблемы планирования. Ссылка плохо маппится на один экран или несёт слишком много «возможно» опций.

Начните с одного согласованного набора маршрутов, который отражает мышление пользователей: список, деталь, настройки, оформление, приглашение. Делайте паттерны читаемыми и стабильными — вы будете их использовать в письмах, QR и на сайте.

Простой набор маршрутов может включать:

  • Главная
  • Список заказов и детали заказа (orderId)
  • Настройки аккаунта
  • Принятие приглашения (inviteId)
  • Поиск (query, tab)

Затем определите параметры:

  • Используйте ID для одиночных объектов (orderId).
  • Для состояния UI (tab, filter) используйте опциональные параметры.
  • Решите значения по умолчанию, чтобы у каждой ссылки было одно «лучшее» место назначения.

Также решите, что делать, если ссылка неверна: отсутствуют данные, ID недействителен или контент недоступен пользователю. Самый безопасный вариант — открыть ближайший стабильный экран (например, список) и показать короткое сообщение. Избегайте пустых экранов или экрана входа без контекста.

Наконец, планируйте по источнику. QR обычно требует короткого маршрута, который быстро открывается и устойчив к плохой связи. Письменная ссылка в e‑mail может быть длиннее и содержать дополнительный контекст. Веб‑ссылка должна деградировать аккуратно: если приложение не установлено, пользователь всё ещё должен попасть на страницу, объясняющую дальнейшие шаги.

Если вы используете подход, управляемый бэкендом (например, генерируете API‑эндпоинты и экраны с платформой вроде AppMaster), этот план маршрутов становится общим контрактом: приложение знает, куда идти, а бэкенд знает, какие ID и состояния валидны.

Безопасная передача токенов без секретов в URL

Глубокая ссылка часто воспринимается как безопасный конверт. Она им не является. Всё, что в URL, может оказаться в истории браузера, скриншотах, превью в мессенджерах, логах аналитики или быть скопированным в чат.

Избегайте помещать секреты в ссылку. Это долгоживущие токены доступа, refresh‑токены, пароли, персональные данные — всё, что позволяет выдать себя за пользователя при пересылке ссылки.

Более безопасный паттерн — одноразовый короткоживущий код. Ссылка несёт только этот код, а приложение меняет его на реальную сессию после открытия. Если кто‑то украдёт ссылку, код станет бесполезен через минуту‑две или после первой успешной замены.

Простой поток передачи:

  • Ссылка содержит одноразовый код, а не сессионный токен.
  • Приложение открывается и вызывает ваш бэкенд для погашения кода.
  • Бэкенд проверяет срок действия, что код не использовался, затем помечает его как использованный.
  • Бэкенд возвращает обычную аутентифицированную сессию для приложения.
  • Приложение очищает код из памяти после погашения.

Даже после успешного погашения подтверждайте личность внутри приложения перед совершением чувствительных действий. Если ссылка должна подтвердить платёж, изменить e‑mail или экспортировать данные — требуйте быстрой перепроверки: биометрию или свежий вход.

Храните полученную сессию безопасно. На iOS обычно это Keychain. На Android — хранилище с поддержкой Keystore. Храните только необходимое и очищайте при выходе, удалении аккаунта или при подозрительной повторной попытке использования.

Конкретный пример: вы отправляете приглашение в рабочую группу. Ссылка содержит одноразовый код, действующий 10 минут. Приложение погашает код, затем показывает экран с явным указанием, что произойдёт (в какую рабочую группу войдёт пользователь). Только после подтверждения пользователь присоединяется.

Если вы строите с AppMaster, это легко отображается как эндпоинт погашения кодов, а мобильный UI обрабатывает шаг подтверждения перед выполнением действий с высоким эффектом.

Аутентификация и «продолжить оттуда, где остановились»

Сохраните путь к исходному коду
Получайте реальный исходный код, когда нужен полный контроль или self‑hosting.
Экспортировать код

Глубокие ссылки часто ведут к экранам с приватными данными. Сначала решите, что можно открывать всем (публичное), а что требует сессии (защищённое). Это простое решение предотвращает большинство сюрпризов, которые «работали в тесте».

Простое правило: сначала ведите на безопасное посадочное состояние, а уже затем переходите к защищённому экрану после подтверждения аутентификации.

Решите, что публично, а что защищено

Относитесь к глубоким ссылкам так, будто их могут переслать не тому человеку.

  • Публичное: маркетинговые страницы, статьи справки, начало сброса пароля, начало принятия приглашения (без показа данных)
  • Защищённое: детали заказа, сообщения, настройки аккаунта, экраны администратора
  • Смешанное: можно показывать превью, но только с не чувствительными заполнителями до входа

«Продолжить после входа», чтобы вернуться в нужное место

Надёжный подход: распарсить ссылку, сохранить намерение (куда вернуться), затем маршрутизировать в зависимости от состояния аутентификации.

Пример: пользователь нажимает ссылку на конкретный тикет поддержки, будучи неавторизованным. Ваше приложение должно открыть нейтральный экран, попросить войти, а затем автоматически перевести на этот тикет.

Чтобы это работало надёжно, сохраните небольшой «return target» локально (имя маршрута + ID тикета) с коротким сроком жизни. После завершения входа прочитайте его один раз, перейдите и удалите. Если вход не удался или цель истекла — вернитесь на безопасный домашний экран.

Обрабатывайте крайние случаи с уважением:

  • Просроченная сессия: покажите короткое сообщение, попросите повторно авторизоваться и затем продолжите.
  • Отозванный доступ: откройте оболочку назначения, затем покажите «У вас больше нет доступа» и предложите безопасный следующий шаг.

Также избегайте показа приватных данных в превью на экране блокировки, в переключателе приложений или в уведомлениях. Держите чувствительные экраны пустыми, пока данные не загрузятся и сессия не будет подтверждена.

Подход к маршрутизации, который избегает кастомного навигационного спагетти

Преобразуйте маршруты в рабочие экраны
Определите таблицу маршрутов один раз, а затем генерируйте бэкенд и мобильные приложения из неё.
Начать создание

Глубокие ссылки становятся грязными, когда каждый экран сам парсит URL. Это разбивает небольшие решения (что опционально, что обязательно, что валидно) по всему приложению и усложняет изменения.

Относитесь к маршрутизации как к общей сантехнике. Держите одну таблицу маршрутов и один парсер, а UI пусть получает чистые входные данные.

Используйте одну общую таблицу маршрутов

Пусть iOS и Android согласятся на единый, понятный список маршрутов. Думайте о нём как о контракте.

Каждый маршрут маппится на:

  1. экран и
  2. небольшую модель ввода.

Например, «Детали заказа» маппятся на экран Order с вводом OrderRouteInput(id). Если маршруту нужны доп. значения (например, источник ref), они принадлежат модели ввода, а не расползаются по коду представления.

Централизуйте парсинг и валидацию

Держите парсинг, декодирование и валидацию в одном месте. UI не должен спрашивать «Есть ли токен?» или «Валиден ли этот ID?». Он должен получать либо валидный вход в виде модели маршрута, либо понятный статус ошибки.

Практический поток:

  • Получили URL (тап, скан, share sheet)
  • Распарсили в известный маршрут
  • Проверили обязательные поля и форматы
  • Сгенерировали цель для экрана и модель ввода
  • Перешли к навигации через единый вход

Добавьте экран‑fallback для «неизвестной ссылки». Сделайте его полезным: покажите, что не удалось открыть, объясните простым языком и предложите следующие действия: домой, поиск или вход.

Пошагово: проектируем глубокие ссылки и поведение «открыть в приложении»

Хорошие глубокие ссылки кажутся скучными в лучшем смысле: пользователь нажал и попал туда, куда нужно, независимо от того, установлено приложение или нет.

Шаг 1: выберите важные точки входа

Перечислите первые 10 типов ссылок, которые действительно используются: приглашения, сброс пароля, квитанции по заказам, «посмотреть тикет», промо‑ссылки. Делайте список намеренно маленьким.

Шаг 2: опишите паттерны как контракт

Для каждой точки входа определите один канонический паттерн и минимум данных, нужных для открытия правильного экрана. Предпочитайте стабильные ID вместо имён. Решите, что обязательно, а что опционально.

Полезные правила:

  • Одна цель на маршрут (invite, reset, receipt).
  • Обязательные параметры всегда присутствуют; опциональные имеют безопасные значения по умолчанию.
  • Используйте те же паттерны на iOS (SwiftUI) и Android (Kotlin).
  • Если ожидаются изменения, зарезервируйте версию (v1).
  • Опишите поведение при отсутствии параметров (показать экран ошибки, а не пустую страницу).

Шаг 3: решите поведение при входе и пост‑логин цель

Опишите для каждого типа ссылки, требуется ли вход. Если да — сохраните цель и вернитесь к ней после входа.

Пример: ссылка на квитанцию может показывать превью без входа, но нажатие «Скачать счёт» потребует входа и вернёт пользователя к той же квитанции после авторизации.

Шаг 4: правила передачи токенов (не храните секреты в URL)

Если ссылка нуждается в одноразовом токене (принятие приглашения, reset, magic sign‑in), определите срок жизни и способ использования.

Практический подход: URL несёт одноразовый код с коротким сроком действием; приложение обменивает его на сессию через бэкенд.

Шаг 5: тестируйте три реальных состояния

Глубокие ссылки ломаются на краях. Протестируйте каждый тип ссылки в:

  • Холодный старт (приложение закрыто)
  • Тёплый старт (приложение в памяти)
  • Нет установленного приложения (ссылка всё равно должна вести куда‑то понятному)

Если вы держите маршруты, проверки авторизации и правила обмена кодов в одном месте, вы избегаете разброса кастомной логики по Kotlin и SwiftUI экранам.

Частые ошибки, которые ломают глубокие ссылки (и как их избежать)

Планируйте маршруты на основе реальных данных
Визуально моделируйте данные в PostgreSQL и поддерживайте согласованность ID и маршрутов.
Создать приложение

Глубокие ссылки чаще всего ломаются по простым причинам: мелким допущениям, переименованию экрана или «временным» токенам, которые в итоге рассылают повсюду.

Ошибки, которые встречаются в реальности (и их исправления)

  • Кладут токены доступа в URL (и сливают их в логи). Строки запроса копируются, пересылаются, сохраняются в истории и попадают в логи аналитики и крашей. Исправление: в ссылке — только одноразовый код, погашайте его внутри приложения и делайте срок коротким.

  • Предполагают, что приложение установлено (нет fallback). Если ссылка открывает страницу с ошибкой или ничего не делает, пользователи сдаются. Исправление: обеспечьте веб‑fallback, который объясняет, что происходит, и предлагает путь к установке.

  • Не учитывают несколько аккаунтов на устройстве. Открыть правильный экран под неправильным пользователем хуже, чем сломанная ссылка. Исправление: при получении ссылки проверьте активный аккаунт, попросите подтвердить или переключиться, затем продолжайте. Если действие зависит от конкретной рабочей области, включите её идентификатор (без секретов) и валидируйте.

  • Ломают ссылки при изменении экранов или маршрутов. Если маршрут завязан на имена UI, старые ссылки умирают при переименовании вкладки. Исправление: делайте стабильные, основанные на намерениях маршруты (invite, ticket, order) и поддерживайте старые версии.

  • Нет трассировки ошибок. Без возможности воспроизвести, саппорт только догадывается. Исправление: включите несекретный request ID в ссылку, логируйте его на сервере и в приложении, и показывайте его в сообщении об ошибке.

Короткая проверка реальности: представьте приглашение, отправленное в групповой чат. Кто‑то открывает его на рабочем телефоне с двумя аккаунтами, приложение не установлено на планшете, и ссылка пересылается коллеге. Если ссылка содержит только код приглашения, поддерживает fallback, предлагает переключиться между аккаунтами и логирует request ID, то она с большой вероятностью сработает во всех этих ситуациях, не раскрывая секретов.

Пример: приглашение, которое открывает нужный экран каждый раз

Отправляйте надёжные ссылки для приглашений
Создайте приём приглашений с понятными шагами подтверждения и проверкой на сервере.
Построить поток приглашений

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

Сценарий: менеджер приглашает нового агента в рабочую группу «Support Team». Агент нажимает приглашение в Telegram.

Если приложение установлено, система должна открыть приложение и передать детали приглашения. Если не установлено, пользователь попадает на простую веб‑страницу с объяснением и предложением установить приложение. После установки и первого запуска приложение должно уметь завершить поток приглашения, чтобы пользователю не нужно было искать ссылку заново.

В приложении поток одинаков для Kotlin и SwiftUI:

  • Прочитать код приглашения из входящей ссылки.
  • Проверить, вошёл ли пользователь в систему.
  • Подтвердить приглашение на бэкенде, затем перейти на нужный экран.

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

Ощущение пользователя должно быть предсказуемым:

  • Не вошёл: он видит экран входа, затем автоматически возвращается к принятию приглашения.
  • Вошёл: он видит подтверждение «Присоединиться к рабочей группе», подтверждает и попадает в нужную рабочую область.

Если приглашение просрочено или уже принято, не кидайте пользователя на пустую страницу с ошибкой. Покажите понятное сообщение и следующую опцию: запросить новое приглашение, переключить аккаунт или связаться с админом. «Это приглашение уже принято» лучше, чем «Неверный токен».

Быстрый чек‑лист и дальнейшие шаги

Глубокие ссылки кажутся «готовыми», когда они одинаково ведут везде: при холодном старте, тёплом старте и когда пользователь уже в системе.

Чек‑лист

Перед релизом протестируйте на реальных устройствах и версиях ОС:

  • Ссылка открывает правильный экран при холодном и тёплом старте.
  • В URL нет чувствительных данных. Если нужен токен — делайте одноразовый и короткоживущий.
  • Неизвестные, просроченные или уже использованные ссылки падают на понятный экран с полезным сообщением и безопасным дальнейшим действием.
  • Ссылки работают из почтовых приложений, браузеров, QR‑сканеров и превью в мессенджерах (некоторые предварительно открывают ссылки).
  • Логи сообщают, что произошло (получена ссылка, распарсен маршрут, требуется аутентификация, причина успеха/ошибки).

Простой способ проверить: выберите несколько критичных ссылок (приглашение, сброс пароля, детализация заказа, тикет поддержки, промо) и прогоняйте их через один и тот же тестовый сценарий: тап из письма, тап из чата, скан QR, открытие после переустановки.

Следующие шаги (чтобы поддерживать в порядке)

Если обработка глубоких ссылок разрастается по экранам, сделайте маршрутизацию и авторизацию общей сантехникой, а не локальным кодом экранов. Централизуйте парсинг в одном месте и делайте так, чтобы каждая цель принимала чистые параметры (а не сырые URL). То же и с аутентификацией: один шлюз решает «продолжить сейчас» или «войти сначала, затем продолжить».

Если хотите уменьшить кастомный код, полезно разрабатывать бэкенд, авторизацию и мобильные приложения вместе. AppMaster (appmaster.io) — платформа no‑code, которая генерирует production‑ready бэкенды и нативные мобильные приложения, что облегчает согласование имён маршрутов и эндпоинтов погашения одноразовых кодов по мере изменения требований.

Если сделаете одно дело на следующей неделе — сделайте это: запишите ваши канонические маршруты и точные fallback‑правила для каждого случая ошибки, затем реализуйте эти правила в одном слое маршрутизации.

Вопросы и ответы

Что должна делать глубокая ссылка при нажатии?

Глубокая ссылка должна открыть именно тот экран, который подразумевает ссылка, а не общий главный экран или панель. Если приложение не установлено, ссылка всё равно должна помогать — приводить на понятную веб‑страницу и затем возвращать пользователя к тому же месту после установки.

Стоит ли использовать Universal Links/App Links или кастомную схему URL?

Universal Links (iOS) и App Links (Android) — это обычные веб‑URL, которые могут открыть приложение, если оно установлено, и корректно падать на веб‑страницу, если нет. Пользовательские схемы URL проще подключить, но браузеры и другие приложения обрабатывают их не всегда предсказуемо, поэтому их лучше использовать вторично.

Почему «открыть в приложении» работает в Safari/Chrome, но ломается в почте или мессенджерах?

Многие почтовые и мессенджер‑приложения открывают ссылки в встроенном браузере, который может не передать управление ОС так, как Safari или Chrome. Планируйте дополнительный шаг: делайте веб‑fallback понятным и обрабатывайте случаи, когда пользователь сначала попал на веб‑страницу.

Как не потерять глубокую ссылку при холодном старте?

При холодном старте приложение показывает сплеш, выполняет проверки или загружает конфиг, и навигация может ещё не быть готова. Надёжное решение — сразу сохранить цель ссылки, закончить инициализацию, а затем «проиграть» навигацию, когда приложение готово.

Какие данные никогда не стоит класть в URL глубокой ссылки?

Никогда не помещайте в URL долгоживущие токены доступа, refresh‑токены, пароли или персональные данные: URL попадают в историю, скриншоты, превью в мессенджерах и логи. Вместо этого передавайте короткий одноразовый код и обменяйте его на сессию уже внутри приложения.

Как сделать так, чтобы пользователь попал на нужный экран после входа?

Разберите ссылку, сохраните желаемую точку возврата и затем маршрутизируйте в зависимости от состояния аутентификации. Сделайте сохранённую цель маленькой и с коротким временем жизни; после успешного использования очистите её.

Как избежать навигационного спагетти при обработке глубоких ссылок?

Определите маршруты как единый контракт и централизуйте парсинг и валидацию в одном месте. Передавайте экранам уже готовые параметры вместо сырых URL — это предотвращает расхождение правил по всему приложению.

Как должны вести себя глубокие ссылки на устройствах с несколькими учётными записями?

Проверьте, какая учётная запись активна и соответствует ли она рабочей области, указанной в ссылке; при необходимости предложите пользователю подтвердить или переключиться, прежде чем показывать приватный контент. Небольшой шаг подтверждения лучше, чем показать данные под неправильной учётной записью.

Что делать, если ссылка неверна, просрочена или нет данных?

По умолчанию открывайте ближайший стабильный экран (например, список) и покажите короткое сообщение, объясняющее, что не получилось открыть. Избегайте пустых экранов, молчаливых ошибок или голого экрана входа без контекста.

Какой минимум тестов нужно пройти перед выпуском глубоких ссылок?

Тестируйте важные типы ссылок в трёх состояниях: приложение закрыто, приложение уже запущено и приложение не установлено. Тестируйте из реальных источников: почта, чаты, QR‑сканеры. Если вы используете AppMaster, можно сохранять согласованность имён маршрутов и эндпоинтов погашения одноразовых кодов между бэкендом и нативными приложениями, что уменьшает количество кастомной «склейки».

Легко начать
Создай что-то невероятное

Экспериментируйте с AppMaster с бесплатной подпиской.
Как только вы будете готовы, вы сможете выбрать подходящий платный план.

Попробовать AppMaster