Pull to refresh

Особенности работы с Apple push notification service

Reading time 3 min
Views 29K
Добрый день, уважаемые хабражители. Совсем недавно я закончил разработку apns-сервиса и хотел бы поделиться некоторыми особенностями работы с ним. Статья не является пошаговой инструкцией, а описывает трудности и подводные камни с которыми может столкнуться разработчик. Примеры кода буду приводить на Ruby, но все написанное актуально и для других платформ, в частности, PHP.


Об общих принципах работы с apns написано много статей, да и официальная документация достаточно понятна и прозрачна, поэтому перейдем сразу к делу:

Особенность номер раз — JSON

Именно в этот формат необходимо преобразовать сообщение для отправки на сервер apple, но здесь нас ждет разочарование. Казалось бы, проще всего сгенерировать сообщение с помощью встроенных функций преобразования массивов в json вот так:
result = {}
result['aps'] = {}
result['aps']['alert'] = m.message
result['aps']['badge'] = 0
result['aps']['sound'] = 'default'
json = result.to_json

Но не все так просто. Вы вдруг обнаружите, что максимальная длина текста сообщения составляет всего 40-50 символов. Почему так? Дело в том, что функция to_json в Ruby преобразует все не ASCII символы в последовательность /uXXXX, которая при упаковке сообщения занимает 4! байта. Для обхода этого ограничения необходимо формировать сообщение в обычном текстовом формате. Например, вот так:
json = '{"aps":{"alert":"'+message.gsub(/['"\\\x0]/,'\\\\\0')+'","badge":"'+badge+'","sound":"default"}}'

.gsub(/['"\\\x0]/,'\\\\\0')
— это аналог PHP-функции addslashes(). Если ее не применить, то сообщения со знаками препинания и некоторыми служебными символами не дойдут пользователю и будут сочтены apple как ошибочные.

Особенность номер два — обрыв соединения и обратная связь

Если при отправке очередного сообщения произошла ошибка, то apple просто разрывает связь, не дожидаясь окончания отправки всех сообщений в очереди. Но есть возможность узнать, какая именно ошибка произошла при отправке. Для этого необходимо составить сообщение особым образом и присвоить каждому уникальный номер:
mes =  [1, #Уникальный ID сообщения#, #Максимальное время ожидания доставки#, 0, 32, token, 0, payload.bytesize, payload].pack("cNNcca*cca*")

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

Особенность номер три — обратная связь

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

Особенность номер четыре — массовая рассылка сообщений

Соединение с серверами apple устанавливается с помощью сертификата, выданного определенному приложению. Именно так Apple определяет, к какому приложению на устройстве пользователя относится отправленное уведомление. Следовательно, для отправки сообщений нескольким приложениям (а именно такая задача стояла у сервиса, разработанного мной), необходимо для каждого массива сообщений создавать отдельное соединение. Частая ошибка, совершаемая разработчиками — они пытаются отправлять сообщения небольшими партиями по 100-200 штук, каждый раз создавая и разрывая соединение. Это может быть воспринята сервером как попытка атаки и ваш сервис будет заблокирован. Чтобы избежать этого, нужно отправлять все сообщения через одно соединение, даже если их несколько десятков тысяч. И отсюда вытекает особенность номер шесть.

Особенность номер шесть — время наш враг

При большом количестве сообщений время исполнения скрипта отправки может быть достаточно велико и на большинстве серверов выполнение будет завершено по таймауту в 30 секунд. Решение — разделение логики создания и отправки сообщений. Я сделал это с помощью демона (в ROR за это отвечает gem daemons. Через веб интерфейс в базу добавляется текст сообщения и фильтр, по которому при отправке будут выбираться токены из базы, а демон каждые 10 секунд опрашивает базу на наличие новых заданий и отправляет сообщения. В такой реализации есть еще один неоспоримый плюс — мы можем настроить отложенную отправку сообщений.

Заключение

Не смотря на все подводные камни и тонкости, которые нужно держать в голове, реализация сервиса push сообщений у apple сделана на высшем уровне. Сервис быстр, удобен и требует минимум ресурсов для отправки огромного количества сообщения. Для примера: Чтобы отправить 15000 сообщений, мы должны передать на сервер всего около 3,5-4 мегабайт данных
Tags:
Hubs:
+18
Comments 9
Comments Comments 9

Articles