URLProtocol: как превратить в URL всё что угодно

Публикуем второй доклад из серии митапа VK Tech Talks | iOS, в котором разработчик Алексей Цветков-Омеличев рассказывает, как сделать URLProtocol, зачем вообще его использовать, и как его используют в приложениях «ВКонтакте»

URLProtocol в повседневной жизни используется для сайтов с протоколами http и https, однако его возможности выходят за пределы веб-навигации. Возможно, когда-нибудь вы видели magnet-ссылки для торрентов, или же tg-ссылки для открытия контента в приложении Telegram. Как заметил разработчик ВКонтакте Алексей Цветков-Омеличев, это очень удобная вещь. О том, как использовать URLProtocol для обмена данными внутри приложения и между приложениями, и что можно с ним сделать, читайте далее.

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

URLProtocol: как превратить в URL всё что угодно, image #1

Разные объекты ведут себя по-разному: есть объекты, которые выполняют какую-то важную функцию (например, обрабатывают изображения, входят в сеть), другие имеют описательный характер, которые можно уложить в URL. Что подводит к выводу, что всё вокруг нас — URL. При желании характеристику любого объекта (автомобиля, здания, животных и т. д.) можно уложить в некоторый URL.

URLProtocol: как превратить в URL всё что угодно, image #2

Полтора года назад, когда разрабатывался новый раздел сообщений в приложении «ВКонтакте», использовался простой класс URLImageView, который содержал метод render. Данный метод умел следующее: ему передавали URL, он с помощью какой-то сессии выполнял запрос к серверу, к определённой картинке, загружал её, обрабатывал данные и устанавливал свойства Image, таким образом отрисовывал.

URLProtocol: как превратить в URL всё что угодно, image #3

Однажды появилась задача: нужно было добавить возможность в приложение — выбирать какую-то картинку из «Галереи», накладывать на неё определённый фильтр, обрезать, поворачивать и затем передавать непосредственно в сообщения, и отправлять. URLImageView не умел это делать, но зато это мог сделать AssetService.

URLProtocol: как превратить в URL всё что угодно, image #4

Неполный список того, что умеет AssetService:

URLProtocol: как превратить в URL всё что угодно, image #5

Структура проекта выглядит следующим образом: есть основной проект VKClient, и у него есть две ссылки на проекты — messages и PhotoPicker. И задача заключалась в том, чтобы подружить эти два проекта и иметь возможность работать в сообщениях с AssetService.

URLProtocol: как превратить в URL всё что угодно, image #6

Это решается следующим образом:

  • Вываливаем AssetService, закрываем большим количеством протоколов.
  • Пишем свою стерилизацию для вязкости, ведь нужно уметь сохранять этот Asset между перезапуском приложения, чтобы ещё не отправленное сообщение также показывало картинки, которые мы отправили.
  • Своя реализация кэша сверху.
  • Закидываем в какой-нибудь сервис или менеджер, который будет всем этим управлять и взаимодействовать с AssetService.
URLProtocol: как превратить в URL всё что угодно, image #7

Такое решение не устраивало. И поэтому был создан AssetURLProtocol.

URLProtocol: как превратить в URL всё что угодно, image #8

Структура проекта стала выглядеть так:

URLProtocol: как превратить в URL всё что угодно, image #9

Был определен некоторый формат ссылки, который описывал требования. Выглядело это так:

URLProtocol: как превратить в URL всё что угодно, image #10
URLProtocol: как превратить в URL всё что угодно, image #11

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

URLProtocol: как превратить в URL всё что угодно, image #12

Стоит отметить, что при работе с URLProtocol часто встречаются два типа данных — URLProtocol и URLProtocolClient. Их особенность в том, что, несмотря на название, URLProtocol является классом, а URLProtocolClient — протокол. Главное не запутаться.

URLProtocol: как превратить в URL всё что угодно, image #13

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

URLProtocol: как превратить в URL всё что угодно, image #14

У неё будет схема Map, а по координатам будем получать какой-то снэпшот. Назовём протокол MapURLProtocol:

URLProtocol: как превратить в URL всё что угодно, image #15

Чтобы реализовать протокол, необходимо в первую очередь унаследоваться от базового класса URLProtocol и реализовать, как минимум, четыре необходимых метода:

  • Первый будет предназначен для того, чтобы можно было бы понять, что данный URLProtocol умеет обрабатывать подобного вида ссылки.
  • Второй метод предназначен для того, чтобы привести запрос к некому каноничному виду.
  • В методе startLoading происходит загрузка всех данных.
  • Последний метода stopLoading, который вызывается, когда работа протокола по какой-то причине была завершена (отмена, выполнение с ошибкой, успешное выполнение).
URLProtocol: как превратить в URL всё что угодно, image #16

Внутри метода разобьём URL на компоненты и передадим дальше, непосредственно, в метод загрузки.

URLProtocol: как превратить в URL всё что угодно, image #17

В методе загрузки обращаемся, непосредственно, к MapSnapshotter, который есть в MapKit. Какими-то параметрами его проинициализируем. После завершения его работы получим данные картинки и передадим в метод завершения запроса.

URLProtocol: как превратить в URL всё что угодно, image #18

Это один из самых важный этапов в работе URLProtocol. Здесь, во-первых, нужно правильно сформировать URLResponse. Также необходимо выполнить последовательно, в нашем случае, три метода, вызвать его у URLProtocolClient. URLProtocolClient — это некоторое прокси между URLProtocol и URL Loading Sisteme. Нам необходимо ему [URLProtocolClient] правильно сообщать о состояние, чтобы URL Loading Sisteme могла корректно реагировать и выполнять определённые действия. Необходимо сообщать: о получении ответа, были загружены какие-то данные, а также о том, что работа протокола завершена. Вызов этих методов может отличаться в зависимости от реализации URLProtocol.

URLProtocol: как превратить в URL всё что угодно, image #19
URLProtocol: как превратить в URL всё что угодно, image #20

Также необходимо корректно оповещать URLProtocolClient о том, что произошла какая-то ошибка. Именно эта ошибка пойдёт в Completion Handler.

URLProtocol: как превратить в URL всё что угодно, image #21

Необходима URLSession для того, чтобы выполнять запросы.

URLProtocol: как превратить в URL всё что угодно, image #22

А также уже знакомый URLImageView, у которого очень простой код: когда рендерим URL, создаём request и в сессии выполняем его; в Completion Handler берём готовую картинку и устанавливаем её в свойство image.

URLProtocol: как превратить в URL всё что угодно, image #23

Предположим, что код View Controller содержит обычный UICollectionViewController с какими-то квадратными ячейками, внутри которых лежит URLImageView, а модели находится просто массив вот таких ссылок:

URLProtocol: как превратить в URL всё что угодно, image #24

Но есть один нюанс — каждый раз, когда выполняется новый запрос, когда скроллим и пытаемся порендерить новый URL внутри URLProtocol, раз за разом попадаем в метод startLoading, загружаем снова через снэпшотер картинку и возвращаем её.

URLProtocol: как превратить в URL всё что угодно, image #25

Что приводит к вопросу:

URLProtocol: как превратить в URL всё что угодно, image #26

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

Первый этап — запись.

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

URLProtocol: как превратить в URL всё что угодно, image #27

На предыдущем слайде можно заметить, что когда завершали выполнение URLProtocol и обращались к URLProtocolClient, также была указана политика кэширования. Она нужна для того, чтобы URLProtocolClient передал её дальше в URL Loading System, а она на основании этого приняла решение: нужен ли вообще ответ на запрос или нет.

URLProtocol: как превратить в URL всё что угодно, image #28

Формируется CachedURLResponse — простой объект, в который передаются непосредственно данные и response, который имеет информацию. И если у URL Loading System есть объект, который ссылается на какой-то URLCache, значит есть на него ссылка, и можно сохранить ответ на запрос с помощью метода storeCachedResponse.

URLProtocol: как превратить в URL всё что угодно, image #29

CachedURLResponse — это довольно простой тип данных, это просто класс, внутри которого, помимо всего прочего, хранятся данные и информация для него, которая и есть набор необходимых данных, который необходим, чтобы определить, что нужно ли вернуть такой ответ или как-то его сохранить.

URLProtocol: как превратить в URL всё что угодно, image #30

Как читается запрос на ответ если есть он в кэше?

Когда формируется URLRequest, при загрузке при рендеринге URL/URLImageView, помимо URL, также можно указать политику кэширования. По умолчанию указывается политика NSURLProtocol — CachePolicy.

Под капотом URL Loading System уже организовано, как работать с HTTP/HTTPS-запросами, нужно ли возвращать для них уже закэшированный ответ или нет.

URLProtocol: как превратить в URL всё что угодно, image #31

В политике кэширования можно указать возвращение кэшированных данных, и загружаем, если их нет. В таком кейсе при создании сущности URLProtocol прямо на вход на инициализацию придёт в параметре cachedResponse, который затем будет сохранён в свойства и его можно будет обработать.

URLProtocol: как превратить в URL всё что угодно, image #32

Единственное, что останется — это в методе startLoading проверить, что в этом свойстве что-то есть. Если есть, то можно сразу завершить выполнение запроса с этими данными, а если нету, то продолжаем загрузку.

URLProtocol: как превратить в URL всё что угодно, image #33

Для того чтобы кэш работал, необходимо:

URLProtocol: как превратить в URL всё что угодно, image #34

Но есть нюанс — при реализации URLProtocol можно столкнуться с рядом крашей.

URLProtocol: как превратить в URL всё что угодно, image #35

Примеры, как решали во «ВКонтакте» проблемы при реализации URLProtocol:

URLProtocol: как превратить в URL всё что угодно, image #36
URLProtocol: как превратить в URL всё что угодно, image #37
URLProtocol: как превратить в URL всё что угодно, image #38
1 of 3

Какая практическая польза от реализации URLProtocol?

В приложении «ВКонтакте» активно используют URLProtocol. Ниже приведены некоторые из них.

VKPPURLProtocol, который помогает загружать картинки из галереи, делает всю работу по их обработке и выдаёт уже готовые результаты.

URLProtocol: как превратить в URL всё что угодно, image #39

Уже известный из примера MapURLProtocol. В разделе сообщений есть большое количество аттачей, это могут быть фотографии, видео, граффити, стикеры и карты, в том числе. Чтобы карты работали как картинки (также загружались, имели одинаковый кэш), был создан URLProtocol.

URLProtocol: как превратить в URL всё что угодно, image #40

PollBackgroundURLProtocol используется для отрисовки красивых фонов в опросах.

URLProtocol: как превратить в URL всё что угодно, image #41

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

URLProtocol: как превратить в URL всё что угодно, image #42

Из недостатков URLProtocol можно отметить:

URLProtocol: как превратить в URL всё что угодно, image #43

Преимуществами URLProtocol являются:

URLProtocol: как превратить в URL всё что угодно, image #44

Вот и всё!

URLProtocol: как превратить в URL всё что угодно, image #45

URLProtocol позволяет удобно обмениваться данными и поддерживается всеми популярными ОС, так что если вы разработчик, возможно, самое время попробовать этот способ.

Посмотреть презентацию можно тут.

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

URLProtocol: как превратить в URL всё что угодно, image #46
The Brown Room — независимое интернет-издание о соцсетях.

Текст: Лена Гамель, Арсений Метелев
Фото и слайды: VK Tech

314 views·3 shares