De la part de l'utilisateur, le client de messagerie est une application simple. Les développeurs de Yandex.Mail plaisantent même qu'il n'y a que trois écrans dans l'application: une liste de lettres; envoyer une lettre; sur l'écran.
Mais beaucoup de choses intéressantes se passent sous le capot. Comme de nombreuses applications mobiles, Mail utilise des notifications push pour interagir avec les utilisateurs. Comme de nombreuses applications iOS, Mail perd certaines notifications en raison de la nature du service de notification push d'Apple.
Asya Sviridenko , chef du groupe Yandex.Mail iOS, prouvera que même avec les limites du système, il est possible et nécessaire de faire face à la perte de notifications push si elles sont critiques pour votre application. Cela est vrai pour Mail, car les notifications push de nouvelles lettres sont celles pour lesquelles l'utilisateur installe l'application. Si, pour votre application, la livraison de notifications push n'est pas si critique, il est toujours intéressant de savoir quels vélos le mobile Yandex.Mail a empilés.
Il s'agit de notifications à distance, c'est-à-dire de notifications provenant du serveur via des APN (Apple Push Notification Service). Nous n'allons pas toucher aux notifications locales et parler de:
- À quoi ressemble l'API pour travailler avec les notifications push. Envisagez un schéma de remise des notifications push et où les pertes peuvent survenir dans ce schéma.
- Comment avez-vous décidé de gérer les pertes dans Yandex.Mail - à propos de la file d'attente de notifications push.
- Comment se connecter et quelles autres difficultés peuvent rencontrer.
Ce que nous avons et où nous perdons
Maintenant, l'API pour travailler avec les notifications push est une chose assez puissante qui vous permet de faire beaucoup de choses intéressantes. Mais cela n'a pas toujours été le cas.

Auparavant, les notifications push ressemblaient exactement à ceci - c'était un tableau de bord bleu malheureux qui apparaissait à l'écran, bloquait le travail avec l'application actuelle, ne permettait rien de faire, puis disparaissait pour toujours, et il n'y avait plus de rappels à ce sujet.
Depuis, assez de temps s'est écoulé.

Pour nous, en tant que développeurs,
tout a commencé dans iOS 3 lorsque les notifications push sont devenues disponibles pour les bibliothèques tierces.
Le centre de notifications est apparu dans iOS 5 , et les notifications push ont cessé d'aller nulle part, maintenant elles restent dans le centre de notifications, où elles peuvent être visualisées à nouveau.
IOS 6 a introduit Ne pas déranger . L'utilisateur a la possibilité de définir la période pendant laquelle il ne souhaite pas recevoir de notifications.
Ces changements concernaient principalement la façon dont l'utilisateur peut travailler avec les notifications push, comment elles peuvent rendre sa vie plus confortable et non comment les développeurs peuvent influencer les notifications.
Pour les développeurs, une étape importante a été
iOS 8 et l'émergence de Notification Action , qui a permis d'effectuer des actions spécifiques à une application spécifique par des notifications push.
IOS 10 introduit l'extension de service de notification et l'extension de contenu de notification . Le premier vous permet de modifier la notification push avant qu'elle ne soit affichée à l'utilisateur. La seconde consiste à afficher une interface utilisateur par notification push sur la notification push, dans laquelle, par exemple, vous pouvez afficher des informations plus détaillées. Dans iOS 10, cette interface utilisateur n'était pas cliquable - vous pouvez regarder, vous ne pouvez pas la toucher.
IOS 11 a introduit les paramètres de confidentialité des notifications . L'utilisateur peut maintenant accéder aux paramètres et indiquer s'il souhaite afficher le contenu des notifications entrantes. C'est un grand pas vers la sécurité. Il n'a fallu que 8 versions d'iOS pour comprendre que tous les utilisateurs ne veulent pas que les informations personnelles apparaissent soudainement sur l'iPhone posé sur la table.
Dans iOS 12, il est devenu possible de regrouper les notifications push par thread-id, et l'interface utilisateur que nous avons reçue dans iOS 10 avec l'extension de contenu de notification est devenue cliquable. Vous pouvez maintenant y ajouter des boutons et des commandes gestuelles - tout ce qui aide l'utilisateur à interagir avec l'interface utilisateur.
Notifications push aujourd'hui
Comme vous pouvez le voir, les notifications push ont parcouru un long chemin, et aujourd'hui avec leur aide, vous pouvez vraiment faire beaucoup de choses.
Messagerie texte et localisation
Comme précédemment, nous pouvons envoyer des messages texte dans une notification push, mais vous pouvez désormais spécifier des clés de localisation.
"aps" : { "alert" : { "title" : "New Mail", "subtitle-loc-key" : "alert_subtitle_localization_key", "loc-key" : "alert_body_localization_key", } }
Si vous spécifiez
subtitle-loc-key
et
loc-key
dans la notification de charge utile, lorsque la notification push arrive sur le périphérique, les valeurs nécessaires seront trouvées dans le fichier Localizable.string de l'application et l'utilisateur verra un message localisé.
Alerte sonore et critique
Comme précédemment, vous pouvez ajouter des sons aux notifications de charge utile.
"aps" : { "sound" : { "critical" : 1, "name" : "bingbong.aiff", "volume" : 1.0, } }
IOS 12 a une alerte critique. Ce sont des sons qui seront joués même si l'utilisateur est en mode Ne pas déranger.
En règle générale, l'utilisateur n'a pas besoin, par exemple, d'une application avec un abonnement à un magazine la nuit pour signaler qu'un nouveau numéro a été publié. Par conséquent, Apple restreint les applications pouvant utiliser une alerte critique. Si votre application fonctionne avec la santé, la sécurité ou si vous pensez que l'alerte critique peut vraiment aider les utilisateurs à interagir avec votre application, écrivez à Apple. Ils vous permettront peut-être d'utiliser cette fonctionnalité.
Notifications silencieuses
L'utilisateur ne voit pas les notifications silencieuses. Ils viennent directement à l'application, la réveillent et vous permettent d'effectuer certaines actions pour mettre à jour l'application: envoyer une demande au serveur, demander des données en arrière-plan, mettre à jour les données de la base de données, mettre à jour l'interface utilisateur de sorte que lorsque l'utilisateur entre dans l'application, il voit données mises à jour.
"aps" : { "content-available" : 1
Pour que la notification push devienne silencieuse, vous devez spécifier dans la charge utile:
"content-available" : 1
. Et ne spécifiez pas les clés d'alerte, de son et de badge dans la charge utile - elles sont complètement inutiles pour les notifications push qui ne seront pas affichées à l'utilisateur.
Regroupement de notifications
Pour regrouper les messages, vous devez spécifier «thread-id» dans la charge utile. Il peut avoir plusieurs valeurs dans la même application, si vous souhaitez regrouper de différentes manières: par comptes, par destinataires, par thème.
"aps" : { "thread-id" : "any_thread_identifier" }
C'est très pratique, car les notifications push ne prennent plus tout l'espace sur l'écran verrouillé, mais sont regroupées. Si vous n'utilisez pas déjà cette fonctionnalité, il est temps de commencer.
Modifier la notification avant de l'afficher
Les notifications push peuvent être modifiées avant d'être affichées. Pour ce faire, vous devez ajouter l'extension de contenu de notification à l'application et remplacer la méthode
didReceive
. Dans cette méthode, vous pouvez obtenir le contenu de la notification et le modifier.
"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) }
Par exemple, vous pouvez envoyer un lien vers le contenu multimédia dans la notification, télécharger le contenu dans Extension et joindre le fichier téléchargé à la notification. Après cela, appelez la fin avec un nouveau contexte et montrez à l'utilisateur une notification push étendue. Vous pouvez changer le titre, le sous-titre, etc.
Un autre cas intéressant est que vous pouvez envoyer une notification push avec un contexte crypté, si vous souhaitez que les données soient protégées en plus, et Apple ne l'a pas vu. Dans l'extension de contenu de notification, vous pouvez les déchiffrer et montrer à l'utilisateur les données déjà déchiffrées.
Contenu de notification masqué
Dans iOS 11, il est devenu possible de masquer le contenu des notifications push, et nous, en tant que développeurs, ne pouvons en aucun cas influencer cela. Si l'utilisateur a coché le "Masquer le contenu de la notification", d'une manière ou d'une autre, il sera masqué. Tout ce que nous pouvons faire, c'est via l'UNNotificationCategory pour spécifier un espace réservé qui sera affiché à la place du contenu (par défaut, il s'agit de la notification) et pour définir s'il faut afficher le titre ou le sous-titre.
let commentCategory = UNNotificationCategory(identifier: "comment-category", actions: [], intentIdentifiers: [], hiddenPreviewsBodyPlaceholder: NSString.localizedUserNotificationString(forKey:"COMMENT_KEY",arguments: nil), options: [.hiddenPreviewsShowTitle])
Étapes de notification sans lancer l'application
Pour effectuer des actions de notification push sans lancer l'application elle-même, vous devez créer une catégorie et y ajouter une action. L'identifiant de catégorie est transmis au champ de catégorie de la notification de charge utile. Vous pouvez connecter différentes actions à différents types de notifications.
"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])
Notifications riches
Dans cette extension, vous pouvez traiter des actions supplémentaires que vous avez ajoutées à la notification push et afficher l'interface utilisateur personnalisée.
Pour ce faire, vous devez ajouter l'extension de contenu de notification à l'application, y définir une classe qui hérite de UNNotificationContentExtension, puis travailler avec comme avec un UIViewController normal.
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 } }
Si vous traitez des actions personnalisées, il est important de se rappeler que ces actions valent la peine de mettre à jour l'interface utilisateur que vous montrez à l'utilisateur. Pas besoin d'essayer d'implémenter la logique métier dans cette extension. Envoyez une demande au serveur par action avec une notification push dans l'application principale, et pas ici. Cet endroit est pour l'interface utilisateur uniquement.
Système de remise des notifications push
Découvrez tout ce que vous pouvez faire avec les notifications push dans iOS. De version en version, nous avons de plus en plus de nouvelles fonctionnalités, mais le schéma de livraison des notifications push est maintenant exactement le même que dans iOS 3.

On pourrait penser que le schéma de livraison des notifications push était bien au départ, mais ce n'est pas le cas.
Il existe trois nœuds principaux dans le schéma de remise des notifications push:
- fournisseur qui génère des notifications push de charge utile;
- APN - Apple Push Notification Service, qui délivre une notification;
- Appareil iOS et votre application.
Je vais sauter la partie sur la façon de m'inscrire, recevoir un jeton, où l'envoyer. Supposons que nous ayons tout cela. Que se passe-t-il ensuite?
- Le fournisseur génère une charge utile et l'envoie aux APN.
- APNs l'envoie à l'appareil.
- L'utilisateur voit un message push sur son appareil.
Mail et de nombreuses autres applications utilisent un schéma avancé de remise des notifications push. L'extension du service de notification est ajoutée, qui reçoit des notifications push avec un
"mutable-content" : 1
. Le fournisseur est divisé en un serveur qui gère la logique de backend de l'application et le fournisseur lui-même, qui génère la charge utile et gère les abonnements.
Dans Yandex, le fournisseur qui forme la charge utile est appelé XIVA. XIVA est une base de données d'abonnement. Mail utilise XIVA pour travailler avec les notifications push en tant que bibliothèque tierce.
Dans Mail, le travail avec les abonnements est organisé de manière assez non triviale. Nous ne signons pas seulement l'application pour les notifications, nous avons plusieurs comptes. Nous pouvons signer différents comptes ou, au sein d'un même compte, choisir les dossiers pour lesquels l'utilisateur souhaite recevoir des notifications et ceux pour lesquels il ne souhaite pas. XIVA s'occupe de tout cela. Certains autres services Yandex fonctionnent également via XIVA: toutes les informations sur les applications, les notifications, les abonnements, les jetons sont stockées dans XIVA.
Où sont les pertes?
Le schéma de remise des notifications push comporte quatre flèches; des pertes peuvent survenir à trois de ces transitions.
Entre le serveur et XIVA, des pertes peuvent survenir dans le cas suivant. L'utilisateur a reçu une lettre, le serveur en a connaissance, génère une notification et l'envoie à XIVA. Mais XIVA peut perdre ces informations, par exemple, si un utilisateur de l'application a choisi "S'abonner" à un dossier spécifique alors qu'il était hors ligne. Ensuite, XIVA ne recevra pas d'informations sur l'abonnement au dossier, et lorsque la charge utile arrivera, elle la supprimera simplement et l'utilisateur ne verra pas la notification.
Entre XIVA et APN , une perte de réseau peut se produire. Nous pouvons difficilement affecter le réseau, nous ne nous attarderons donc pas sur ce point.
Entre APN et Extension, ou APNS et iOS si vous n'utilisez pas Extension. Il s'agit du type de perte le plus courant. De telles pertes se produisent car les APN ne stockent pas plus d'un push par application sur l'appareil. Si, alors que l'utilisateur est hors ligne, il reçoit plusieurs notifications, lorsqu'il se connecte, il ne verra que le dernier message.
Ce sont les mêmes pertes qui ne nous permettent pas de garantir la livraison et de compter sur les notifications push. Apple écrit clairement que la livraison n'est pas garantie.
Entre l'application Extension et iOS, aucune perte ne peut se produire , et Apple le garantit. Si vous utilisez Extension et remplacez la méthode didReceiveContent avec complétion, même si vous n'appelez pas cette complétion, la notification sera quand même affichée. C'est important à retenir. Vous ne pouvez pas l'appeler ou n'avez pas le temps de l'appeler, mais la notification sera affichée sans aucune modification, sous la forme dans laquelle elle provient des APN.
Nous verrons comment nous gérons les pertes entre les APN et l'extension. Mais si vous avez besoin d'augmenter la délivrabilité des notifications push, jetez un œil à l'ensemble du schéma. Vérifiez s'il y a des pertes du côté service, si votre fournisseur interagit normalement avec les APN et ainsi de suite. Vérifiez et mesurez toute la chaîne, puis tirez des conclusions là où les pertes se produisent le plus et quelle partie de ce circuit doit être modifiée.
File d'attente de notification push
Notre façon de gérer les pertes dans l'ensemble d'APN et d'extension, nous avons appelé la file d'attente de notifications push.
Si vous compressez toute l'histoire en une seule phrase, ce sera:
Si vous avez manqué la notification push, vous pouvez la demander à nouveau.

Dans notre schéma de livraison de notification, tous les mêmes participants sont: XIVA, APN, Extension. Le schéma simplifié fonctionne comme ceci:
- XIVA numérote les notifications push qu'il a l'intention d'envoyer aux APN, puis envoie uniquement des informations.
- L'extension reçoit une notification push numéro 1 et, après un certain temps, numéro 3. Elle comprend que certaines données sont manquantes.
- Envoie à XIVA une demande avec la dernière position reçue, diff et demande de renvoyer les données manquantes.
- XIVA renvoie la notification push car elle stocke la base de données de charges utiles et la base de données d'abonnement. Tous les abonnements sont stockés pendant un certain temps et peuvent être demandés à nouveau.
- Nous demandons à nouveau, nous recevons une notification push, et nous avons sur le client tous les messages que le client aurait dû recevoir.
Le premier problème attendu est les notifications en double. Lorsque nous demandons à nouveau un message à XIVA, nous ne savons pas ce qui est dans la file d'attente pour l'envoi, car nous communiquons avec lui non pas directement, mais via des APN. Supposons que nous ayons vu que certaines notifications manquaient et avons envoyé une demande à XIVA. XIVA envoyé via des APN de charge utile avec une notification manquée. Mais avant de le recevoir, nous avons reçu une autre charge utile et également avec un laissez-passer. Ils ont demandé à nouveau - XIVA a envoyé à nouveau.
Pour que les notifications ne soient pas dupliquées, nous utilisons
apns-collapse-id . Ce paramètre permet au côté iOS de réduire les notifications push avec le même ID. Si plusieurs notifications push avec le même id-collapse-id sont arrivées sur l'appareil, iOS les réduira et l'utilisateur ne verra qu'une seule notification.
XIVA
Je vais vous expliquer comment tout cela fonctionne sur XIVA, car c'est toujours curieux de savoir ce qui se passe sur le backend.
XIVA existait avant la file d'attente de notifications push et était une base de données d'abonnement. Il est important que dans la base de données tout soit stocké par les utilisateurs:
- La clé était
<service, user>
. - La charge utile a été stockée en tant que valeur (données sur les lettres dans le cas de Mail).
XIVA a pris des données de la base de données et envoyées aux APN ou à un autre service, car cela ne fonctionne pas uniquement avec iOS. Nous avons décidé de le réutiliser.
Nous sommes venus à l'équipe de développement XIVA et avons vraiment demandé une file d'attente de notifications push. En principe, XIVA avait déjà tout pour cela: la base de données, TTL pour les charges utiles, c'est-à-dire qu'elles ne sont pas supprimées immédiatement, elles peuvent être transmises. La seule chose qui manquait était qu'il était possible de configurer la file d'attente de notifications push dans le cadre de l'implémentation XIVA actuelle - c'est par le biais de la numérotation.
Pour la numérotation directe, les notifications push doivent être numérotées par appareil et nom_app. C'est-à-dire que la numérotation de bout en bout est nécessaire pour un appareil spécifique et pour une application spécifique afin de pouvoir compter sur elle côté client. Nous avons fait cela comme suit: réutilisé la base de données XIVA, mais commencé à y écrire des charges utiles à l'aide d'une clé différente. Maintenant, apns_queue agit comme un service,
device_id + app_name
tant qu'utilisateur - les données mêmes qui doivent être numérotées sur le client, c'est-à-dire la
key: <apns_queue, device_id + app_name>
.
Désormais, XIVA prend les données de la base de données principale et les place dans la file d'attente lorsqu'elles doivent être envoyées. À ce stade, les charges utiles reçoivent une nouvelle numérotation, car elles se trouvent maintenant dans la même base de données, mais avec une clé différente. Déjà à partir de là, XIVA les sort et les envoie via des APN. Au total, le client reçoit la numérotation de charge utile nécessaire.
Le client utilise l'extension du service de notification.
public override func didReceive(_ request: UNNotificationRequest, withContentHandler contentHandler: @escaping (UNNotificationContent) -> Void) {
Nous y redéfinissons la méthode
didReceive
et voyons ce qui provient du serveur. Nous ajoutons
"mutable-content" : 1
à toutes les notifications push afin qu'elles tombent dans Extension, car sinon nous ne pourrons pas les prendre en compte dans les calculs.
Plus loin dans le code à l'intérieur de la méthode, il y a des contrôles continus: si la charge utile nécessaire est venue, si elle peut l'analyser. S'il n'est pas analysé, ce message ne provient pas de XIVA. Si le message ne provient pas de XIVA, nous ne pouvons pas continuer à travailler avec lui et simplement appeler la complétion avec la notification provenant d'APN, nous n'effectuons aucun calcul.
guard let payload = try? self.payloadParser.parsePayload(from: request.content.userInfo) else {
Nous nous connectons, vérifions si le deviceId a changé, car nous savons que dans iOS, c'est possible. Honnêtement, nous n'avons pas rencontré de changement dans deviceId, mais juste au cas où nous le traiterions, car s'il change, nous ne pourrons pas faire confiance aux numéros de XIVA.
self.logger.logNotificationReceived(with: payload) if lastPositionDeviceId != deviceId {
En outre, nous examinons si nous pouvons recevoir les données XIVA dans cette charge utile, qu'elles le soient ou non. Sinon, appelez à nouveau contentHandler.
guard let xivaInfo = payload.xivaInfo else { contentHandler(request.content); return }
S'il y a des données, vérifiez si deviceId a reçu des données. XIVA envoie un hachage de l'appareil à la charge utile, s'il est vérifié et correspond, nous continuons, non, nous appelons contentHandler.
guard isHashCompatible(deviceId: deviceId, deviceIdHash: xivaInfo.deviceIdHash) else {
Le bloc suivant consiste à voir s'il y a une position enregistrée:
- Si nous n'avons pas la dernière position enregistrée, alors nous n'avons pas encore reçu de notifications et ne sommes pas entrés dans Extension, ou pour une raison quelconque, nous avons abandonné. Ensuite, il n'y a rien à pousser pour trouver le diff manqué, et nous appelons à nouveau l'achèvement.
- S'il y en a, continuez.
guard let lastPos = lastNotificationPosition else {
Nous comptons le nombre de notifications manquées. Si le zéro est parfait, nous n'avons rien manqué.
let missedMessages = xivaInfo.notificationPosition - lastPos - 1 guard missedMessages > 0 else {
Sinon, nous prenons de XIVA les données de position - de cette même numérotation continue. En outre, nous examinons si la quantité de manqués ne dépasse pas une certaine valeur définie.
lastNotificationPosition = xivaInfo.notificationPosition guard missedMessages <= repeatMaxCount else {
Pourquoi est-ce nécessaire? Supposons que l'utilisateur soit hors ligne depuis longtemps, et pendant ce temps, une centaine de messages ont été accumulés. Nous demanderons la centaine entière (c'est facile pour nous), XIVA enverra la centaine entière et l'utilisateur recevra toutes les notifications. Même si nous les groupons par thread-id (et nous les groupons), tout de même, pour chaque notification, cette extension sera appelée, toutes les vérifications passeront. Il semble peu probable que l'utilisateur ait besoin des cent notifications. Par conséquent, nous générons une notification dans laquelle nous écrivons que vous avez 100 messages manqués, allez dans l'application et regardez. Et nous montrons exactement ce message à l'utilisateur, car nous pouvons remplacer les notifications push.
Une fois tous les contrôles effectués, nous envoyons une demande à XIVA: la dernière position qui nous est parvenue et le nombre de messages manqués. Et regardez:
- Si le XIVA répond avec succès: "Tout va bien, j'enverrai les données", nous montrons à l'utilisateur la notification actuelle et attendons que le XIVA envoie tout le reste et que l'utilisateur voit tous les messages manquants.
- Si XIVA répond avec une erreur, nous montrons à l'utilisateur une notification personnalisée qu'il a manqué des messages qui peuvent être consultés dans l'application.
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()) } }
Ainsi, l'implémentation sur le client se résume à un grand nombre de contrôles dans lesquels nous découvrons si nous pouvons travailler avec les données reçues.
Exploitation forestière et autres difficultés
Comme vous le savez, pour vous assurer que l'approche fonctionne bien, vous devez vous connecter. Nous avons commencé à collecter des statistiques sur une nouvelle méthode de livraison des notifications et à comparer l'évolution de la délivrabilité.
Limitations de l'extension push
La première chose que nous avons rencontrée est les restrictions de push-extension.
Pas toujours appelé . Si vous désactivez le dessin de notification dans les paramètres de l'application (la possibilité de recevoir une notification reste activée, mais tout le rendu possible est désactivé), l'extension ne sera pas appelée - toute la logique avec les recomptes et, surtout, la journalisation ne sera pas appelée. Nous ne pourrons pas savoir ce qui est le plus important pour nous - si l'utilisateur a reçu une notification.
L'extension push a une limite de temps . La documentation Apple indique que dans environ 30 secondes, vous devez appeler la fin avec une notification modifiée, sinon la notification initiale sera affichée.
Je me demande comment nous l'avons compris. Nous avons implémenté une fonctionnalité que nous avons appelée «belles» notifications push, attaché des éléments multimédias aux notifications, changé le titre, le sous-titre. Au cours des tests, il s'est avéré que certaines notifications push sont devenues belles, tandis que le reste est demeuré de vilains canetons.
Nous avons commencé à examiner la différence entre ces notifications push et nous avons découvert qu'il n'y avait pas de différence, pour certains, nous avons réussi à appeler l'achèvement, mais pour d'autres non. , , push- , APNs.
— . Apple , , push-extension, , , . , 12 .
Apple Developer Forum , , . , — 10 .
, . AppMetrica. , AppMetrica , Extension . , - .
: Extension .
push-extension UserDefaults. , , AppMetrica.
. . , , . , . , XIVA ( ), , .
, Notification Extension iOS 10 , Extension, , .
AppMetrica : , push-extension . AppMetrica push-, , . ,
AppMetrica Push SDK .
, . — , . , .

— , , .
, push-, , — .
, , . , …
: , , . , ? - , push-? , ? user experience ?
, 2–3–20 ?
, , , , , , , . , push-. , .
Résumé
Push- iOS . , .. , .
push- ( ) . . XIVA. , , . , , . !
push-extension. , . , .
, . , , , , - . , push- . , , , App Store, , !
AppsConf , 21 22 , .. 50 , . 1 , — .