Universal Links в iOS на практике
Ожидает приглашения
Практически год прошел с WWDC 2015, где инженеры компании Apple представили новый подход к Deep Linking, но в сети так и не появилось хороших статей на этот счет — сейчас буду пробовать это исправить.
В iOS 9 была добавлена поддержка перехода в приложение по веб URL с http:// и https:// схемами — Seamless Linking (читаем как “бесшовные ссылки”). Т.е. пользователь просто нажимает на обычную ссылку на сайт, а попадает в приложение без открытия Safari. Если же приложения нет, то откроется браузер. Браузер же можно открыть и из приложения по нажатию кнопки, которая появится вместо индикатора заряда. Также можно открыть эту ссылку из самого приложения вызовом openURL. Более того, Вы можете указать какие разделы сайта в каком приложении открывать — и для всего этого не придется писать ни одной строки кода на сервере. Далее я подробно объясню, как все это настроить и избежать возможных граблей.
Т.к. зачастую у серверных разработчиков и «без нас куча дел» никто скорее всего помогать настраивать Applinks не будет — и не нужно, мы справимся без них. Я знаю два неплохих варианта, где можно получить себе домен и закинуть apple-app-site-association не заплатив ни цента: GitHub Pages и Google Cloud Platform. Очень интересно послушать другие варианты, но пока мы выбираем первый, т.к. тут будет всем привычный гитхаб, система контроля версий и, скорее всего, аккаунт в наличии.
Как создать такой репозиторий я писать не буду, инструкция очень проста (https://pages.github.com). На выхлопе я получаю пустой сайт по адресу bernikowich.github.io.
Создаем проект и выбираем как-нибудь Bundle Identifier, у меня это bernikowich.UniversalLinks. Выбираем команду с действующей подпиской на Apple Developer Program, в которой создастся Application ID в Member Center.

Далее открываем в настройках таргета Capabilities и включаем Assosiated Domains. На этом этапе в Member Center создастся AppID с указанным ранее Bundle Identifier. Далее указываем наш домен в таком формате applinks:{домен}, у меня получается applinks:bernikowich.github.io.

Теперь самое интересное, нужно создать файл для нашего сайта, из которого Apple будет доставать BundleID, пути по которым нужно открывать приложения и т.д… Сейчас я покажу самый простой вариант для этого:
Интересны тут всего 3 вещи:
1) Значение по ключу «apps» должно присутствовать и должно быть пустым массивом.
2) «appID» я получил из Member Center (далее будет скриншот где явно видно откуда взялась строка 3EFP2B895H и bernikovich.UniversalLinks.
3) «paths» определяет пути, по которым iOS будет открывать вместо сайта приложение с заданным appID.
Далее мы сохраняем это файл в корень репозитория сайта. Обратите внимание на то, что у файла нет расширения — просто apple-app-site-association. Пушим все на github.
Сейчас мы возвращаемся к Xcode проекту. Найдите файл имплементации делегата (по умолчанию: AppDelegate.m) и добавьте там следующий метод:
Сейчас мы лишь добавили метод, который будет вызван после запуска приложения (или перехода в foreground). Метод выведет путь из линки. Для обработки URL'ов советую пользоваться NSURLComponents, который был представлен в iOS 7.
Отправьте себе на почту или сохраните в заметках ссылку с со своим доменом, например такую: bernikowich.github.io/index.php. Теперь можете установить и запустить приложение на девайсе (Applinks не будет работать на симуляторе!). Если вы сделали все верно, то после проделанных действий у вас должно открываться приложение по нажатию на ссылку из почты или заметок, при это в логах будет что-то типа:
Что если открывать в приложении нужно лишь некоторые разделы или вовсе отдельные страницы? Для всего этого есть специальные символы в разметке:
Обратите внимание, что пути чувствительны к регистру.
Немного тонкостей. Задача следующая: нужно открывать в приложении видео (/videos/cats/video1), но не открывать разделы (/videos/cats). Пишем в файле apple-app-site-association такой путь:
Казалось бы все ОК, но не тут то было. Пути формата /videos/2014 все равно открываются в приложении. Решилось все добавлением в конце еще одного слеша:
Объяснения этому пока не найдено, но возможно решение поможет кому-то еще.
Universal Links отличная вещь, уход от «швов» делает переходы к приложению по ссылке намного проще и красивее. Общие ссылки на сайты и приложения определенно шаг вперед, а подход к определению ответственных приложений защищен от недобросовестных разработчиков.
Что же нового придумали в Apple?
В iOS 9 была добавлена поддержка перехода в приложение по веб URL с http:// и https:// схемами — Seamless Linking (читаем как “бесшовные ссылки”). Т.е. пользователь просто нажимает на обычную ссылку на сайт, а попадает в приложение без открытия Safari. Если же приложения нет, то откроется браузер. Браузер же можно открыть и из приложения по нажатию кнопки, которая появится вместо индикатора заряда. Также можно открыть эту ссылку из самого приложения вызовом openURL. Более того, Вы можете указать какие разделы сайта в каком приложении открывать — и для всего этого не придется писать ни одной строки кода на сервере. Далее я подробно объясню, как все это настроить и избежать возможных граблей.
Часть 1. Cайт для тестирования
Т.к. зачастую у серверных разработчиков и «без нас куча дел» никто скорее всего помогать настраивать Applinks не будет — и не нужно, мы справимся без них. Я знаю два неплохих варианта, где можно получить себе домен и закинуть apple-app-site-association не заплатив ни цента: GitHub Pages и Google Cloud Platform. Очень интересно послушать другие варианты, но пока мы выбираем первый, т.к. тут будет всем привычный гитхаб, система контроля версий и, скорее всего, аккаунт в наличии.
Как создать такой репозиторий я писать не буду, инструкция очень проста (https://pages.github.com). На выхлопе я получаю пустой сайт по адресу bernikowich.github.io.
Часть 2. Настройка проекта
Создаем проект и выбираем как-нибудь Bundle Identifier, у меня это bernikowich.UniversalLinks. Выбираем команду с действующей подпиской на Apple Developer Program, в которой создастся Application ID в Member Center.

Далее открываем в настройках таргета Capabilities и включаем Assosiated Domains. На этом этапе в Member Center создастся AppID с указанным ранее Bundle Identifier. Далее указываем наш домен в таком формате applinks:{домен}, у меня получается applinks:bernikowich.github.io.

Часть 3. Самый простой файл apple-app-site-association
Теперь самое интересное, нужно создать файл для нашего сайта, из которого Apple будет доставать BundleID, пути по которым нужно открывать приложения и т.д… Сейчас я покажу самый простой вариант для этого:
{
"applinks": {
"apps": [],
"details": [
{
"appID": "3EFP2B895H.bernikovich.UniversalLinks",
"paths": [
"*"
]
}
]
}
}
Интересны тут всего 3 вещи:
1) Значение по ключу «apps» должно присутствовать и должно быть пустым массивом.
2) «appID» я получил из Member Center (далее будет скриншот где явно видно откуда взялась строка 3EFP2B895H и bernikovich.UniversalLinks.
3) «paths» определяет пути, по которым iOS будет открывать вместо сайта приложение с заданным appID.
Далее мы сохраняем это файл в корень репозитория сайта. Обратите внимание на то, что у файла нет расширения — просто apple-app-site-association. Пушим все на github.
Часть 4. Обработка URL'ов
Сейчас мы возвращаемся к Xcode проекту. Найдите файл имплементации делегата (по умолчанию: AppDelegate.m) и добавьте там следующий метод:
- (BOOL)application:(UIApplication *)application continueUserActivity:(NSUserActivity *)userActivity restorationHandler:(void (^)(NSArray * _Nullable))restorationHandler
{
if ([userActivity.activityType isEqualToString:NSUserActivityTypeBrowsingWeb]) {
NSURLComponents *URLComponents = [NSURLComponents componentsWithURL:userActivity.webpageURL resolvingAgainstBaseURL:YES];
NSLog(@"%@", URLComponents.path);
}
return YES;
}
Сейчас мы лишь добавили метод, который будет вызван после запуска приложения (или перехода в foreground). Метод выведет путь из линки. Для обработки URL'ов советую пользоваться NSURLComponents, который был представлен в iOS 7.
Отправьте себе на почту или сохраните в заметках ссылку с со своим доменом, например такую: bernikowich.github.io/index.php. Теперь можете установить и запустить приложение на девайсе (Applinks не будет работать на симуляторе!). Если вы сделали все верно, то после проделанных действий у вас должно открываться приложение по нажатию на ссылку из почты или заметок, при это в логах будет что-то типа:
2016-03-04 15:39:04.898 UniversalLinks[619:169698] /index.html
Часть 5. Интересности с apple-app-site-association
Что если открывать в приложении нужно лишь некоторые разделы или вовсе отдельные страницы? Для всего этого есть специальные символы в разметке:
- Если приложение поддерживает все разделы сайта используйте просто *
- Указать определенную линку можно написав /news/latest/
- Указать раздел можно используя * как символ определяющий любую подстроку. Например /*/latest/ либо /videos/*
- Для подстановки любого одного символа ?. Пример: /videos/201?/
Обратите внимание, что пути чувствительны к регистру.
Немного тонкостей. Задача следующая: нужно открывать в приложении видео (/videos/cats/video1), но не открывать разделы (/videos/cats). Пишем в файле apple-app-site-association такой путь:
"/videos/*/*"
Казалось бы все ОК, но не тут то было. Пути формата /videos/2014 все равно открываются в приложении. Решилось все добавлением в конце еще одного слеша:
"/videos/*/*/"
Объяснения этому пока не найдено, но возможно решение поможет кому-то еще.
Выводы
Universal Links отличная вещь, уход от «швов» делает переходы к приложению по ссылке намного проще и красивее. Общие ссылки на сайты и приложения определенно шаг вперед, а подход к определению ответственных приложений защищен от недобросовестных разработчиков.