Как реализовать технологии глубоких ссылок для приложений iOS React Native

Универсальные ссылки и схемы URL - это две функции iOS, которые позволяют разработчикам настраивать ссылки, которые переходят прямо в приложение на определенном экране, независимо от того, насколько глубоко в приложении. В этой статье описываются различия между этими двумя функциями и способы их интеграции в проекты Xcode и React Native, включая извлеченные проекты Expo, с описанием вариантов использования между двумя методами глубинных ссылок. Также будут задокументированы полезные пакеты React Native для облегчения интеграции.

Краткая история глубинных ссылок в iOS

Глубокие ссылки - это довольно общий термин, который охватывает идею перехода к определенному проиндексированному фрагменту материала, будь то веб-страница или определенное место в приложении. Эта концепция практиковалась с момента появления HTTP и гиперссылки и оказалась неотъемлемой частью навигации по веб-сайтам в браузере.

В последнее время эта концепция была принята для использования с приложениями, поскольку их сложность и возможности выросли. Возможность перехода к определенному состоянию в приложении особенно полезна для приложений iOS, которые существуют в виде выделенных ресурсов изолированной программной среды, также известный как «огороженный сад» подход Apple к приложениям. Поэтому Apple приняла Deep Linking как ответ на прыжок в приложение, поддерживая эти механизмы безопасности.

Глубинные ссылки были куплены на iOS очень рано

Глубокие ссылки для iOS сначала были представлены в форме схем URL, что на самом деле означает термин «глубокие ссылки» в iOS. mailto://<address> - это обычная схема URL-адресов, которая переходит в почтовое приложение по умолчанию рассматриваемого устройства. facetime://<email> и facetime-audio://<email> - другие примеры, относящиеся к iOS. Схемы URL-адресов фактически используются в iOS с 2008 года и играют ключевую роль в навигации между приложениями.

Просмотрите архив документации Apple по схемам URL, чтобы увидеть все схемы, которые они реализовали. Также ознакомьтесь с поддерживаемыми схемами URL-адресов в React Native.

Что более актуально для разработчиков, так это возможность определять настраиваемые схемы URL, которые конкретно ссылаются на их приложение. Это делается в Xcode через оптимизированный графический интерфейс, поэтому вы можете перейти к своему приложению с его конкретным именем, например схемой URL-адресов myappname://. Отсюда ваша схема URL-адресов может быть расширена, чтобы указывать на определенное место в вашем приложении:

# URL Schemes can simply navigate to a particular screen
myapp://settings 
# or a "deeper" state of the app
myapp://blog/latest/page/5

Эти ссылки работают в приложениях iOS, таких как Notes, а также в браузере.

Но где React Native вписывается в это решение? Ответ кроется в Linking API, встроенной ссылке на схемы URL, на которые можно подписаться через прослушиватель событий, который отслеживает входящие события URL. Затем этот прослушиватель событий может обработать это событие соответствующим образом, например, перейти к определенному экрану с помощью react-navigation или других средств маршрутизации. Мы узнаем, как именно это делается, ниже.

Схемы URL-адресов были первой попыткой создания глубоких ссылок для приложений iOS. Позже появились универсальные ссылки, которые связывают реальное доменное имя с приложением.

Универсальные ссылки используют схему URL-адресов веб-сайтов для создания глубинных ссылок на приложение.

Универсальные ссылки были представлены в 2015 году в iOS 9 как превосходное средство для создания прямых ссылок в приложение. Связывая доменное имя (которое должно принадлежать разработчику или компании-разработчику приложения) с приложением и используя его схему URL-адресов для перехода к определенным точкам приложения. Таким образом сайт и приложение синхронизируются с точки зрения доставки контента.

Чтобы связать домен с приложением, необходима некоторая конфигурация Xcode, а также метод appdelegate.m для обработки входящих событий URL. Не только это, но и файл JSON, называемый файлом A pple App Site Association, должен быть настроен и размещен на соответствующем поддерживаемом доменном имени по адресу https://mydomain/.well-known/apple-app-site-association. В этом файле мы можем настроить определенные шаблоны URL-адресов для глубокой ссылки на приложение - вам не нужно поддерживать все шаблоны домена для веб-версии, даже если вы могли бы использовать подстановочный знак (подробнее об этом позже).

Это может показаться сложным решением, но на практике оно довольно простое, особенно когда используются функции автоматической подготовки Xcode. Мы также расскажем, как именно интегрировать универсальные ссылки, далее в статье.

Схемы URL-адресов и универсальные ссылки можно использовать вместе, хотя Apple рекомендует использовать универсальные ссылки в качестве стандартных средств глубинных ссылок. Этот совет оправдан, если у вас действительно есть аналог вашего приложения на веб-сайте, при этом ваше приложение содержит сложную схему URL-адресов, которая также используется для других версий приложения, таких как аналог Android. Если вы хотите предложить пользователю перейти в приложение в определенных частях вашего веб-сайта, универсальные ссылки также являются предпочтительным решением для создания прямых ссылок.

Универсальные ссылки предложат пользователю открыть приложение через подзаголовок, который вы, возможно, заметили в iOS Safari, где у вас установлено соответствующее приложение. Вот пример моего приложения для проверки грамматики китайского языка:

Если в нативном приложении на устройстве, на котором оно установлено, задействована универсальная ссылка, пользователь перейдет непосредственно в приложение.

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

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

Настройка схемы URL-адресов

В этом разделе описаны этапы настройки реализации схемы URL-адресов для вашего приложения.

Первый шаг - определить само название схемы; это сделано в Xcode. Выбрав проект в навигаторе проекта, перейдите на вкладку Информация и прокрутите вниз до Типы URL. Именно здесь определяются схемы URL:

  • В поле Идентификатор обычно вводят идентификатор пакета вашего приложения (в формате com.domain.app).
  • В поле Схемы URL введите имя самой схемы, которая действует как префикс URL.
  • Оставьте роль Редактора, так как ваше приложение отвечает за определение этой схемы URL. Если вы перенимаете схему URL из другого приложения, измените это значение на Viewer.

Вот общий тип URL-адреса, который был предварительно заполнен в Xcode:

Официальную документацию Apple по регистрации схем URL-адресов можно найти здесь.

Теперь в файле your appDelegate.m включите следующий метод для обработки входящих URL-адресов со ссылкой на toRCTLinkingManager, предоставленный React Native:

#import <React/RCTLinkingManager.h>
// ...
#pragma mark - Handling URLs
- (BOOL)application:(UIApplication *)application openURL:(NSURL *)url
  sourceApplication:(NSString *)sourceApplication annotation:(id)annotation
{
  return [RCTLinkingManager 
           application:application openURL:url
           sourceApplication:sourceApplication 
           annotation:annotation
         ];
}
// ...

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

В разрабатываемых Expo-проектах используется схема URL-адресов exp: //

В настоящее время важно отметить, что когда ваше приложение работает в среде разработки Expo, например. перед публикацией и сборкой вашего приложения для TestFlight используется exp:// Схема URL; мы докажем, что это так, в следующем разделе, когда будем отслеживать входящие события URL в React Native.

По этой причине может быть полезно поддерживать несколько схем URL при мониторинге событий URL в среде разработки - и это именно то, что будет задокументировано в следующем разделе с помощью вспомогательного пакета, доступного в NPM.

Изменение схемы URL-адресов проекта Expo

Если вы переходите из удаленного проекта Expo, вы, возможно, заметили, что для вашего приложения уже существует схема URL-адресов - это была автоматически сгенерированная строка, которую Expo настроил для вас. Скорее всего, вы не хотите использовать эту схему URL-адресов и хотели бы более удобочитаемую и удобную альтернативу.

Полные инструкции - включая то, как установить другую схему - были задокументированы в Официальной документации Expo. По крайней мере, на стороне iOS процесс замены схемы URL-адресов довольно прост:

  • Измените поле scheme в app.json на новое имя схемы.
  • Измените значение первого ofCFBundleURLSchemes вхождения в вашем файле plist на ios/<your-project-name>/Supporting/Info.plist.

После этого вы можете удалить предыдущий тип URL-адреса в Xcode. Чтобы удалить тип URL-адреса, перейдите в свой файл info.plist, найдите клавишу Типы URL-адресов и нажмите значок «минус» (-) соответствующего типа URL-адреса.

Теперь, если вы создадите свой проект и установите его из TestFlight, вы сможете протестировать свою схему URL-адресов в действии - без какой-либо настройки на стороне React Native. Самый простой способ сделать это - просто ввести URL-адрес в приложении Notes. Щелчок по этому URL-адресу должен немедленно вывести ваше приложение на передний план в его текущем состоянии.

# testing a URL with your URL scheme
<url_scheme>://testing

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

И схемы URL, и универсальные ссылки отслеживаются с помощью Linking API React Native. Единственное отличие будет заключаться в самом URL.

Мониторинг URL-событий в React Native

Как уже было установлено, Linking API React Native предоставляет нам доступ к событиям URL с глубокими ссылками. Для облегчения отслеживания этих событий можно использовать пакет react-native-deep-linking. В качестве единственной зависимости для того, чтобы это работало, установите его с помощью пряжи:

yarn add react-native-deep-linking

react-native-deep-linking обеспечивает уровень абстракции для настройки и управления входящими URL-событиями, экономя время и дополнительную ненужную сложность.

Мы будем использовать 3 метода из импортированного объекта DeepLinking:

  • DeepLinking.addScheme для хранения каждой схемы URL-адресов, поддерживаемых вашим приложением.
  • DeepLinking.evaluateUrl(url) для передачи URL-адреса в DeepLinking контроллер маршрута.
  • DeepLinking.route(route, response => {}) для обработки каждого маршрута, который мы ожидаем от URL.

Эти API-интерфейсы могут использоваться вместе с Linking и useEffect, чтобы функциональные компоненты могли обрабатывать эти входящие URL-адреса. Поскольку наше приложение может поддерживать несколько схем URL-адресов, таких как exp:// в среде разработки Expo, разумно определить эти схемы как глобальные константы:

// constants.js
export const URL_SCHEMES = [
  'exp://',
  'myapp://',
];

Теперь их можно импортировать в компонент (ы), где будут отслеживаться события URL. Вот решение, которое ищет один маршрут, /settings, и переходит на экран настроек, если он совпадает. Кроме того, прослушиватель событий Linking управляется через useEffect:

...
import { URL_SCHEMES } from './constants'
import DeepLinking from 'react-native-deep-linking'
export const App = (props) => {
  // add URL schemes to `DeepLinking`
  for (let scheme of URL_SCHEMES) {
    DeepLinking.addScheme(scheme);
  }
  // configure a route, in this case, a simple Settings route
  DeepLinking.addRoute('/settings', (response) => {
    navigation.navigate("Settings");
  });
  // manage Linking event listener with useEffect
  useEffect(() => {
   Linking.addEventListener('url', handleOpenURL);
    return (() => {
      Linking.removeEventListener('url', handleOpenURL);
    })
  }, []);
  // evaluate every incoming URL
  const handleOpenURL = (event) => {
    DeepLinking.evaluateUrl(event.url);
  }
  return(
    <Text>App</Text>
  );
}

Используя ловушку useEffect вместе с объектами Linking и DeepLinking, мы можем относительно легко построить логику обработки событий.

Теперь, когда есть средства отслеживания входящих URL-адресов, давайте перейдем к настройке универсальных ссылок.

Настройка универсальных ссылок

В отличие от того, что вас могут представить в некоторых статьях, конфигурация универсальных ссылок не более сложна, чем схемы URL-адресов, с единственной дополнительной сложностью, связанной с файлом JSON, который должен находиться на вашем сервере, размещенном в поддерживаемом вами домене. В дополнение к этому требуется небольшая настройка Xcode.

Метод делегата приложения универсальных ссылок

Для работы универсальных ссылок требуется дополнительный appDelegate.m метод.

Вернитесь к Xcode и скопируйте следующий код под предыдущим методом схемы URL в разделе #pragma mark — Handling URLs, который также снова возвращает RCTLinkingManager, маршрутизирующий события URL в Linking API React Native:

// Universal Links
- (BOOL)application:(UIApplication *)application continueUserActivity:(NSUserActivity *)userActivity
 restorationHandler:(void (^)(NSArray<id<UIUserActivityRestoring>> * _Nullable))restorationHandler
{
  return [RCTLinkingManager 
            application:application
            continueUserActivity:userActivity
            restorationHandler:restorationHandler
         ];
}

Добавление прав на связанные домены

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

В верхнем пустом поле, которое появляется после добавления этого права, вставьте applinks:, а затем ваше доменное имя. Вот конфигурация приложения, с которым я работаю, которое поддерживает домен chinesegrammarapp.com:

Префикс applinks: является намеком на Xcode, что этот домен используется для универсального связывания, и является одним из немногих префиксов, которые Xcode распознает в зависимости от технологии, которую вы реализуете. webcredentials: - еще один пример.

Если вы сейчас перейдете в свои профили Provisioning Profiles в своей учетной записи Apple Developer, для вашего приложения должны быть включены связанные домены. Это необязательная проверка, но она подтверждает, что право на связанные домены было добавлено на стороне сервера:

Добавление файла AASA (Apple App Site Association)

Последний этап настройки универсальных ссылок - это добавление файла JSON на ваш сервер, который будет обслуживаться через ваш поддерживаемый домен. Apple предоставляет обширную документацию по настройке, но структура относительно проста.

На момент написания структура iOS 13 файла AASA, найденного здесь, не проверяет мои попытки поддержать ее и не распознается как конфигурация универсальных ссылок. Поэтому я рекомендую придерживаться формата файла iOS 12 и ниже. Это действительно формат, описанный в документации Apple. Это может быть запутанным конфликтом, особенно с теми, кто вроде меня стремится поддерживать новейшие стандарты. Однако пока формат iOS 12 и ниже работает и в iOS 13.

Имея в виду вышеизложенное, вот файл AASA, который поддерживает ссылку на настройки, а также страницу продукта, которая будет принимать произвольное значение через подстановочный знак:

{
    "applinks": {
        "apps": [],
        "details": [{
            "appID": "<TeamID>.com.example.myapp",
            "paths": ["/settings", "product/*]
            }
        ]
    }
}

Здесь нужно обратить внимание только на пару вещей:

  • Поле appID ожидает, что ваш идентификатор группы (находится здесь на странице членства) будет префиксом вашего идентификатора пакета приложений.
  • Каждый URL-адрес настраивается в массиве details, причем каждый элемент в этом массиве является объектом с appID и массивом paths каждого поддерживаемого URI. Подстановочный знак (*) может использоваться для поддержки уникальных идентификаторов в URI.
  • Поле apps необходимо оставить как пустой массив.

Теперь просто добавьте файл в хорошо известную папку на верхнем уровне вашего домена, чтобы файл был размещен по адресу:

https://your-domain.com/.well-known/apple-app-site-association

Обратите внимание, что вы должны работать по HTTPS, без перенаправлений. Также не добавляйте .json в конце имени файла. Отсюда переустановите приложение, чтобы получить конфигурацию AASA. Это сохранит конфигурацию как метаданные на устройстве.

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

Вернемся к React Native. Теперь с помощью Linking мы можем проверить, вызвала ли универсальная ссылка запуск приложения.

Это делается с помощью Linking.getInitialURL() API и может быть вызвано в useEffect при начальном рендеринге компонента. Исходный URL-адрес, если он существует, может рассматриваться как любой другой входящий URL-адрес. Стандартный способ проверки исходных ссылок - через AppState:

// checking if an initialURL is present
const [initialised, setInitialised] = useState(false);
useEffect(() => {
  AppState.addEventListener('change', handleAppStateChange);
  if (Linking.getInitialURL() !== null) {
    AppState.removeEventListener('change', handleAppStateChange);        
  }
}, []);
const handleAppStateChange = async (event) => {
  const initial = await Linking.getInitialURL();
  
  if (initial !== null && !initialised) {
    setInitialised(true);
    // app was opened by a Universal Link
    // custom setup dependant on URL...
  }
}

Обратите внимание, что initialised useState также использовался для предотвращения повторного выполнения пользовательской настройки каждый раз, когда приложение выходит на передний план. Теперь, когда ваши AppState прослушиватели событий обрабатывают initialURL(), универсальные ссылки можно тестировать как в браузере, так и в собственных приложениях.

Linking также содержит методы для открытия URL-адресов и открытия настроек приложения с методами openURL() и openSettings() соответственно.

Другой URL выставки Попался: exps: //

Если вы проверяете URL-адреса, имейте в виду, что Expo переформатирует исходный URL-адрес с префиксом exps:// в режиме разработки, заменив https://. Это можно проверить, зарегистрировав исходный URL-адрес в обработчике событий AppState:

const handleAppStateChange = async (event) => {
  const initial = await Linking.getInitialURL();
  console.log(initial);
}

Не является серьезной проблемой, но может привести к ошибкам в вашем коде.

Устранение неполадок универсальных ссылок

В целом, универсальные ссылки не так уж и сложно реализовать, хотя бывают случаи, когда вы можете чесать голову, если они не работают.

Прежде чем предположить, что в вашей конфигурации есть ошибка, примите во внимание следующие важные моменты:

  • Обновление файла Apple App Site Association (который с этого момента будет называться AASA) на устройстве может занять до 48 часов, в течение которых самые последние URL-адреса работать не будут.
  • После обновления универсальных ссылок удалите приложение и переустановите его. Во время установки ваше приложение получает конфигурацию AASA и сохраняет ее как метаданные приложения на устройстве.
  • Чтобы убедиться, что URL-адрес работает, скопируйте его в приложение Notes и нажмите и удерживайте, чтобы открыть контекстное меню. Верхняя ссылка должна быть Открыть в ‹Отображаемое имя приложения›. В дополнение к этому, нажатие на ссылку должно открывать ваше приложение напрямую.

Инструмент проверки API поиска приложений

Размещенный Apple Инструмент проверки API поиска приложений является самым простым средством проверки файла AASA среди других метаданных, содержащихся на ваших веб-страницах. Просто не забудьте указать URL-адрес, который настроен в вашем файле AASA, а не URL-адрес, который не настроен.

Инструмент проверки API поиска приложений всегда выбирает действующую версию вашего AASA, поэтому вам не нужно беспокоиться о том, что он получит кешированные версии.

Системная диагностика

Если у вас возникли проблемы, которые вы не можете понять, вы можете перейти к более подробной отладке с помощью инструмента sysdiagnose для iOS. Этот инструмент сможет точно регистрировать, какая конфигурация AASA в настоящее время находится на вашем устройстве.

Другие вопросы

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

Сказать это, у меня пока не возникало проблем с интеграцией универсальных ссылок!

Резюме

Эта статья была нацелена на документирование реализации механизмов Deep Linking для iOS, относящихся к схемам URL и универсальным ссылкам. Мы рассмотрели преимущества обеих технологий и выяснили, где их использовать. Затем мы рассмотрели, как реализовать обе технологии и как получать события URL в React Native.

React Native довольно гибок в том, как вы обрабатываете входящие URL-события; вы можете реагировать на них внутри компонентов так же, как на fetch ответы или прямую трансляцию. В дополнение к этому вы также можете выбрать «начальный маршрут» через Linking API, который проверяет, было ли приложение открыто через URL-схему или универсальную ссылку. Если бы это было так, вы могли бы отобразить настраиваемый целевой экран или перейти к другому экрану по умолчанию.

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