Skip to content

Analytique push et journalisation d’événements personnalisés

Cette page couvre les workflows suivants : l’analytique push native (ouvertures, ouvertures influencées et rapports de campagne) et la journalisation de données personnalisées (événements personnalisés et attributs) à partir des payloads push. Utilisez ce guide pour identifier le workflow qui correspond à votre cas d’utilisation et suivez les étapes pour votre plateforme.

Conditions préalables

Avant de commencer, effectuez l’intégration initiale des notifications push pour votre plateforme :

Analytique push native vs. journalisation d’événements personnalisés

Les workflows suivants ont chacun des surfaces de reporting différentes.

Ce que Braze enregistre automatiquement

Lorsque votre intégration SDK est configurée, Braze enregistre automatiquement les données d’interaction de base du canal, y compris les ouvertures push et les ouvertures influencées. Aucun code supplémentaire n’est nécessaire pour l’analytique push standard. Pour une liste complète des données collectées automatiquement, consultez Collecte de données du SDK.

Pour plus de détails, consultez les ressources suivantes :

Préserver l’analytique push native avec un gestionnaire push personnalisé

Vous pouvez utiliser un gestionnaire push personnalisé lorsque vous devez intégrer plusieurs fournisseurs push, traiter des données de payload supplémentaires ou implémenter une logique d’affichage de notification personnalisée. Si vous utilisez un gestionnaire push personnalisé, vous devez tout de même transmettre les payloads push aux méthodes du SDK de Braze. Cela permet à Braze d’extraire les données de suivi intégrées et de journaliser l’analytique push native (ouvertures, ouvertures influencées et indicateurs de distribution).

Si vous avez un FirebaseMessagingService personnalisé, appelez BrazeFirebaseMessagingService.handleBrazeRemoteMessage(...) dans votre méthode onMessageReceived. Gardez à l’esprit que votre sous-classe FirebaseMessagingService doit terminer son exécution dans les 9 secondes suivant l’invocation pour éviter d’être signalée ou terminée par le système Android.

1
2
3
4
5
6
7
8
9
10
11
public class MyFirebaseMessagingService extends FirebaseMessagingService {
  @Override
  public void onMessageReceived(RemoteMessage remoteMessage) {
    super.onMessageReceived(remoteMessage);
    if (BrazeFirebaseMessagingService.handleBrazeRemoteMessage(this, remoteMessage)) {
      // Braze processed a Braze push payload.
    } else {
      // Non-Braze payload: pass to your other handlers.
    }
  }
}

Pour un exemple d’implémentation complet, consultez l’application exemple Firebase push du SDK Android de Braze.

Dans une intégration push manuelle, transmettez les rappels de notifications en arrière-plan et de notifications utilisateur à Braze.

Notifications en arrière-plan :

1
2
3
4
5
6
7
if let braze = AppDelegate.braze, braze.notifications.handleBackgroundNotification(
  userInfo: userInfo,
  fetchCompletionHandler: completionHandler
) {
  return
}
completionHandler(.noData)

Réponses aux notifications utilisateur :

1
2
3
4
5
6
7
if let braze = AppDelegate.braze, braze.notifications.handleUserNotification(
  response: response,
  withCompletionHandler: completionHandler
) {
  return
}
completionHandler()

Notifications au premier plan :

1
2
3
4
5
6
7
8
9
10
11
12
13
14
func userNotificationCenter(
  _ center: UNUserNotificationCenter,
  willPresent notification: UNNotification,
  withCompletionHandler completionHandler: @escaping (UNNotificationPresentationOptions) -> Void
) {
  if let braze = AppDelegate.braze {
    braze.notifications.handleForegroundNotification(notification: notification)
  }
  if #available(iOS 14.0, *) {
    completionHandler([.banner, .list, .sound])
  } else {
    completionHandler([.alert, .sound])
  }
}

Pour un exemple d’implémentation complet, consultez l’exemple push manuel du SDK Swift de Braze (AppDelegate.swift).

Pour le push web, configurez votre service de traitement et l’initialisation du SDK comme décrit dans Notifications push Web.

Pour plus d’exemples de code, consultez le dépôt du SDK Web de Braze.

Journaliser des données personnalisées à partir des payloads push

Utilisez cette section lorsque vous devez journaliser des données supplémentaires à partir des paires clé-valeur du payload push, telles que des événements personnalisés ou des attributs liés à votre logique métier.

Pour plus d’informations sur les événements personnalisés, consultez Événements personnalisés. Pour journaliser des événements personnalisés via les méthodes du SDK, consultez Journaliser des événements personnalisés.

Option A : Journaliser avec l’endpoint /users/track

Vous pouvez journaliser des données analytiques en temps réel en appelant l’endpoint /users/track.

Pour identifier le profil utilisateur, incluez braze_id dans les paires clé-valeur de votre payload push.

Option B : Journaliser avec les méthodes du SDK après le lancement de l’application

Vous pouvez également enregistrer les données du payload localement et journaliser les événements personnalisés et les attributs via les méthodes du SDK après l’initialisation de l’application. Cette approche est courante dans les flux d’extension de contenu de notification, où les données analytiques sont d’abord persistées puis envoyées au prochain lancement de l’application.

Journaliser depuis une extension de contenu de notification (Swift)

Les étapes suivantes décrivent comment enregistrer et envoyer des événements personnalisés, des attributs personnalisés et des attributs utilisateur depuis une extension de contenu de notification Swift.

Étape 1 : Configurer les groupes d’applications dans Xcode

Dans Xcode, ajoutez la capacité App Groups à la cible de votre application principale. Activez App Groups, puis cliquez sur + pour ajouter un nouveau groupe. Utilisez l’identifiant de bundle de votre application pour créer l’identifiant du groupe (par exemple, group.com.company.appname.xyz). Activez App Groups à la fois pour la cible de votre application principale et pour la cible de l’extension de contenu.

Xcode montrant la capacité App Groups activée pour l'application principale et l'extension de notification

Étape 2 : Choisir ce que vous souhaitez journaliser

Avant d’implémenter les extraits de code, choisissez la catégorie d’analytique que vous souhaitez journaliser :

  • Événements personnalisés : Actions effectuées par les utilisateurs (par exemple, compléter un flux ou appuyer sur un élément d’interface spécifique). Utilisez les événements personnalisés pour les déclencheurs basés sur des actions, la segmentation et l’analytique des événements. Pour plus d’informations, consultez Événements personnalisés et Journaliser des événements personnalisés.
  • Attributs personnalisés : Champs de profil que vous définissez (par exemple, plan_tier ou preferred_language) et mettez à jour au fil du temps. Pour plus d’informations, consultez Attributs personnalisés et Définir les attributs utilisateur.
  • Attributs utilisateur : Champs de profil standard (par exemple, e-mail, prénom et numéro de téléphone). Dans l’exemple de code, ceux-ci sont représentés par un modèle typé UserAttribute puis mappés aux champs utilisateur de Braze.

Les fichiers utilitaires de cette section (RemoteStorage, UserAttribute et EventName Dictionary) sont des fichiers utilitaires locaux utilisés par cet exemple d’implémentation. Ce ne sont pas des classes intégrées au SDK. Ils stockent les données dérivées du payload dans UserDefaults, définissent un modèle typé pour les mises à jour utilisateur en attente et standardisent la construction du payload d’événement. Pour plus d’informations sur le comportement du stockage local des données, consultez Stockage.

Enregistrer des événements personnalisés

Créez le payload analytique en construisant un dictionnaire, en renseignant les métadonnées et en l’enregistrant avec le fichier utilitaire.

  1. Initialisez un dictionnaire avec les métadonnées de l’événement.
  2. Initialisez userDefaults pour récupérer et stocker les données d’événement.
  3. Si un tableau existant est trouvé, ajoutez-y les données et enregistrez.
  4. Si aucun tableau n’existe, enregistrez un nouveau tableau.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
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)
  }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
- (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:RemoteStorageKeyPendingCustomEvents];
  } else {
    // 4
    [remoteStorage store:@[ customEventDictionary ] forKey:RemoteStorageKeyPendingCustomEvents];
  }
}

Envoyer des événements personnalisés à Braze

Journalisez les données analytiques enregistrées juste après l’initialisation du SDK.

  1. Parcourez les événements en attente.
  2. Parcourez les paires clé-valeur de chaque événement.
  3. Vérifiez la présence de la clé event_name.
  4. Ajoutez les paires clé-valeur restantes au dictionnaire properties.
  5. Journalisez chaque événement personnalisé.
  6. Supprimez les événements en attente du stockage.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
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 == "event_name" {
        // 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 {
      AppDelegate.braze?.logCustomEvent(name: eventName, properties: properties)
    }
  }
  
  // 6
  remoteStorage.removeObject(forKey: .pendingCustomEvents)
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
- (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) {
      [AppDelegate.braze logCustomEvent:eventName properties:properties];
    }
  }
  
  // 6
  [remoteStorage removeObjectForKey:RemoteStorageKeyPendingCustomEvents];
}

Enregistrer des attributs personnalisés

Créez le dictionnaire analytique de zéro, puis persistez-le.

  1. Initialisez un dictionnaire avec les métadonnées de l’attribut.
  2. Initialisez userDefaults pour récupérer et stocker les données d’attribut.
  3. Si un tableau existant est trouvé, ajoutez-y les données et enregistrez.
  4. Si aucun tableau n’existe, enregistrez un nouveau tableau.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
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)
  }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
- (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];
  }
}

Envoyer des attributs personnalisés à Braze

Journalisez les données analytiques enregistrées juste après l’initialisation du SDK.

  1. Parcourez les attributs en attente.
  2. Parcourez chaque paire clé-valeur.
  3. Journalisez chaque clé et valeur d’attribut personnalisé.
  4. Supprimez les attributs en attente du stockage.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
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)
    }
  }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
- (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]];
  }
}

Enregistrer des attributs utilisateur

Lors de l’enregistrement des attributs utilisateur, créez un objet personnalisé pour capturer quel champ utilisateur est mis à jour (email, first_name, phone_number, etc.). L’objet doit être compatible avec le stockage et la récupération via UserDefaults. Consultez le fichier utilitaire UserAttribute dans l’onglet Fichiers utilitaires pour un exemple.

  1. Initialisez un objet UserAttribute encodé avec le type correspondant.
  2. Initialisez userDefaults pour récupérer et stocker les données.
  3. Si un tableau existant est trouvé, ajoutez-y les données et enregistrez.
  4. Si aucun tableau n’existe, enregistrez un nouveau tableau.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
func saveUserAttribute() {
  // 1
  guard let data = try? PropertyListEncoder().encode(UserAttribute.email("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)
  }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
- (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];
  }
}

Envoyer des attributs utilisateur à Braze

Journalisez les données analytiques enregistrées juste après l’initialisation du SDK.

  1. Parcourez les données pendingAttributes.
  2. Décodez chaque UserAttribute.
  3. Définissez les champs utilisateur en fonction du type d’attribut.
  4. Supprimez les attributs utilisateur en attente du stockage.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
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):
      AppDelegate.braze?.user.set(email: email)
    }
  }
  // 4
  remoteStorage.removeObject(forKey: .pendingUserAttributes)
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
- (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];
}

Fichier utilitaire RemoteStorage

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
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:
      // Use the App Group identifier you created in Step 1.
      return UserDefaults(suiteName: "group.com.company.appname.xyz")!
    }
  }()
   
  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)
    }
  }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
@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 (!_defaults) {
    switch (self.storageType) {
      case StorageTypeStandard:
        _defaults = [NSUserDefaults standardUserDefaults];
        break;
      case StorageTypeSuite:
        _defaults = [[NSUserDefaults alloc] initWithSuiteName:@"group.com.company.appname.xyz"];
        break;
    }
  }
  return _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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
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.encodeIfPresent(email, forKey: .email)
    }
  }
   
  init(from decoder: Decoder) throws {
    let values = try decoder.container(keyedBy: CodingKeys.self)
     
    let email = try values.decodeIfPresent(String.self, forKey: .email)
    self = .email(email)
  }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
@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

1
2
3
4
5
6
7
8
9
10
11
12
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
      }
    }
  }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
@implementation NSMutableDictionary (Helper)

+ (instancetype)dictionaryWithEventName:(NSString *)eventName
                              properties:(NSDictionary *)properties {
  NSMutableDictionary *dict = [NSMutableDictionary dictionary];
  dict[@"event_name"] = eventName;

  if (properties) {
    for (id key in properties) {
      dict[key] = properties[key];
    }
  }

  return dict;
}
 
@end

Analyser les résultats

Utilisez la surface de reporting correspondant à la catégorie d’analytique :

Pour la création de rapports personnalisés, consultez Générateur de rapports.

Références associées

New Stuff!