
AppboyKit (également connu sous le nom de SDK Objective-C) n’est plus pris en charge et a été remplacé par le SDK Swift. Il ne recevra plus de nouvelles fonctionnalités, de corrections de bugs, de mises à jour de sécurité ni d’assistance technique—cependant, l’envoi de messages et l’analyse continueront à fonctionner normalement. Pour en savoir plus, consultez Présentation du nouveau SDK Braze Swift.

Vous recherchez le guide d’intégration de base du développeur de notifications push ? Retrouvez-le dans le guide d’intégration des notifications push iOS.
Guide d’implémentation des notifications push
Ce guide d’implémentation avancé et facultatif explique comment tirer parti des extensions d’application de contenu de notification push pour exploiter au mieux vos messages push. Il contient trois cas d’usage personnalisés créés par notre équipe, les extraits de code l’accompagnant et des directives concernant l’enregistrement des analyses. Visitez le dépôt de démonstrations Braze ! Notez que ce guide d’implémentation est centré autour d’une implémentation Swift, mais les extraits de code Objective-C sont fournis aux personnes intéressées.
Extensions d’application de contenu de notification

Les notifications push, bien qu’elles semblent standard sur les différentes plateformes, offrent d’immenses possibilités de personnalisation au-delà de ce qui est normalement proposé dans l’interface utilisateur par défaut. Lorsqu’une notification push est développée, les extensions de contenu de notification permettent d’afficher une vue personnalisée de la notification push développée.
Les notifications push peuvent être développées de trois manières différentes :
- Un appui long sur la bannière de notification push
- Un balayage vers le bas sur la bannière de notification push
- Un balayage de la bannière et la sélection de « Afficher »
Ces vues personnalisées offrent des moyens intelligents d’engager vos clients en vous permettant d’afficher de nombreux types de contenu distincts, y compris des notifications interactives, des notifications alimentées par les données utilisateur, et même des messages push qui peuvent capturer des informations telles que des numéros de téléphone et des adresses e-mail. Bien que l’implémentation de notifications push de cette manière puisse sembler inhabituelle pour certains, l’une de nos fonctionnalités bien connues chez Braze, Push Stories, est un excellent exemple de ce à quoi peut ressembler une vue personnalisée pour une extension d’application de contenu de notification !
Conditions requises

- Les notifications push correctement intégrées dans votre application
- iOS 10 ou version ultérieure
- Les fichiers suivants générés par Xcode en fonction de votre langage de programmation :
Swift
- NotificationViewController.swift
- MainInterface.storyboard
Objective-C
- NotificationViewController.h
- NotificationViewController.m
- MainInterface.storyboard
Configuration de catégorie personnalisée
Pour configurer une vue personnalisée dans le tableau de bord, vous devez activer les boutons de notification et saisir votre catégorie personnalisée. La catégorie iOS personnalisée pré-enregistrée que vous fournissez est ensuite comparée au UNNotificationExtensionCategory dans le .plist de votre cible Notification Content Extension. La valeur indiquée ici doit correspondre à ce qui est défini dans le tableau de bord de Braze.


Étant donné que les notifications push avec des extensions de contenu ne sont pas toujours évidentes, il est recommandé d’inclure un appel à l’action pour inciter vos utilisateurs à développer leurs notifications push.
Cas d’usage et guide de mise en œuvre
Trois types d’extensions de contenu pour les notifications push sont proposés. Chacun comprend un aperçu du concept, des cas d’usage potentiels et un aperçu de la façon dont les variables de notification push peuvent apparaître et être utilisées dans le tableau de bord de Braze :
- Notification push interactive
- Notifications push personnalisées
- Notifications push de capture d’informations
Notification push interactive
Les notifications push peuvent répondre aux actions des utilisateurs à l’intérieur d’une extension de contenu. Pour les utilisateurs exécutant iOS 12 ou une version ultérieure, cela signifie que vous pouvez transformer vos messages push en notifications push entièrement interactives ! Cette interactivité offre de nombreuses possibilités pour impliquer vos utilisateurs dans vos notifications. L’exemple suivant montre une notification push où les utilisateurs peuvent jouer à un jeu d’association à l’intérieur de la notification étendue.

Configuration du tableau de bord
Pour configurer une vue personnalisée dans le tableau de bord, dans les paramètres du bouton de notification, saisissez la catégorie spécifique que vous souhaitez afficher. Ensuite, dans le fichier .plist de votre extension de contenu de notification, vous devez également définir la catégorie personnalisée dans l’attribut UNNotificationExtensionCategory. La valeur fournie ici doit correspondre à ce qui est défini dans le tableau de bord de Braze. Enfin, pour activer les interactions utilisateur dans une notification push, définissez la clé UNNotificationExtensionInteractionEnabled sur true.


Autres cas d’usage
Les extensions de contenu push sont une option intéressante pour introduire de l’interactivité dans vos promotions et applications. Parmi les exemples, on peut citer un jeu pour les utilisateurs, une roue à tourner pour obtenir des réductions, ou un bouton « J’aime » pour enregistrer une annonce ou une chanson.
Prêt à enregistrer les analyses ?
Consultez la section suivante pour mieux comprendre à quoi le flux de données devrait ressembler.
Notifications push personnalisées

Les notifications push peuvent afficher des informations spécifiques à l’utilisateur à l’intérieur d’une extension de contenu. L’exemple ci-joint montre une notification push après qu’un utilisateur a terminé une tâche spécifique (un cours Braze Learning) et est désormais encouragé à étendre cette notification pour consulter sa progression. Les informations fournies ici sont spécifiques à l’utilisateur et peuvent être déclenchées lorsqu’une session est terminée ou qu’une action utilisateur spécifique est effectuée en exploitant un déclencheur API.
Configuration du tableau de bord
Pour configurer une notification push personnalisée dans le tableau de bord, vous devez enregistrer la catégorie spécifique que vous souhaitez afficher, puis dans les paires clé-valeur, en utilisant le Liquid standard, définir les attributs utilisateur appropriés que vous souhaitez afficher dans le message. Ces vues peuvent être personnalisées en fonction d’attributs utilisateur spécifiques d’un profil utilisateur donné.

Gestion des paires clé-valeur
La méthode suivante, didReceive, est appelée lorsque l’extension de contenu a reçu une notification ; elle se trouve dans le NotificationViewController. Les paires clé-valeur fournies dans le tableau de bord sont représentées dans le code via l’utilisation d’un dictionnaire userInfo.
Analyse des paires clé-valeur à partir des notifications push
func didReceive(_ notification: UNNotification) {
let userInfo = notification.request.content.userInfo
guard let value = userInfo["YOUR-KEY-VALUE-PAIR"] as? String,
let otherValue = userInfo["YOUR-OTHER-KEY-VALUE-PAIR"] as? String,
else { fatalError("Key-Value Pairs are incorrect.")}
...
}
- (void)didReceiveNotification:(nonnull UNNotification *)notification {
NSDictionary *userInfo = notification.request.content.userInfo;
if (userInfo[@"YOUR-KEY-VALUE-PAIR"] && userInfo[@"YOUR-OTHER-KEY-VALUE-PAIR"]) {
...
} else {
[NSException raise:NSGenericException format:@"Key-Value Pairs are incorrect"];
}
}
Autres cas d’usage
Les possibilités pour les extensions de contenu push basées sur la progression et centrées sur l’utilisateur sont infinies. Parmi les exemples, citons l’ajout d’une option pour partager votre progression sur différentes plateformes, l’affichage des réalisations débloquées, les cartes de fidélité ou même les listes de vérification d’onboarding.
Prêt à enregistrer les analyses ?
Consultez la section suivante pour mieux comprendre à quoi le flux de données devrait ressembler.
Notification push de capture d’informations
Les notifications push peuvent capturer des informations utilisateur à l’intérieur d’une extension de contenu, vous permettant de repousser les limites de ce qui est possible avec une notification push. En examinant le flux suivant, la vue est capable de répondre aux changements d’état. Ces composants de changement d’état sont représentés dans chaque image.
- L’utilisateur reçoit une notification push.
- La notification push est ouverte et invite l’utilisateur à fournir des informations.
- Les informations sont fournies et, si elles sont valides, le bouton d’inscription s’affiche.
- La vue de confirmation est affichée et la notification push est fermée.
Notez que les informations demandées ici peuvent être très variées, comme la capture d’un numéro SMS ; elles ne doivent pas nécessairement être spécifiques à l’e-mail.
Configuration du tableau de bord
Pour configurer une notification push capable de capturer des informations dans le tableau de bord, vous devez enregistrer et définir votre catégorie personnalisée, et fournir les paires clé-valeur nécessaires. Comme le montre l’exemple, vous pouvez également inclure une image dans votre notification push. Pour ce faire, vous devez intégrer les notifications enrichies, définir le style de notification dans votre campagne sur Rich Notification et inclure une image push enrichie.

Gestion des actions des boutons
Chaque bouton d’action est identifié de manière unique. Le code vérifie si votre identifiant de réponse est égal à l’actionIdentifier, et si c’est le cas, sait que l’utilisateur a cliqué sur le bouton d’action.
Gestion des réponses aux boutons d’action des notifications push
func didReceive(_ response: UNNotificationResponse, completionHandler completion: @escaping (UNNotificationContentExtensionResponseOption) -> Void) {
if response.actionIdentifier == "YOUR-REGISTER-IDENTIFIER" {
// do something
} else {
// do something else
}
}
- (void)didReceiveNotificationResponse:(UNNotificationResponse *)response completionHandler:(void (^)(UNNotificationContentExtensionResponseOption))completion {
if ([response.actionIdentifier isEqualToString:@"YOUR-REGISTER-IDENTIFIER"]) {
completion(UNNotificationContentExtensionResponseOptionDismiss);
} else {
completion(UNNotificationContentExtensionResponseOptionDoNotDismiss);
}
}
Fermeture des notifications push
Les notifications push peuvent être automatiquement fermées lors de l’appui sur un bouton d’action. Il existe trois options de fermeture de notification push prédéfinies qui sont recommandées :
completion(.dismiss)- Ferme la notificationcompletion(.doNotDismiss)- La notification reste ouvertecompletion(.dismissAndForward)- La notification push se ferme et l’utilisateur est redirigé vers l’application.
Autres cas d’usage
Demander des informations aux utilisateurs via les notifications push est une opportunité intéressante que de nombreuses entreprises n’exploitent pas. Dans ces messages push, vous pouvez non seulement demander des informations de base comme le nom, l’e-mail ou le numéro de téléphone, mais vous pouvez également inviter les utilisateurs à compléter leur profil utilisateur s’il est incomplet, ou même à soumettre des commentaires.
Prêt à enregistrer les analyses ?
Consultez la section suivante pour mieux comprendre à quoi le flux de données devrait ressembler.
Enregistrement des analyses
Enregistrement avec l’API Braze (recommandé)
L’enregistrement des analyses ne peut se faire qu’en temps réel, à l’aide du serveur du client qui appelle notre endpoint /users/track. Pour enregistrer les analyses, envoyez la valeur braze_id dans le champ des paires clé-valeur (comme illustré dans la capture d’écran suivante) afin d’identifier le profil utilisateur à mettre à jour.

Enregistrement manuel
L’enregistrement manuel nécessite d’abord de configurer les App Groups dans Xcode, puis de créer, enregistrer et récupérer les analyses. Cela implique un travail de développement personnalisé de votre côté. Les extraits de code suivants vous aideront à résoudre ce problème.
Il est également important de noter que les analyses ne sont pas envoyées à Braze tant que l’application mobile n’est pas relancée par la suite. Cela signifie que, selon vos paramètres de rejet, il existe souvent une période indéterminée entre le moment où une notification push est rejetée et celui où l’application mobile est lancée et où les analyses sont récupérées. Bien que ce délai n’affecte pas tous les cas d’usage, les utilisateurs doivent considérer son impact et, si nécessaire, ajuster leur parcours utilisateur pour inclure l’ouverture de l’application afin de résoudre ce problème.

Étape 1 : Configurer les App Groups dans Xcode
Ajoutez la capacité App Groups. Si vous n’avez aucun groupe d’applications dans votre application, accédez aux capacités de la cible de l’application principale, activez App Groups, puis cliquez sur « + ». Utilisez le bundle ID de votre application pour créer le groupe d’applications. Par exemple, si le bundle ID de votre application est com.company.appname, vous pouvez nommer votre groupe d’applications group.com.company.appname.xyz. Assurez-vous que App Groups est activé à la fois pour la cible de l’application principale et pour la cible de l’extension de contenu.

Étape 2 : Intégrer les extraits de code
Les extraits de code suivants constituent une référence utile pour enregistrer et envoyer des événements personnalisés, des attributs personnalisés et des attributs utilisateur. Ce guide s’exprimera en termes de UserDefaults, mais la représentation du code se présentera sous la forme d’un fichier utilitaire RemoteStorage. Il existe également des fichiers utilitaires supplémentaires UserAttributes et EventName Dictionary qui sont utilisés lors de l’envoi et de l’enregistrement des attributs utilisateur. Tous les fichiers utilitaires se trouvent à la fin de ce guide.
Enregistrement des événements personnalisés
Pour enregistrer des événements personnalisés, vous devez créer les analyses dans un nouveau design. Pour ce faire, créez un dictionnaire, remplissez-le de métadonnées et enregistrez les données via un fichier utilitaire.
- Initialisez un dictionnaire avec les métadonnées de l’événement
- Initialisez
userDefaultspour récupérer et stocker les données de l’événement - S’il existe un tableau existant, ajoutez les nouvelles données au tableau existant et enregistrez
- S’il n’y a pas de tableau existant, enregistrez le nouveau tableau dans
userDefaults
func saveCustomEvent(with properties: [String: Any]? = nil) {
// 1
let customEventDictionary = Dictionary(eventName: "YOUR-EVENT-NAME", properties: properties)
// 2
let remoteStorage = RemoteStorage(storageType: .suite)
// 3
if var pendingEvents = remoteStorage.retrieve(forKey: .pendingCustomEvents) as? [[String: Any]] {
pendingEvents.append(contentsOf: [customEventDictionary])
remoteStorage.store(pendingEvents, forKey: .pendingCustomEvents)
} else {
// 4
remoteStorage.store([customEventDictionary], forKey: .pendingCustomEvents)
}
}
- (void)saveCustomEvent:(NSDictionary<NSString *, id> *)properties {
// 1
NSDictionary<NSString *, id> *customEventDictionary = [[NSDictionary alloc] initWithEventName:@"YOUR-EVENT-NAME" properties:properties];
// 2
RemoteStorage *remoteStorage = [[RemoteStorage alloc] initWithStorageType:StorageTypeSuite];
NSMutableArray *pendingEvents = [[remoteStorage retrieveForKey:RemoteStorageKeyPendingCustomEvents] mutableCopy];
// 3
if (pendingEvents) {
[pendingEvents addObject:customEventDictionary];
[remoteStorage store:pendingEvents forKey:RemoteStorageKeyPendingCustomAttributes];
} else {
// 4
[remoteStorage store:@[ customEventDictionary ] forKey:RemoteStorageKeyPendingCustomAttributes];
}
}
Envoi d’événements personnalisés à Braze
Une fois le SDK initialisé, c’est le meilleur moment pour enregistrer toutes les analyses sauvegardées depuis une extension de contenu de notification. Pour ce faire, parcourez les événements en attente, vérifiez la clé « Event Name », définissez les valeurs appropriées dans Braze, puis videz le stockage pour la prochaine fois que cette fonction sera nécessaire.
- Parcourez le tableau des événements en attente
- Parcourez chaque paire clé-valeur du dictionnaire
pendingEvents - Vérifiez explicitement la clé « Event Name » pour définir la valeur en conséquence
- Toute autre paire clé-valeur sera ajoutée au dictionnaire
properties - Enregistrez chaque événement personnalisé individuel
- Supprimez tous les événements en attente du stockage
func logPendingCustomEventsIfNecessary() {
let remoteStorage = RemoteStorage(storageType: .suite)
guard let pendingEvents = remoteStorage.retrieve(forKey: .pendingCustomEvents) as? [[String: Any]] else { return }
// 1
for event in pendingEvents {
var eventName: String?
var properties: [AnyHashable: Any] = [:]
// 2
for (key, value) in event {
if key == PushNotificationKey.eventName.rawValue {
// 3
if let eventNameValue = value as? String {
eventName = eventNameValue
} else {
print("Invalid type for event_name key")
}
} else {
// 4
properties[key] = value
}
}
// 5
if let eventName = eventName {
logCustomEvent(eventName, withProperties: properties)
}
}
// 6
remoteStorage.removeObject(forKey: .pendingCustomEvents)
}
- (void)logPendingEventsIfNecessary {
RemoteStorage *remoteStorage = [[RemoteStorage alloc] initWithStorageType:StorageTypeSuite];
NSArray *pendingEvents = [remoteStorage retrieveForKey:RemoteStorageKeyPendingCustomEvents];
// 1
for (NSDictionary<NSString *, id> *event in pendingEvents) {
NSString *eventName = nil;
NSMutableDictionary *properties = [NSMutableDictionary dictionary];
// 2
for (NSString* key in event) {
if ([key isEqualToString:@"event_name"]) {
// 3
if ([[event objectForKey:key] isKindOfClass:[NSString class]]) {
eventName = [event objectForKey:key];
} else {
NSLog(@"Invalid type for event_name key");
}
} else {
// 4
properties[key] = event[key];
}
}
// 5
if (eventName != nil) {
[[Appboy sharednstance] logCustomEvent:eventName withProperties:properties];
}
}
// 6
[remoteStorage removeObjectForKey:RemoteStorageKeyPendingCustomEvents];
}
Enregistrement des attributs personnalisés
Pour enregistrer des attributs personnalisés, vous devez créer les analyses dans un nouveau design. Pour ce faire, créez un dictionnaire, remplissez-le de métadonnées et enregistrez les données à l’aide d’un fichier utilitaire.
- Initialisez un dictionnaire avec les métadonnées de l’attribut
- Initialisez
userDefaultspour récupérer et stocker les données de l’attribut - S’il existe un tableau existant, ajoutez les nouvelles données au tableau existant et enregistrez
- S’il n’y a pas de tableau existant, enregistrez le nouveau tableau dans
userDefaults
func saveCustomAttribute() {
// 1
let customAttributeDictionary: [String: Any] = ["YOUR-CUSTOM-ATTRIBUTE-KEY": "YOUR-CUSTOM-ATTRIBUTE-VALUE"]
// 2
let remoteStorage = RemoteStorage(storageType: .suite)
// 3
if var pendingAttributes = remoteStorage.retrieve(forKey: .pendingCustomAttributes) as? [[String: Any]] {
pendingAttributes.append(contentsOf: [customAttributeDictionary])
remoteStorage.store(pendingAttributes, forKey: .pendingCustomAttributes)
} else {
// 4
remoteStorage.store([customAttributeDictionary], forKey: .pendingCustomAttributes)
}
}
- (void)saveCustomAttribute {
// 1
NSDictionary<NSString *, id> *customAttributeDictionary = @{ @"YOUR-CUSTOM-ATTRIBUTE-KEY": @"YOUR-CUSTOM-ATTRIBUTE-VALUE" };
// 2
RemoteStorage *remoteStorage = [[RemoteStorage alloc] initWithStorageType:StorageTypeSuite];
NSMutableArray *pendingAttributes = [[remoteStorage retrieveForKey:RemoteStorageKeyPendingCustomAttributes] mutableCopy];
// 3
if (pendingAttributes) {
[pendingAttributes addObject:customAttributeDictionary];
[remoteStorage store:pendingAttributes forKey:RemoteStorageKeyPendingCustomAttributes];
} else {
// 4
[remoteStorage store:@[ customAttributeDictionary ] forKey:RemoteStorageKeyPendingCustomAttributes];
}
}
Envoi d’attributs personnalisés à Braze
Une fois le SDK initialisé, c’est le meilleur moment pour enregistrer toutes les analyses sauvegardées depuis une extension de contenu de notification. Pour ce faire, parcourez les attributs en attente, définissez l’attribut personnalisé approprié dans Braze, puis videz le stockage pour la prochaine fois que cette fonction sera nécessaire.
- Parcourez le tableau des attributs en attente
- Parcourez chaque paire clé-valeur du dictionnaire
pendingAttributes - Enregistrez chaque attribut personnalisé individuel avec la clé et la valeur correspondantes
- Supprimez tous les attributs en attente du stockage
func logPendingCustomAttributesIfNecessary() {
let remoteStorage = RemoteStorage(storageType: .suite)
guard let pendingAttributes = remoteStorage.retrieve(forKey: .pendingCustomAttributes) as? [[String: Any]] else { return }
// 1
pendingAttributes.forEach { setCustomAttributesWith(keysAndValues: $0) }
// 4
remoteStorage.removeObject(forKey: .pendingCustomAttributes)
}
func setCustomAttributesWith(keysAndValues: [String: Any]) {
// 2
for (key, value) in keysAndValues {
// 3
if let value = value as? [String] {
setCustomAttributeArrayWithKey(key, andValue: value)
} else {
setCustomAttributeWithKey(key, andValue: value)
}
}
}
- (void)logPendingCustomAttributesIfNecessary {
RemoteStorage *remoteStorage = [[RemoteStorage alloc] initWithStorageType:StorageTypeSuite];
NSArray *pendingAttributes = [remoteStorage retrieveForKey:RemoteStorageKeyPendingCustomAttributes];
// 1
for (NSDictionary<NSString*, id> *attribute in pendingAttributes) {
[self setCustomAttributeWith:attribute];
}
// 4
[remoteStorage removeObjectForKey:RemoteStorageKeyPendingCustomAttributes];
}
- (void)setCustomAttributeWith:(NSDictionary<NSString *, id> *)keysAndValues {
// 2
for (NSString *key in keysAndValues) {
// 3
[self setCustomAttributeWith:key andValue:[keysAndValues objectForKey:key]];
}
}
Enregistrement des attributs utilisateur
Lors de l’enregistrement des attributs utilisateur, il est recommandé de créer un objet personnalisé pour déterminer quel type d’attribut est mis à jour (email, first_name, phone_number, etc.). L’objet doit être compatible avec le stockage/la récupération depuis UserDefaults. Consultez le fichier utilitaire UserAttribute pour un exemple de mise en œuvre.
- Initialisez un objet
UserAttributeencodé avec le type correspondant - Initialisez
userDefaultspour récupérer et stocker les données de l’événement - S’il existe un tableau existant, ajoutez les nouvelles données au tableau existant et enregistrez
- S’il n’y a pas de tableau existant, enregistrez le nouveau tableau dans
userDefaults
func saveUserAttribute() {
// 1
guard let data = try? PropertyListEncoder().encode(UserAttribute.userAttributeType("USER-ATTRIBUTE-VALUE")) else { return }
// 2
let remoteStorage = RemoteStorage(storageType: .suite)
// 3
if var pendingAttributes = remoteStorage.retrieve(forKey: .pendingUserAttributes) as? [Data] {
pendingAttributes.append(contentsOf: [data])
remoteStorage.store(pendingAttributes, forKey: .pendingUserAttributes)
} else {
// 4
remoteStorage.store([data], forKey: .pendingUserAttributes)
}
}
- (void)saveUserAttribute {
// 1
UserAttribute *userAttribute = [[UserAttribute alloc] initWithUserField:@"USER-ATTRIBUTE-VALUE" attributeType:UserAttributeTypeEmail];
NSError *error;
NSData *data = [NSKeyedArchiver archivedDataWithRootObject:userAttribute requiringSecureCoding:YES error:&error];
if (error != nil) {
// log error
}
// 2
RemoteStorage *remoteStorage = [[RemoteStorage alloc] initWithStorageType:StorageTypeSuite];
NSMutableArray *pendingAttributes = [[remoteStorage retrieveForKey:RemoteStorageKeyPendingUserAttributes] mutableCopy];
// 3
if (pendingAttributes) {
[pendingAttributes addObject:data];
[remoteStorage store:pendingAttributes forKey:RemoteStorageKeyPendingUserAttributes];
} else {
// 4
[remoteStorage store:@[data] forKey:RemoteStorageKeyPendingUserAttributes];
}
}
Envoi d’attributs utilisateur à Braze
Une fois le SDK initialisé, c’est le meilleur moment pour enregistrer toutes les analyses sauvegardées depuis une extension de contenu de notification. Pour ce faire, parcourez les attributs en attente, définissez l’attribut personnalisé approprié dans Braze, puis videz le stockage pour la prochaine fois que cette fonction sera nécessaire.
- Parcourez le tableau de données
pendingAttributes - Initialisez un objet
UserAttributeencodé à partir des données d’attribut - Définissez le champ utilisateur spécifique en fonction du type d’attribut utilisateur (e-mail)
- Supprimez tous les attributs utilisateur en attente du stockage
func logPendingUserAttributesIfNecessary() {
let remoteStorage = RemoteStorage(storageType: .suite)
guard let pendingAttributes = remoteStorage.retrieve(forKey: .pendingUserAttributes) as? [Data] else { return }
// 1
for attributeData in pendingAttributes {
// 2
guard let userAttribute = try? PropertyListDecoder().decode(UserAttribute.self, from: attributeData) else { continue }
// 3
switch userAttribute {
case .email(let email):
user?.email = email
}
}
// 4
remoteStorage.removeObject(forKey: .pendingUserAttributes)
}
- (void)logPendingUserAttributesIfNecessary {
RemoteStorage *remoteStorage = [[RemoteStorage alloc] initWithStorageType:StorageTypeSuite];
NSArray *pendingAttributes = [remoteStorage retrieveForKey:RemoteStorageKeyPendingUserAttributes];
// 1
for (NSData *attributeData in pendingAttributes) {
NSError *error;
// 2
UserAttribute *userAttribute = [NSKeyedUnarchiver unarchivedObjectOfClass:[UserAttribute class] fromData:attributeData error:&error];
if (error != nil) {
// log error
}
// 3
if (userAttribute) {
switch (userAttribute.attributeType) {
case UserAttributeTypeEmail:
[self user].email = userAttribute.userField;
break;
}
}
}
// 4
[remoteStorage removeObjectForKey:RemoteStorageKeyPendingUserAttributes];
}
Fichiers utilitaires
Fichier utilitaire RemoteStorage
enum RemoteStorageKey: String, CaseIterable {
// MARK: - Notification Content Extension Analytics
case pendingCustomEvents = "pending_custom_events"
case pendingCustomAttributes = "pending_custom_attributes"
case pendingUserAttributes = "pending_user_attributes"
}
enum RemoteStorageType {
case standard
case suite
}
class RemoteStorage: NSObject {
private var storageType: RemoteStorageType = .standard
private lazy var defaults: UserDefaults = {
switch storageType {
case .standard:
return .standard
case .suite:
return UserDefaults(suiteName: "YOUR-DOMAIN-IDENTIFIER")!
}
}()
init(storageType: RemoteStorageType = .standard) {
self.storageType = storageType
}
func store(_ value: Any, forKey key: RemoteStorageKey) {
defaults.set(value, forKey: key.rawValue)
}
func retrieve(forKey key: RemoteStorageKey) -> Any? {
return defaults.object(forKey: key.rawValue)
}
func removeObject(forKey key: RemoteStorageKey) {
defaults.removeObject(forKey: key.rawValue)
}
func resetStorageKeys() {
for key in RemoteStorageKey.allCases {
defaults.removeObject(forKey: key.rawValue)
}
}
}
@interface RemoteStorage ()
@property (nonatomic) StorageType storageType;
@property (nonatomic, strong) NSUserDefaults *defaults;
@end
@implementation RemoteStorage
- (id)initWithStorageType:(StorageType)storageType {
if (self = [super init]) {
self.storageType = storageType;
}
return self;
}
- (void)store:(id)value forKey:(RemoteStorageKey)key {
[[self defaults] setValue:value forKey:[self rawValueForKey:key]];
}
- (id)retrieveForKey:(RemoteStorageKey)key {
return [[self defaults] objectForKey:[self rawValueForKey:key]];
}
- (void)removeObjectForKey:(RemoteStorageKey)key {
[[self defaults] removeObjectForKey:[self rawValueForKey:key]];
}
- (void)resetStorageKeys {
[[self defaults] removeObjectForKey:[self rawValueForKey:RemoteStorageKeyPendingCustomEvents]];
[[self defaults] removeObjectForKey:[self rawValueForKey:RemoteStorageKeyPendingCustomAttributes]];
[[self defaults] removeObjectForKey:[self rawValueForKey:RemoteStorageKeyPendingUserAttributes]];
}
- (NSUserDefaults *)defaults {
if (!self.defaults) {
switch (self.storageType) {
case StorageTypeStandard:
return [NSUserDefaults standardUserDefaults];
break;
case StorageTypeSuite:
return [[NSUserDefaults alloc] initWithSuiteName:@"YOUR-DOMAIN-IDENTIFIER"];
}
} else {
return self.defaults;
}
}
- (NSString*)rawValueForKey:(RemoteStorageKey)remoteStorageKey {
switch(remoteStorageKey) {
case RemoteStorageKeyPendingCustomEvents:
return @"pending_custom_events";
case RemoteStorageKeyPendingCustomAttributes:
return @"pending_custom_attributes";
case RemoteStorageKeyPendingUserAttributes:
return @"pending_user_attributes";
default:
[NSException raise:NSGenericException format:@"Unexpected FormatType."];
}
}
Fichier utilitaire UserAttribute
enum UserAttribute: Hashable {
case email(String?)
}
// MARK: - Codable
extension UserAttribute: Codable {
private enum CodingKeys: String, CodingKey {
case email
}
func encode(to encoder: Encoder) throws {
var values = encoder.container(keyedBy: CodingKeys.self)
switch self {
case .email(let email):
try values.encode(email, forKey: .email)
}
}
init(from decoder: Decoder) throws {
let values = try decoder.container(keyedBy: CodingKeys.self)
let email = try values.decode(String.self, forKey: .email)
self = .email(email)
}
}
@implementation UserAttribute
- (id)initWithUserField:(NSString *)userField attributeType:(UserAttributeType)attributeType {
if (self = [super init]) {
self.userField = userField;
self.attributeType = attributeType;
}
return self;
}
- (void)encodeWithCoder:(NSCoder *)encoder {
[encoder encodeObject:self.userField forKey:@"userField"];
[encoder encodeInteger:self.attributeType forKey:@"attributeType"];
}
- (id)initWithCoder:(NSCoder *)decoder {
if (self = [super init]) {
self.userField = [decoder decodeObjectForKey:@"userField"];
NSInteger attributeRawValue = [decoder decodeIntegerForKey:@"attributeType"];
self.attributeType = (UserAttributeType) attributeRawValue;
}
return self;
}
@end
Fichier utilitaire EventName Dictionary
extension Dictionary where Key == String, Value == Any {
init(eventName: String, properties: [String: Any]? = nil) {
self.init()
self[PushNotificationKey.eventName.rawValue] = eventName
if let properties = properties {
for (key, value) in properties {
self[key] = value
}
}
}
}
@implementation NSDictionary (Helper)
- (id)initWithEventName:(NSString *)eventName properties:(NSDictionary *)properties {
self = [self init];
if (self) {
dict[@"event_name"] = eventName;
for(id key in properties) {
dict[key] = properties[key];
}
}
return self;
}
@end