Привет, Хабр! Меня зовут Виктор, я Frontend-разработчик в Clevertec. Сейчас мы с командой разрабатываем веб-версию банкинга с использованием React. Чтобы дать пользователям доступ к функциям, привычным в нативных приложениях, и добавить новые, мы используем Web API. Подробно расскажу о них в этой статье и раскрою некоторые тонкости.

Web Share API

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

Web Share API имеет следующие возможности и ограничения:

  • Только сайты с протоколом HTTPS могут использовать Web Share API. Кроме того, для облегчения разработки API работает на localhost.

  • Может запускаться только по определенным действиям, например, на клик. Он не может быть запущен при загрузке страницы или через setTimeout().

  • С его помощью можно делиться URL-адресами, текстом или файлами.

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

Чтобы поделиться ссылками и текстом, используйте метод share() на основе промисов с обязательным объектом свойств. Чтобы браузер не генерировал TypeError, объект должен содержать хотя бы одно из следующих свойств: title, text, url или files.

К примеру, можно поделиться текстом без URL-адреса или наоборот. Указание всех трех параметров расширяет гибкость вариантов использования. Допустим, после выполнения приведенного ниже кода пользователь выбрал приложение электронной почты в качестве цели. Параметр title может стать темой письма, text — телом сообщения, а файлы — вложениями.

if(navigator.share) {
  navigator.share({
    title: 'web.dev',
    text: 'Открой для себя web.dev',
    url: 'https://web.dev/'
  })
    .then(() => console.log('Удалось поделиться'))
    .catch((error) => console.log('Не удалось поделиться', error));
}

Чтобы поделиться файлами, сначала проверьте и вызовите navigator.canShare(). Затем включите массив файлов в вызов navigator.share():

if(navigator.canShare && navigator.canShare({ files: filesArray })) {
  navigator.share({
    files: filesArray,
    title: 'web.dev',
    text: 'Открой для себя web.dev',
  })
    .then(() => console.log('Удалось поделиться'))
    .catch((error) => console.log('Не удалось поделиться', error));
} else {
  console.log('Ваша система не поддерживает обмен файлами');
}

Важно: в этом примере обнаружение функций реализуется путем проверки navigator.canShare(), а не navigator.share(). Объект данных, передаваемый в canShare(), поддерживает только свойство files. Если navigator.canShare() возвращает true, то возможность поделиться массивом файлов filesArray поддерживается.

Поддержка Web Share API на разных устройствах, операционных системах и браузерах на данный момент:

  • IOS: Safari, Chrome

  • Android: Chrome, Yandex, Edge

  • Windows: Chrome, Yandex, Edge, Opera

  • macOS: Safari

Не поддерживается:

  • Android: Firefox, Samsung browser, Opera (файлы шарить нельзя, только ссылки и текст)

  • Windows: Firefox

  • macOS: Chrome

Contact Picker API

API выбора контактов предоставляет пользователям простой способ поделиться контактами из своего списка. В поле ввода номера телефона или рядом с ним добавляется кнопка, при клике на которую открывается книга контактов устройства, из которой удобно выбрать нужный.

Чтобы проверить, поддерживается ли API выбора контактов, используйте:

const supported = ('contacts' in navigator && 'ContactsManager' in window)

На данный момент API поддерживается только на мобильных устройствах. На Android это Chrome, на IOS – Safari. Перед разработкой и тестированием API выбора контактов необходимо включить поддержку этой функции на устройстве. На Android он теперь доступен в Chrome 80 или более поздней версии по умолчанию.

Но если вы работаете с iOS, API выбора контактов считается экспериментальной функцией, и вам необходимо следовать этим инструкциям:

  1. Зайти в «Настройки», прокрутить вниз и выбрать Safari

  2. Прокрутить вниз, нажать «Дополнительно»

  3. Выбрать меню «Experimental Features и переключить Contact Picker API

У объекта contacts есть методы getProperties() и select(). Метод getProperties() возвращает свойства, доступные на пользовательском устройстве.

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

const supported = ('contacts' in navigator && 'ContactsManager' in window)

interface ContactsManager {
  getProperties: () => Promise<ContactProperty[]>;
}

enum ContactProperty {
  "address" = "address",
  "email" = "email",
  "icon" = "icon",
  "name" = "name",
  "tel" = "tel",
}

async function checkPropertiesSupport(): Promise<void> {
  try {
    const supportedProperties = await navigator.contacts.getProperties();
    setContactProperties(supportedProperties);
  } catch {
    console.warn(
      "This browser doesn't support the Contact Picker API"
    );
  }
}

Список поддерживаемых свойств будет возвращен в виде массива. Например в приведенном выше случае мы могли бы получить массив типа ["email", "name", "tel"].

Метод select() принимает два аргумента:

1. Массив свойств контакта, например ( ["email", "name", "tel"] )

2. Объект с параметром multiple, который может быть true или false. Если установлен в true, то можно выбрать несколько контактов за один раз.

interface ContactsManager {
  select: (
    properties: ContactProperty[],
    options?: ContactSelectOptions
  ) => Promise<ContactInfo[]>;
}

interface ContactInfo {
  address: Array<ContactAddress>;
  email: Array<string>;
  icon: Blob;
  name: Array<string>;
  tel: Array<string>;
};

async function selectContacts () {
  const contacts = await navigator.contacts.select(['name', 'tel'], { multiple: true });

  if (!contacts.length) {
    // нет выбранных контактов
    return;
  }

  return contacts;
}

selectContacts();

Безопасность использования API выбора контактов:

1. Доступ осуществляется только на уровне браузера.

2. Работает только по протоколу HTTPS.

3. Не может быть активировано программным способом. Пользователь должен разрешить выбор из контактов, как если бы это происходило в нативном приложении.

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

Media Capture and Streams API

Это API позволяет веб-приложениям получать доступ к возможностям захвата мультимедиа на устройстве. Например, можно активировать камеру, считать штрихкод и редиректнуть пользователя по определенной ссылке.

В наше приложение интегрирована система приёма электронных платежей, в которой можно оплатить что-либо, отсканировав QR-код. После нажатия на “Сканировать QR” открывается окошко с камерой. Далее камера обнаруживает QR-код – и пользователя редиректит в дерево электронных платежей.

Для реализации использовали библиотеку html5-qrcode. Под капотом она использует Media Capture and Streams API. Работает как на десктопных, так и на мобильных устройствах, поддерживается всеми основными браузерами. Достаточно легковесная – 56 Kb.

После импорта стартуем работу камеры девайса с помощью html5QrCode.start()

import { Html5Qrcode } from 'html5-qrcode';

const html5QrCode = new Html5Qrcode("reader");
html5QrCode.start(
  cameraId, 
  {
    fps: 10,
    qrbox: 250
  },
  qrCodeMessage => {
    // сделать что то когда код прочитан
  },
  errorMessage => {
    // если ошибка при чтении
  })
.catch(err => {
  // обработка ошибки
});

Этот метод возвращает промис с инициализацией сканирования QR-кода.

После проведения необходимых манипуляций останавливаем сканирование QR-кода с помощью метода html5QrCode.stop(), который возвращает промис для остановки сканирования.

html5QrCode.stop().then(ignore => {
  // остановлено сканирование QR-кода
}).catch(err => {
  // обработка ошибки
});

Саммари

Главное, что дают нам Web API – возможность предоставить бесшовный переход от использования нативного приложения к веб-версии. Если мобильное приложение недоступно – есть веб-версия, в которой благодаря технологиям мы обеспечиваем комфортную работу и максимально сокращаем шаги для выполнения рутинных действий.

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

Из планов на будущее: WEB OTP API

В финале решил поделиться планами. WEB OTP API мы хотим внедрить на проекте. Это позволит автоматически вставлять код из смс.

Для подтверждения оплаты на телефон приходит сообщение. Внизу экрана появляется модальное окно с подтверждением вставки. И после нажатия на “Разрешить” код будет подставлен в поле ввода.

Если у вас есть успешный опыт использования Web OTP API и знание нюансов – поделитесь в комментариях :)

Комментарии (27)


  1. dark0n01
    13.06.2024 07:33

    Спасибо за пост. Подскажите , а есть ли вообще web api для доступа к галерее? Подумываем запилить веб приложение для отработки фотографий.


    1. artptr86
      13.06.2024 07:33
      +4

      1. dark0n01
        13.06.2024 07:33

        Логично. Покопаем. Спасибо.


      1. ViRFront Автор
        13.06.2024 07:33
        +1

        с галереей на практике не приходилось пока работать, но судя по описанию да, это api наиболее подходящее для вашей задачи


    1. evgensenin
      13.06.2024 07:33
      +1

      Напишите потом, получилось ли читать фотки в iOS?


      1. dark0n01
        13.06.2024 07:33

        Да, конечно.


  1. achekalin
    13.06.2024 07:33
    +2

    А я вот, наоборот, не рад, когда web заменяет нативное. Во-первых, с точки зрения безопасности, во-вторых, с точки зрения (потенциально) кривых рук авторов.

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

    В общем, я лично за то, чтобы а) на уровне ОС был включатель, что в каждой аппликузе и в каждом веб-приложении можно включать/разрешать, и б) обязать по закону банки и прочих не думающих о пользователях товарищей иметь в своих приложенях/сайтах минимальный набор функций работающими без включения доступов всюду и везде.

    Честно - вот вижу, что веб-приложениям надо давать доступ только к FaceID/сканеру пальца, а куда-то дальше с очень большими проблемами и прямым разрешением юзера.


    1. StriganovSergey
      13.06.2024 07:33
      +2

      Поддерживаю, только замечу, что "прямое разрешение юзера" считаю не решением проблемы,
      а простым перекладыванием вины и ответственности на пользователя.
      Десятки приложений, и о каждом нужно юзеру заботиться.
      Все осознавать, и всегда помнить куда кому дал доступ и не пора ли этот доступ отозвать.
      А если что не так, то - "сам дурак".

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


    1. polRk
      13.06.2024 07:33
      +2

      Видимо, вы совершенно не разбираетесь в этой области… Во первых: веб - независимая платформа, а нативные приложения - контролируют плохие дяди, куда вас могут не пустить по цвету кожи или форме ушей (привет apple) . Во вторых - веб это кроссплатформенность + огромное количество стандартов. Третий пункт вытекает из второго - огромный выбор разных свобод при работе с веб, а не с нативными приложениями.

      Про faceid/touchid - это уже есть WebAuthn, называет passkey, работает прекрасно и безопасно.


  1. lanabel
    13.06.2024 07:33
    +3

    То есть в результате мне придётся дать разрешение читать мои контакты и смс не отдельному банку, а всему браузеру целиком? Спасибо, но может не надо? И пуши тоже будут в общие браузерные сыпаться... Про быструю авторизацию по пину тоже кажется можно забыть. Спасибо, я вспомнила, почему Сбер и Тинь суммарно отжирают у меня полтора гига места, а я не очень-то и жалуюсь (кроме случая когда на стареньком телефончике понадобилось сбер обновить, а он не поместился).


    1. dvsa
      13.06.2024 07:33
      +8

      Разве разрешения выдаются на весь браузер, а не строго на каждый сайт по отдельности?


    1. artptr86
      13.06.2024 07:33

      То есть в результате мне придётся дать разрешение читать мои контакты и смс не отдельному банку, а всему браузеру целиком?

      Разрешение дадите всему браузеру, да, но приложение не получит весь список контактов. Браузер предложит вам явно выбрать, какими контактами вы хотите с приложением поделиться. Демо работы Contact Picker API: https://contact-picker.glitch.me (работает сейчас только в гуглобраузерах под андроидом).


      1. ViRFront Автор
        13.06.2024 07:33

        так же работает и в iOS + Safari, пока в качестве экспериментальной функции (нужно включить в настройках)


  1. genikineg
    13.06.2024 07:33

    Сделай статью про Web OTP API и реакт


    1. ViRFront Автор
      13.06.2024 07:33

      как только внедрим, поработаю с ним на практике, обязательно напишу


    1. artptr86
      13.06.2024 07:33
      +4

      Какое отношение Реакт имеет к WebOTP? Напоминает вопрос про плагин для jQuery для сложения чисел.


      1. ViRFront Автор
        13.06.2024 07:33

        об этом в конце статьи, планируем вставлять код из смс с помощью него


        1. artptr86
          13.06.2024 07:33
          +3

          Да, я статью прочитал. Но Реакт тут по-прежнему ни при чём.


  1. Daseron
    13.06.2024 07:33

    Удручает что все эти API были и 5 лет. В далёком 2019 году я верил, что React заменит native, но сейчас понимаю что лучше использовать flutter.


    1. Reallamos
      13.06.2024 07:33

      Реакт в принципе лучше никому не использовать.

      Его единственное преимущество - простота освоения. Из за этого он собственно и юзается повсеместно.


      1. artptr86
        13.06.2024 07:33

        Его преимущество — популярность, поэтому он и требуется в компаниях. VueJS будет даже проще, а SolidJS вообще почти идентичен внешне Реакту, работает быстрее и меньше грузит браузер, но почти никому не нужен.


    1. avdosev
      13.06.2024 07:33

      Я поверхностно знаком с экосистемой реакта, но разве react.native не вполне себе рабочий инструмент? У него есть набор минусов, например, сложные анимации, но в целом какую то долю мобильной разработки он занимает.

      Конечно, реакт натив не совсем честный реакт, тк рендерит всё происходящее не в html, а нативно.

      В веб который заменит натив я тоже уже перестал верить, приложений которые так реально смогли можно пересчитать по пальцам руки фрезеровщика


  1. savostin
    13.06.2024 07:33

    А нет ли такого API чтобы все происходящее внутри div, анимашки там и двигание объектов, стримило как видео куда-нибудь?


    1. artptr86
      13.06.2024 07:33

      Кажется, есть только захват экрана/окна и захват канваса.


    1. ViRFront Автор
      13.06.2024 07:33

      хм... С таким пока не сталкивался, в любом случае можно посмотреть https://developer.mozilla.org/en-US/docs/Web/API и попробовать что то подобрать


    1. crispart
      13.06.2024 07:33

      Не совсем по запросу, но пара вариантов есть. Какой-то, может, подойдёт. Есть getDisplayMedia, а есть rrweb. Последний в Sentry используется, например, для скринкастов.