Contact Picker API позволяет предоставить сайту доступ к выбранным пользователем контактам из его записной книжки. В данной заметке мы разберём возможности, которые у нас появились, и некоторые, возможно, неочевидные моменты.
Что это и зачем?
Представьте: вы хотите пригласить своего друга на ваш любимый сайт; оформляете интернет-заказ и хотите указать контактный номер жены как получателя; вызываете такси для коллеги и указываете его номер как номер пассажира (да, я понимаю, что самый популярный кейс тут первый, но с остальными я также столкнулся за последний месяц) или ещё как-то и где-то указываете номер телефона из своей записной книжки. Что вы делаете? Сворачиваете браузер или приложение, открываете приложение «Телефон», откуда переходите в «Контакты», ищете нужную запись, заходите в неё, копируете номер, снова открываете браузер или приложение, выбираете поле для ввода, вставляете скопированную запись. Квест выполнен!
Contact Picker API позволяет пользователю достичь той же цели значительно быстрее: нажать на кнопку, найти нужную запись, выбрать её, нажать «Готово». Круто? Да. Сложно для реализации? Нет.
Где и как это работает?
В Android 6+ и Chrome 80+, а также за флагом в iOS 14.5 beta 2. Вы можете попробовать API в демо от разработчиков.
API
API включает в себя класс ContactsManager
и инстанс этого класса по ссылке window.navigator.contacts
.
Имеются два асинхронных метода: getProperties
, позволяющий узнать, какого рода информацию о контактах вообще может предоставить текущая связка браузера и операционной системы, и select
, как раз отображающий пользователю запрос на выбор данных из записной книжки.
В текущий момент черновик предложения спецификации говорит, что через API можно получить следующие типы полей: «address», «email», «icon», «name» и «tel». То есть прежде, чем просить пользователя поделиться информацией, скажем, о почте и изображении пользователя, хорошо было бы проверить, есть ли у пользователя такая возможность.
const supportedProps = await navigator.contacts.getProperties();
if (supportedProps.includes('address') && supportedProps.includes('icon')) {
// всё хорошо, показываем волшебную кнопку для выбора контактов
}
После этого мы можем с чистой совестью попросить пользователя выбрать контакт или контакты.
Метод navigator.contacts.select
первым аргументом принимает обязательный массив типов полей, которые нам нужны. Эта информация будет использована операционной системой для уведомления пользователя о том, чем именно он поделится. Те поля, которые вы не запросили ��вно, вы не получите. Вторым, опциональным аргументом можно передать объект дополнительных настроек. Сейчас это только multiple
(по умолчанию false
), т.е. один контакт можно будет выбрать пользователю, либо несколько.
В результате вызова вы получите массив (всегда, вне зависимости от значения multiple
) объектов-описаний контактов, где ключами будут запрошенные типы полей, а значениями — массивы их значений (всегда — массивы). Пример:
[{
"email": [],
"name": ["Queen O’Hearts"],
"tel": ["+1-206-555-1000", "+1-206-555-1111"]
}]
Поля «email», «name» и «tel» являются массивами строк. Для почты и телефона это логично. А вот если заполнить все поля, относящиеся к имени, которые предоставляет Android, то в качестве значения имени всё равно будет получен массив из лишь одной строки формата <обращение> <имя> <отчество> <фамилия>, <звание/титул>
. Поле «address» приходит как массив объектов, соответствующих интерфейсу ContactAddress
, расширяющему (но ничего не добавляющему) интерфейс PaymentAddress
. Поле «icon» — как массив Blob-ов.
Теперь мы можем использовать эту информацию по своему усмотрению.
Что может пойти не так
Как можно было заметить выше, нам всегда приходят массивы значений. Это потому, что у контакта может быть записано как несколько номеров телефонов, так и ни одного. Это всегда нужно будет проверять и предусматривать в дизайне перед дальнейшим использованием.
Что ещё может пойти не так? Пользователь банально может нажать кнопку «Назад», не выбрав контакты. При этом результат вызова всё равно будет успешным и вы получите пустой массив.
В случае, если у приложения нет доступа к списку контактов (запрет на уровне системы), либо операционная система не поддерживает данную возможность, вы получите TypeError: Unable to open a contact selector.
Определение поддержки
Авторы рекомендуют проверять наличие поддержки таким образом:
const supported = ('contacts' in navigator && 'ContactsManager' in window);
Но дополнительно, возможно, стоит проверить, а не Android 5- ли сейчас перед вами. Сделать это можно по юзерагенту. Если процент пользователей этих версий мал, можно ограничиться общим отловом ошибок вида Unable to open a contact selector.
Безопасность и предоставление доступа
При первом обращении к этому API, если браузеру не был предоставлен доступ к контактам ранее, система запросит доступ для приложения браузера. В случае разрешения в последующие разы никаких дополнительных запросов не будет. Обратите внимание, что это совсем не похоже на привычную схему получения доступа сайтов к специфичным API. Если обычно мы делаем запрос для текущего ориджина, то есть можем запретить и разрешить доступ, например, к камере, индивидуально для каждого сайта, то тут вы автоматически делаете запрос для всего приложения, никаких дополнительных индивидуальных запросов на каждый ориджин вы делать не будете. А соответственно, вы не можете узнать статус разрешения данного API через Permission API, в любом случае придётся пробовать и обрабатывать исключения.
Почему это хорошо и правильно в данном случае? Несколько причин. Первая: для вызова метода navigator.contacts.select
необходимо явное действие пользователя, вызвать программно, например, сразу после загрузки страницы не получится. Вторая: интерфейс выбора пользователя, судя по всему, относится именно к самой ОС, и у скриптов сайтов нет никакой возможности вмешаться в выбор пользователя. А значит, нет никакого шанса получить список контактов пользователя без его спроса. Каждый раз, когда пользователь наткнётся на интерфейс выбора контактов, это будет следствием его явного действия, и ему первым делом будет выдано пояснение, какой именно сайт и какую именно информацию получит, если пользователь решит продолжить.
Особенности дизайна
Что нужно предусмотреть:
- наличие поддержки API;
- возможные ошибки, связанные с доступом к контактам или поддержкой со стороны ОС;
- вариант, когда пользователь передумает и не выберет контакты;
- вариант, когда желаемых данных у контакта не будет или наоборот, будет несколько (к примеру, два телефона или три почты).
Поддержка API в Chrome на других операционных системах
В настоящий момент данное API доступно только на Android. Потенциально в будущем оно может быть доступно и на других ОС, например, в Windows 10 для реализации Contact Picker API есть подходящее API Windows.ApplicationModel.Contacts
, а в macOS — API Contacts
. Но на данный момент никаких заявлений от разработчиков Chrome по поводу поддержки других ОС я не встречал.
Другие браузеры
Скорее всего все Chroimum-based-браузеры на Android автоматически получат это API после обновления движка до определённой версии, хоть это и не точно. Лично у меня пока есть небольшие сомнения насчёт браузеров Samsung Internet и Miui, они иногда имеют некоторые особенности.
Firefox пока не определился по поводу своей официальной позиции относительно этого API, но на настоящий момент отношение положительное. UPD: Firefox принял решение не реализовывать данное API в ближайшее время.
Ожидать какой-то реакции или, тем более, поддержки со стороны Safari не стоит, тем более что некая аналогичная базовая функциональность там уже есть, и реализована она просто как автозаполнение полей форм. Вот демо для вашего Safari. UPD: Внезапно, данное API без объявления войны было имплементировано за флагом в iOS 14.5 beta 2 и уже находится во вполне работоспособном состоянии. Когда фича будет доступна всем пользователям спрогнозировать сложно, остаётся только ждать. Работает API в самом Safari и в режиме рабочего стола, в альтернативных браузерах — не доступно. В десктопном Safari Technology Preview тоже имеется такой флаг, но попытка его использовать приводит к исключению.
Заключение
Данное API — ещё один шаг Проекта Фугу по направлению к сокращению разрыва между вебом и нативными приложениями. Разумеется, не получится написать альтернативное приложение для управления контактами, но большинство сценариев использования, как мне кажется, оно покрывает и делает вашего пользователя немного счастливее. Напишите свои мысли по этому поводу или поделитесь возможными сценариями использования!