Хабрахабр

Когда почта доставляет: боремся с потерями push-уведомлений в iOS

Со стороны пользователя почтовый клиент — приложение нехитрое. Разработчики Яндекс.Почты даже шутят, что в приложении всего три экрана: список писем; отправка письма; экран about.

Как многие мобильные приложения, Почта использует push-уведомления, чтобы взаимодействовать с пользователями. Но очень много интересного происходит под капотом. Как многие iOS-приложения, Почта теряет часть уведомлений в силу особенностей работы Apple Push Notification Service.

Для Почты это так, потому что push-уведомления о новых письмах — это то, ради чего пользователь устанавливает приложение. Руководитель iOS-группы Яндекс.Почты Ася Свириденко докажет, что даже с учетом ограничений системы, с потерями push-уведомлений можно и нужно бороться, если они критичны для вашего приложения. Если же для вашего приложения доставка push-уведомлений не так критична, узнать, какие велосипеды нагородила мобильная Яндекс.Почта, все равно интересно.

Речь пойдет о remote notification, то есть уведомлениях, которые приходят с сервера через APNs (Apple Push Notification Service). Локальные уведомления затрагивать не будем и поговорим о том:

  • Как выглядит API для работы с push-уведомлениями. Рассмотрим схему доставки push-уведомлений и то, где в этой схеме могут возникать потери. 
  • Как решили бороться с потерями в Яндекс.Почте — об очереди push-уведомлений.
  • Как логировать и какие еще сложности могут встретиться.

Что имеем и где теряем

Сейчас API для работы с push-уведомлениями — это достаточно мощная штука, которая позволяет делать много интересных вещей. Но так было не всегда.

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

С тех пор прошло достаточно времени.

Для нас, как для разработчиков, все началось в iOS 3, когда push-уведомления стали доступны для сторонних библиотек.

В iOS 5 появился Notification Center, и push-уведомления перестали уходить в никуда, теперь они остаются в Notification Center, где их можно посмотреть повторно.

У пользователя появилась возможность задать промежуток времени, в течение которого он не хочет получать уведомления. В iOS 6 появился режим «Не беспокоить».

Эти изменения касались в основном того, как пользователь может работать с push-уведомлениями, как они могут сделать его жизнь комфортнее, а не того, как разработчики могут влиять на уведомления.

Для разработчиков важной вехой стал iOS 8 и появление Notification Action, которые позволили выполнять по push-уведомлениям действия, характерные для конкретного приложения.

Первый позволяет модифицировать push-уведомление до того, как оно будет показано пользователю. В iOS 10 появились Notification Service Extension и Notification Content Extension. В iOS 10 этот UI был некликабельный — смотреть можно, трогать нельзя. Второй — по Force Touch на push-уведомление показывать некоторый UI, в котором, например, можно отображать более детальную информацию.

Теперь пользователь может зайти в настройки и указать, хочет ли он, чтобы в пришедших уведомлений отображалось содержимое. В iOS 11 появился Notification Privacy Settings. Понадобилось всего 8 версий iOS, чтобы понять, что не все пользователи хотят, чтобы на лежащем на столе iPhone внезапно всплывала персональная информация. Это огромный шаг в сторону безопасности.

Теперь туда можно добавлять кнопки и управление жестами — все то, что помогает пользователю взаимодействовать с UI. В iOS 12 появилась возможность группировать push-уведомления по thread-id, и тот UI, который мы получили в iOS 10 с помощью Notification Content Extension, стал кликабельным.

Push-уведомления сегодня

Как вы видите, push-уведомления прошли огромный путь, и сегодня с их помощью можно делать действительно много всего.

Текстовые сообщения и локализация

Как и раньше, мы можем отправлять текстовые сообщения в push–уведомлении, но теперь дополнительно можно указать ключи для локализации.

"aps" :
}

Если указать subtitle-loc-key и loc-key в payload уведомления, то когда push-уведомление придет на устройство, в файле Localizable.string приложения будут найдены нужные значения, и пользователь увидит локализованное сообщение.

Звук и critical alert

Как и раньше, можно добавлять звуки в payload уведомления.

"aps" : { "sound" : { "critical" : 1, "name" : "bingbong.aiff", "volume" : 1.0, }
}

В iOS 12 появился critical alert. Это звуки, которые будут проиграны даже в том случае, если пользователь находится в режиме «Не беспокоить».

Поэтому Apple ограничивает приложения, которые могут использовать critical alert. Обычно пользователю не нужно, чтобы, к примеру, приложение с подпиской на журнал ночью сообщило, что вышел новый номер. Возможно, они разрешат вам использовать эту функциональность. Если ваше приложение работает со здоровьем, безопасностью, или вы считаете, что critical alert — это то, что действительно может помочь пользователям взаимодействовать с вашим приложением, напишите Apple.

Silent-уведомления

Silent-уведомления пользователь не видит. Они приходят напрямую в приложение, будят его и позволяют выполнить какие-то действия, чтобы привести приложение в актуальное состояние: отправить запрос на сервер, запросить данные в фоне, обновить данные из базы, обновить UI, чтобы когда пользователь войдет в приложение, он увидел обновленные данные.

"aps" : { "content-available" : 1 // не включать alert, sound и badge ключи в payload
}

Для того, чтобы push-уведомление стало silent, необходимо в payload указать: "content-available" : 1. И не указывать alert, sound и badge ключи в payload — они совершенно бесполезны для push-уведомления, которое не будет показано пользователю.

Группировка уведомлений

Чтобы сгруппировать сообщения, в payload необходимо указать «thread-id». Он может иметь несколько значений в рамках одного приложения, если вы хотите группировать по-разному: по аккаунтам, по получателям, по темам.

"aps" : { "thread-id" : "any_thread_identifier"
}

Это очень удобно, потому что теперь push-уведомления не занимают все место на заблокированном экране, а сгруппированы вместе. Если вы еще не используете эту функциональность, самое время начать.

Изменение уведомления до его показа

Push-уведомления можно менять до того, как они будут показаны. Для этого необходимо добавить в приложение Notification Content Extension и переопределить метод didReceive. В этом методе можно получить контент уведомления и модифицировать его.

"aps" : { "mutable-content" : 1 }
override func didReceive(_ request: UNNotificationRequest, withContentHandler contentHandler: @escaping (UNNotificationContent) -> Void) { guard let mutableContent = request.content.mutableCopy() as? UNMutableNotificationContent else { contentHandler(request.content); return } mutableContent.subtitle = "Got it!" contentHandler(mutableContent)
}

К примеру, можно в уведомлении отправить ссылку на медиа-контент, скачать контент в Extension, и прикрепить скачанное к уведомлению. После этого вызываете completion с новым контекстом, и показываете пользователю расширенное push-уведомление. Можно менять title, subtitle и т.д.

В Notification Content Extension вы сможете их дешифровать и показать пользователю уже дешифрованные данные. Еще интересный кейс — можно отправлять push-уведомление с зашифрованным контекстом, если хотите, чтобы данные были дополнительно защищены, и Apple их не увидел.

Скрытый контент уведомления

В iOS 11 появилась возможность скрывать содержимое push-уведомления, и мы с вами, как разработчики, на это повлиять никак не можем. Если пользователь выставил галочку «Скрывать контент уведомления», так или иначе он будет скрыт. Все, что мы можем сделать — это через UNNotificationCategory указать placeholder, который отобразится вместо содержимого (по умолчанию это notification), и задать, показывать ли title или subtitle.

let commentCategory = UNNotificationCategory(identifier: "comment-category", actions: [], intentIdentifiers: [], hiddenPreviewsBodyPlaceholder:
NSString.localizedUserNotificationString(forKey:"COMMENT_KEY",arguments: nil), options: [.hiddenPreviewsShowTitle])

Действия по уведомлению без запуска приложения

Чтобы осуществлять действия по push-уведомлению без запуска самого приложения, необходимо создать категорию и добавить в нее action. Identifier категории передается в поле category у payload уведомления. Можно подключать разные actions к разным типам уведомлений.

"aps" : { "category" : "message" }
let action = UNNotificationAction(identifier:"reply", title:"Reply", options:[])
let category = UNNotificationCategory(identifier: "message", actions: [action], minimalActions: [action], intentIdentifiers: [], options: [])
UNUserNotificationCenter.current().setNotificationCategories([category])

Rich Notifications

В этом расширении можно обрабатывать дополнительные действия, которые вы добавили к push-уведомлению, и показывать custom UI.

Для этого необходимо в приложение добавить Notification Content Extension, определить в нем класс, который наследуется от UNNotificationContentExtension, и дальше работать с ним, как с обычным UIViewController.

class NotificationViewController: UIViewController, UNNotificationContentExtension { @IBOutlet var userLabel: UILabel? func didReceive(_ notification: UNNotification) { let content = notification.request.content self.title = content.title let userInfo = content.userInfo self.userLabel?.text = userInfo["video-user"] as? String }
}

Если вы обрабатываете кастомные действия, важно помнить, что по этим действиям стоит обновить UI, который вы показываете пользователю. Не надо пытаться реализовать в этом расширении бизнес-логику. Отправлять запрос на сервер по действию с push-уведомлением надо в основном приложении, а не здесь. Это место только для UI.

Схема доставки push-уведомлений

Видите, сколько всего можно сделать с push-уведомлениями в iOS. От версии к версии у нас появляется все новая и новая функциональность, но схема доставки push уведомлений сейчас точно такая же, какая она была в iOS 3.

Можно было бы подумать, что схема доставки push-уведомлений была прекрасна с самого начала, но это не так.

В схеме доставки push-уведомлений есть три основных узла:

  • провайдер, который формирует payload push-уведомления;
  • APNs — Apple Push Notification Service, который доставляет уведомление;
  • устройство с iOS и вашим приложением.

Я пропущу часть о том, как зарегистрироваться, получать токен, куда его отправлять. Предположим, все это у нас есть. Что происходит дальше?

  • Провайдер формирует payload и отправляет его в APNs.
  • APNs отправляет его на устройство.
  • Пользователь видит push-сообщение на своем устройстве.

В Почте и во многих других приложениях используется расширенная схема доставки push-уведомлений. Добавляется Notification Service Extension, в который приходят push-уведомления с "mutable-content" : 1. Провайдер разделяется на сервер, который занимается бэкенд-логикой приложения, и собственно провайдер, который формирует payload и занимается подписками.

XIVA — это база данных подписок. В Яндексе провайдер, формирующий payload, называется XIVA. Почта использует XIVA для работы с push-уведомлениями как стороннюю библиотеку.

Мы не просто подписываем приложение на уведомления, у нас есть мультиаккаунтность. В Почте работа с подписками организована достаточно нетривиально. Всем этим занимается XIVA. Мы можем подписывать разные аккаунты, или в рамках одного аккаунта выбрать, на какие папки пользователь хочет получать уведомления, а на какие не хочет. Некоторые другие сервисы Яндекса также работают через XIVA: вся информация о приложениях, уведомлениях, подписках, токенах хранится в XIVA.

Где потери?

В схеме доставки push-уведомлений четыре стрелочки, потери могут возникать на трех из этих переходов.

Пользователю пришло письмо, сервер об этом знает, формирует уведомление и отправляет в XIVA. Между сервером и XIVA потери могут возникнуть в следующем случае. Тогда XIVA не получит информацию о подписке на папку, и когда придет payload, просто его удалит, и пользователь не увидит нотификации. Но XIVA может потерять эту информацию, например, если пользователь в приложении выбрал «Подписаться» на определенную папку, пока был офлайн.

Мы почти не можем повлиять на сеть, поэтому останавливаться на этом пункте не будем. Между XIVA и APNs могут возникать потери, связанные с сетью.

Это самый частый вид потерь. Между APNs и Extension либо APNS и iOS, если вы не используете Extension. Если, пока пользователь офлайн, ему приходит несколько уведомлений, когда он выйдет онлайн, он увидит только последнее сообщение. Такие потери происходят потому, что APNs не хранит более одного push по приложению на устройстве.

Apple явно пишет, что доставка не гарантирована. Это те самые потери, которые не позволяют нам гарантировать доставку и полагаться на push-уведомления.

Об этом важно помнить. Между Extension приложения и iOS потерь возникать не может, и Apple это гарантирует. Если вы используете Extension и переопределили метод didReceiveContent with completion, даже если вы не вызовете этот completion, уведомление будет показано все равно. Вы можете его не вызвать или не успеть его вызвать, но тогда уведомление будет показано без каких-либо изменений, в том виде, в котором оно приходит из APNs.

Но если вам понадобится увеличить доставляемость push-уведомлений, посмотрите на всю схему. Мы рассмотрим, как мы бороться с потерями между APNs и Extension. Проверьте и измерьте всю цепочку, а потом уже делайте выводы, где больше всего возникает потерь и какую часть этой схемы стоит модифицировать. Проверьте, не возникают ли потери на стороне сервиса, нормально ли ваш провайдер взаимодействует с APNs и так далее.

Очередь push-уведомлений

Наш способ борьбы с потерями в связке APNs и Extension мы назвали очередью push-уведомлений.

Если сжать весь рассказ до одной фразы, то это будет:

Если вы пропустили push-уведомление, его можно запросить заново.

Упрощенно схема работает так: В нашей схеме доставки уведомлений все те же самые участники: XIVA, APNs, Extension.

  • XIVA нумерует push-уведомления, которые собирается отправлять в APNs, и только потом отправляет информацию. 
  • Extension получает push-уведомление номер 1 и, через какое-то время, номер 3. Понимает, что какие-то данные пропущены.
  • Отправляет в XIVA запрос с последней полученной позицией, diff и просит прислать пропущенные данные заново. 
  • XIVA повторно отправляет push-уведомление, потому что хранит у себя базу payloads и базу подписок. Все подписки хранятся в течение некоторого времени, и их можно перезапросить.
  • Перезапрашиваем, получаем push-уведомление, и имеем на клиенте все сообщения, которые клиент должен был получить.

Первая ожидаемая проблема — дублирование уведомлений. Когда мы повторно запрашиваем у XIVA сообщение, мы не знаем, что сейчас в очереди на отправку, потому что общаемся с ней не напрямую, а через APNs. Предположим, мы увидели, что каких-то уведомлений не хватает, и отправили запрос в XIVA. XIVA отправила через APNs payload с пропущенным уведомлением. Но до того, как мы его получили, мы получили другой payload и тоже с пропуском. Опять перезапросили — XIVA еще раз отправила.

Эта настройка позволяет на стороне iOS схлопывать push-уведомления с одинаковыми ID. Чтобы уведомления не дублировались, мы используем apns-collapse-id. Если на устройство пришло несколько push-уведомлений с одинаковым apns-collapse-id, iOS их схлопнет, и пользователь увидит только одно уведомление.

XIVA

Расскажу, как это все работает на XIVA, потому что всегда любопытно, что происходит на бэкенде.

Важно, что в базе все хранилось по юзерам: XIVA существовала до появления очереди push-уведомлений и представляла из себя базу данных подписок.

  • В качестве ключа выступал <service, user>.
  • В качестве value хранился payload (данные о письмах в случае Почты). 

XIVA брала данные из базы и отправляла в APNs или другой сервис, так как работает не только с iOS. Мы решили это переиспользовать.

В принципе, у XIVA для этого уже все было: база данных, TTL у payloads, то есть они не удаляются сразу, их можно переотправить. Мы пришли к команде, разрабатывающей XIVA, и очень попросили сделать очередь push-уведомлений. Единственное, чего не хватало для того, чтобы можно было в рамках текущей реализации XIVA настроить очередь push-уведомлений — это сквозная нумерация.

То есть сквозная нумерация нужна для конкретного устройства и для конкретного приложения, чтобы опираться на неё на стороне клиента. Для сквозной нумерации push-уведомления должны были пронумерованы по устройству и app_name. Теперь в качестве сервиса выступает apns_queue, в качестве юзера device_id + app_name — те самые данные, по которым нужна нумерация на клиенте, то есть key: <apns_queue, device_id + app_name>. Сделали это следующим образом: переиспользовали базу данных XIVA, но стали записывать в неё payloads по другому ключу.

В этот момент payloads получают новую нумерацию, потому что теперь лежат в этой же базе, но по другому ключу. Теперь XIVA берет данные из основной базы данных и, когда их необходимо отправить, складывает в очередь. Итого на клиенте получается необходимая нумерация payload. Уже оттуда XIVA их вынимает и отправляет через APNs.

На клиенте используется Notification Service Extension.

public override func didReceive(_ request: UNNotificationRequest, withContentHandler contentHandler: @escaping (UNNotificationContent) -> Void) { // . . . }

В нем переопределяем метод didReceive и смотрим, что же пришло с сервера. Всем push-уведомлениям добавляем "mutable-content" : 1, чтобы они попали в Extension, потому что иначе не можем учесть их в расчетах.

Если не распарсили, то это сообщение не из XIVA. Дальше в коде внутри метода идут сплошные проверки: пришел ли необходимый payload, смогли ли его распарсить. Если сообщение не из XIVA, мы не можем с ним дальше работать и просто вызываем completion с тем уведомлением, которое пришло из APNs, никакие расчеты не осуществляем.

guard let payload = try? self.payloadParser.parsePayload(from: request.content.userInfo) else { // невалидный формат, или нотификация не из xiva contentHandler(request.content); return
}

Логируем, проверяем, не поменялся ли deviceId, так как знаем, что в iOS это возможно. Честно говоря, мы с изменением deviceId так и не столкнулись, но на всякий случай обрабатываем, потому что если он изменится, мы не сможем доверять нумерации от XIVA.

self.logger.logNotificationReceived(with: payload)
if lastPositionDeviceId != deviceId { // deviceId изменился, сбрасываем сохранённые настройки lastNotificationPosition = nil lastPositionDeviceId = deviceId
}

Дальше смотрим, можем ли получить данные XIVA в этом payload, есть они или нет. Если нет, снова вызываем contentHandler.

guard let xivaInfo = payload.xivaInfo else { contentHandler(request.content); return
}

Если данные есть, проверяем, для того ли deviceId пришли данные. XIVA присылает в payload хэш устройства, если сверили и совпало, продолжаем, нет — вызываем contentHandler.

guard isHashCompatible(deviceId: deviceId, deviceIdHash: xivaInfo.deviceIdHash) else { // payload device_id не равен device_id приложения contentHandler(request.content); return
}

Следующим блоком смотрим, есть ли сохраненная позиция:

  • Если у нас нет последней сохраненной позиции, то мы либо еще не получали уведомления и не заходили в Extension, либо по какой-то причине дропнули. Тогда не от чего отталкиваться, чтобы посчитать diff пропущенных, и мы снова вызываем completion.
  • Если есть, идем дальше.

guard let lastPos = lastNotificationPosition else { // сохраняем позицию на следующий раз lastNotificationPosition = xivaInfo.notificationPosition contentHandler(request.content); return
}

Считаем количество пропущенных уведомлений. Если пропущенных ноль — отлично, мы ничего не пропустили.

let missedMessages = xivaInfo.notificationPosition - lastPos - 1
guard missedMessages > 0 else { // получили дубликат push–уведомления или ничего не пропустили contentHandler(request.content); return
}

Иначе берем из XIVA данные по позиции — из той самой сквозной нумерации. Дальше смотрим, не превышает ли количество пропущенных некое заданное значение.

lastNotificationPosition = xivaInfo.notificationPosition
guard missedMessages <= repeatMaxCount else { // слишком много сообщений пропущено, показываем одно contentHandler(buildNewNotification()); return
}

Зачем это нужно? Предположим, пользователь достаточно долго был офлайн, и за это время накопилась сотня пропущенных сообщений. Мы запросим всю сотню (нам несложно), XIVA всю сотню вышлет, и пользователь получит все уведомления. Даже если мы сгруппируем их по thread-id (а мы группируем), то все равно для каждого уведомления вызовется этот Extension, пройдут все проверки. Кажется маловероятным, что пользователю нужны все сто уведомлений. Поэтому мы формируем уведомление, в котором так и пишем, что у вас 100 пропущенных сообщений, зайдите в приложение и посмотрите. И показываем пользователю именно это сообщение, потому что можем подменять push-уведомления.

И смотрим: Когда все проверки пройдены, мы отправляем запрос в XIVA: последнюю позицию, которая нам пришла, и количество пропущенных сообщений.

  • Если XIVA ответила успешно: «Все хорошо, перепосылаю данные», мы показываем пользователю текущее уведомление и ждем, пока XIVA дошлет все остальное, и пользователь увидит все пропущенные сообщения. 
  • Если же XIVA отвечает ошибкой, то показываем пользователю кастомное уведомление о том, что у него есть пропущенные сообщения, которые можно посмотреть в приложении. 

self.requestMissedNotifications(lastPosition: xivaInfo.notificationPosition, gap: missedMessages) { result in result.onValue { _ in self.logger.logNotificationProcessed(with: .success) contentHandler(request.content) }.onError { error in self.logger.logNotificationProcessed(with: .failure(error)) contentHandler(buildNewNotification()) }
}

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

Логирование и прочие сложности

Как известно, чтобы убедиться, хорошо ли работает подход, надо логировать. Мы стали собирать статистику по новому способу доставки уведомлений и сравнивать, как изменилась доставляемость.

Ограничения push-extension

Первое, с чем мы столкнулись, — это ограничения push-extension.

Если в настройках приложения выключить отрисовку уведомлений (возможность получать уведомление остается включенной, но выключаются все возможные отрисовки), Extension вызван не будет — не будет вызвана вся логика с пересчетами и, самое главное, логирование. Не всегда вызывается. Мы не сможем узнать то, что нам важнее всего, — получил ли пользователь уведомление.

В документации Apple написано, что в течение примерно 30 секунд необходимо вызвать completion с видоизмененным уведомлением, иначе будет показано изначальное уведомление. У push-extension есть ограничение по времени.

Мы реализовали фичу, которую назвали «красивые» push-уведомления, прикрепляли к уведомлениям медиаэлементы, изменяли title, subtitle. Интересно то, как мы это выяснили. В ходе тестирования оказалось, что некоторые push-уведомления стали красивыми, а остальные как были гадкими утятами, так и остались.

Соответственно, когда не успеваем, push-уведомления показывается именно в том виде, в котором пришли с APNs. Мы стали смотреть, в чем разница между этими push-уведомлениями, и выяснили, что разницы нет, просто для одних мы успеваем вызвать completion, а для других нет.

Apple предупреждает, что память, выделяемая на push-extension, ограничена, и не рекомендует загружать в него тяжелые данные, но не уточняет точный размер. Третье ограничение — по памяти. У нас получилось, что это примерно 12 МБ.

Ограничения на память немного отличаются, но порядок примерно такой — 10 МБ. На Apple Developer Forum разработчики активно обсуждают, какие есть ограничения, высказывают свои предположения и пытаются их точно вычислить.

Для логирования мы используем Яндекс AppMetrica. Мы столкнулись с этим ограничением, когда добавляли логирование. Поэтому нам пришлось нагородить маленький велосипед, чтобы все-таки залогировать получение уведомлений. Когда мы начинали, AppMetrica для загрузки требовалось много памяти, и наш Extension все время отваливался.

Измерение результатов превратилось в игру: попытку не уронить Extension и залогировать данные.

Измеряем результаты

В итоге логирования push-extension пишет данные в UserDefaults. Потом, когда основное приложение просыпается, оно отправляет данные в AppMetrica.

Основной из них сказывается на измерении. У этого подхода есть минусы. Поэтому мы строим выводы только на основе измерений тех пользователей, которые запустили приложение в тот же или на следующий день. Нам пришлось учитывать, что пользователи не обязательно запускают приложение в тот же день, это вообще может произойти через месяц. Иначе у нас будет большое несоответствие между теми данными, которые отправила XIVA (мы их логируем), и тем, что получил пользователь.

Важно помнить, что Notification Extension работает с iOS 10 и выше, поэтому если вы логируете данные через Extension, не забывайте удалять данные о тех пользователях, которые используют более ранние версии.

В AppMetrica есть логирование push-уведомлений, и я думаю, что в ближайшее время мы выкинем наш велосипед и вернёмся к нормальному логированию. В защиту AppMetrica: очень многое сделано с тех пор, push-extension уже давно не падает по памяти. Какие есть возможность, можно прочитать в AppMetrica Push SDK.

график за январь — было до того, как внедрили очередь. Вот, что показали измерения. По вертикали доставляемость, по горизонтали время.

Явные падения — это выходные дни, когда пользователи и меньше отправляют уведомлений, и гораздо реже открывают почту.

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

Тут можно было бы и остановиться, но… Доставляемость увеличивается, а значит, мы движемся в верном направлении.

Фрустрация

Мы сделали многое: написали код, посчитали, графики нарисовали. Но как определить, сработало ли? Изменилось ли что-то от того, что мы внедрили очередь push-уведомлений? Доставляемость увеличилась, а как это повлияло на работу с приложением? Как это поменяло user experience и сценарий работы?

Стали ли наши пользователи счастливее от того, что они за день увидели на 2–3–20 уведомлений больше?

Мы, разработка, взяли задачу, придумали решение, написали код, получили результаты и, вроде бы, стали счастливее. Но с продуктовой точки зрения мы еще не до конца поняли, что именно дало увеличение доставляемости push-уведомлений. Возможно, это будет темой следующего рассказа.

Итоги

Push-уведомления в iOS прошли большой путь. Если вы еще не используете их в своем приложении или используете по минимуму, посмотрите на пример Яндекс.Почты. Возможно, некоторые решения вам пригодятся.

Может, у вас есть похожий сервис, который поможет вам в этом. Пропущенные push-уведомления можно (и нужно) перезапрашивать. Совершенно необязательно делать это как в Яндекс.Почте через XIVA. Ищите! Может, найдете сторонний, который тоже умеет делать нечто подобное.

Следите, чтобы он вызывался. Помните про ограничения push-extension. Не перегружайте его по памяти, учитывайте ограничение по времени.

Возможно, увеличение доставляемости push-уведомлений для вашего приложения абсолютно не критично и ничего вам не даст. Подумайте, что вам даст улучшение доставки. Прежде, чем ввязаться в эту авантюру, подумайте, нужно ли вам это, даст ли это что-то вашему приложению. Но, возможно, именно вам оно даст прирост пользователей, и вы окажетесь на вершине App Store, где мы все хотим оказаться, и обязательно окажемся!

В равной степени мы предвкушаем еще почти 50 выступлений, призванных помочь мобильным разработчикам расти. Мы в AppsConf с нетерпением ждем следующего доклада Аси, в котором она уже 21 или 22 октября расскажет об идеях и открытиях, родившихся за время рефакторинга большей части приложения Яндекс.Почты. До 1 сентября можно подать заявку на доклад и оказаться в компании крутых людей, которые прямо влияют на будущее индустрии — успевайте.

Теги
Показать больше

Похожие статьи

Добавить комментарий

Ваш e-mail не будет опубликован. Обязательные поля помечены *

Кнопка «Наверх»
Закрыть