Zum Inhalt springen



Implementierungsleitfaden für Push-Benachrichtigungen

Dieser optionale Leitfaden für die erweiterte Implementierung beschreibt, wie Sie die App-Erweiterungen für Push-Benachrichtigungsinhalte nutzen können, um den größtmöglichen Erfolg mit Ihren Push-Nachrichten zu erzielen. Er enthält drei von unserem Team erstellte angepasste Anwendungsfälle, begleitende Code-Snippets und eine Anleitung zur Protokollierung von Analytics. Besuchen Sie das Braze Demo Repository! Beachten Sie, dass sich dieser Implementierungsleitfaden auf eine Swift-Implementierung konzentriert. Für Interessierte werden jedoch Objective-C-Snippets bereitgestellt.

App-Erweiterungen für Benachrichtigungsinhalte

Zwei Push-Benachrichtigungen nebeneinander. Die linke Nachricht zeigt, wie ein Push mit der Standard-UI aussieht. Die rechte Nachricht zeigt einen Kaffee-Stempelkarten-Push, der durch die Implementierung einer angepassten Push-UI erstellt wurde.

Push-Benachrichtigungen, die auf verschiedenen Plattformen scheinbar einheitlich wirken, bieten weitreichende Anpassungsmöglichkeiten über das hinaus, was normalerweise in der Standard-UI implementiert wird. Wenn eine Push-Benachrichtigung erweitert wird, ermöglichen Erweiterungen für Benachrichtigungsinhalte eine angepasste Ansicht der erweiterten Push-Benachrichtigung.

Push-Benachrichtigungen können auf drei verschiedene Arten erweitert werden:
- Langes Drücken auf das Push-Banner
- Nach unten wischen auf dem Push-Banner
- Auf das Banner wischen und „Anzeigen“ auswählen

Diese angepassten Ansichten bieten intelligente Möglichkeiten, Kund:innen einzubinden, indem sie viele verschiedene Inhaltstypen anzeigen – darunter interaktive Benachrichtigungen, mit Nutzerdaten befüllte Benachrichtigungen und sogar Push-Nachrichten, die Informationen wie Telefonnummern und E-Mail-Adressen erfassen können. Auch wenn die Implementierung von Push-Benachrichtigungen auf diese Weise für manche ungewohnt sein mag – eines unserer bekannten Features bei Braze, Push Stories, ist ein hervorragendes Beispiel dafür, wie eine angepasste Ansicht für eine App-Erweiterung für Benachrichtigungsinhalte aussehen kann!

Voraussetzungen

Xcodes Bildschirm „Choose a template for your new target“ mit ausgewählter Option „Notification Content Extension“ unter „Application Extension“.

  • Push-Benachrichtigungen erfolgreich in Ihrer App integriert
  • iOS 10 oder höher
  • Die folgenden Dateien, die von Xcode basierend auf Ihrer Programmiersprache generiert werden:

Swift
- NotificationViewController.swift
- MainInterface.storyboard

Objective-C
- NotificationViewController.h
- NotificationViewController.m
- MainInterface.storyboard

Konfiguration angepasster Kategorien

Um eine angepasste Ansicht im Dashboard einzurichten, müssen Sie die Benachrichtigungs-Buttons aktivieren und Ihre angepasste Kategorie eingeben. Die vorab registrierte angepasste iOS-Kategorie, die Sie angeben, wird dann mit der UNNotificationExtensionCategory in der .plist Ihres Notification Content Extension Targets abgeglichen. Der hier angegebene Wert muss mit dem im Braze-Dashboard festgelegten Wert übereinstimmen.

Die Optionen für Benachrichtigungs-Buttons in den Einstellungen des Push-Nachrichten-Editors. Eine plist, die NSExtension zeigt, wobei UNNotificationExtensionCategory auf „your_custom_category“, UNNotificationExtensionDefaultContentHidden auf 1 und UNNotificationExtensionInitialContentSizeRatio auf 1 gesetzt ist.

Anwendungsfälle und Implementierungs-Walkthrough

Es gibt drei Typen von Push-Benachrichtigungs-Content-App-Erweiterungen. Jeder Typ enthält einen Konzept-Walkthrough, mögliche Anwendungsfälle sowie einen Einblick, wie Push-Benachrichtigungsvariablen im Braze-Dashboard aussehen und verwendet werden können:

Interaktive Push-Benachrichtigung

Push-Benachrichtigungen können innerhalb einer Content Extension auf Nutzeraktionen reagieren. Für Nutzer:innen mit iOS 12 oder höher bedeutet dies, dass Sie Ihre Push-Nachrichten in vollständig interaktive Push-Benachrichtigungen verwandeln können! Diese Interaktivität bietet viele Möglichkeiten, Ihre Nutzer:innen mit Ihren Benachrichtigungen zu begeistern. Das folgende Beispiel zeigt eine Push-Benachrichtigung, bei der Nutzer:innen ein Zuordnungsspiel in der erweiterten Benachrichtigung spielen können.

Ein Diagramm, das zeigt, wie die Phasen einer interaktiven Push-Benachrichtigung aussehen könnten. Die Bilder zeigen, wie Nutzer:innen auf eine Push-Benachrichtigung tippen, die ein interaktives Zuordnungsspiel anzeigt.

Dashboard-Konfiguration

Um eine angepasste Ansicht im Dashboard einzurichten, geben Sie in den Einstellungen für Benachrichtigungs-Buttons die spezifische Kategorie ein, die Sie anzeigen möchten. Anschließend müssen Sie in der .plist Ihrer Notification Content Extension ebenfalls die angepasste Kategorie für das Attribut UNNotificationExtensionCategory festlegen. Der hier angegebene Wert muss mit dem im Braze-Dashboard gesetzten Wert übereinstimmen. Um schließlich Nutzerinteraktionen in einer Push-Benachrichtigung zu aktivieren, setzen Sie den Schlüssel UNNotificationExtensionInteractionEnabled auf „true“.

Der Abschnitt „Notification Buttons“ im Braze-Dashboard, in dem das Feld „iOS Notification Category“ auf „match_game“ gesetzt ist.

Die Optionen für Benachrichtigungs-Buttons in den Einstellungen des Push-Nachrichten-Editors.

Weitere Anwendungsfälle

Push Content Extensions sind eine spannende Möglichkeit, Interaktivität in Ihre Aktionen und Anwendungen einzuführen. Einige Beispiele sind ein Spiel für Nutzer:innen, ein Glücksrad für Rabatte oder ein „Gefällt mir“-Button, um ein Angebot oder einen Song zu speichern.

Bereit, Analytics zu protokollieren?

Besuchen Sie den folgenden Abschnitt, um besser zu verstehen, wie der Datenfluss aussehen sollte.

Personalisierte Push-Benachrichtigungen

Zwei iPhones nebeneinander dargestellt. Das erste iPhone zeigt die nicht erweiterte Ansicht der Push-Nachricht. Das zweite iPhone zeigt die erweiterte Version der Push-Nachricht mit einem Fortschrittsanzeiger, wie weit ein Kurs abgeschlossen ist, der nächsten Sitzung und deren Fälligkeitsdatum.

Push-Benachrichtigungen können nutzerspezifische Informationen in einer Content Extension anzeigen. Das begleitende Beispiel zeigt eine Push-Benachrichtigung, nachdem Nutzer:innen eine bestimmte Aufgabe (einen Braze-Lernkurs) abgeschlossen haben, und fordert nun dazu auf, die Benachrichtigung zu erweitern, um den Fortschritt zu überprüfen. Die hier bereitgestellten Informationen sind nutzerspezifisch und können ausgelöst werden, wenn eine Sitzung abgeschlossen oder eine bestimmte Nutzeraktion durchgeführt wird, indem ein API-Trigger verwendet wird.

Dashboard-Konfiguration

Um eine personalisierte Push-Benachrichtigung im Dashboard einzurichten, müssen Sie die spezifische Kategorie registrieren, die angezeigt werden soll, und dann innerhalb der Schlüssel-Wert-Paare mithilfe von Standard-Liquid die entsprechenden Nutzerattribute festlegen, die die Nachricht anzeigen soll. Diese Ansichten können basierend auf bestimmten Nutzerattributen eines bestimmten Nutzerprofils personalisiert werden.

Vier Schlüssel-Wert-Paare, wobei „next_session_name“ und „next_session_complete_date“ als API-Trigger-Eigenschaft mit Liquid gesetzt sind und „completed_session count“ und „total_session_count“ als angepasstes Nutzerattribut mit Liquid gesetzt sind.

Handhabung von Schlüssel-Wert-Paaren

Die folgende Methode didReceive wird aufgerufen, wenn die Content Extension eine Benachrichtigung empfangen hat. Sie befindet sich innerhalb des NotificationViewController. Die im Dashboard bereitgestellten Schlüssel-Wert-Paare werden im Code durch die Verwendung eines userInfo-Dictionarys dargestellt.

Parsen von Schlüssel-Wert-Paaren aus Push-Benachrichtigungen

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"];
  }
}

Weitere Anwendungsfälle

Die Möglichkeiten für fortschrittsbasierte und nutzerfokussierte Push Content Extensions sind endlos. Einige Beispiele sind die Möglichkeit, den Fortschritt über verschiedene Plattformen hinweg zu teilen, freigeschaltete Erfolge anzuzeigen, Stempelkarten oder sogar Onboarding-Checklisten.

Bereit, Analytics zu protokollieren?

Besuchen Sie den folgenden Abschnitt, um besser zu verstehen, wie der Datenfluss aussehen sollte.

Push-Benachrichtigung zur Informationserfassung

Push-Benachrichtigungen können Nutzerinformationen innerhalb einer Content Extension erfassen, sodass Sie die Grenzen dessen erweitern können, was mit einer Push-Benachrichtigung möglich ist. Wenn Sie den folgenden dargestellten Ablauf betrachten, kann die Ansicht auf Statusänderungen reagieren. Diese Statusänderungskomponenten sind in jedem Bild dargestellt.

  1. Nutzer:innen erhalten eine Push-Benachrichtigung.
  2. Die Push-Benachrichtigung wird geöffnet und fordert die Nutzer:innen zur Eingabe von Informationen auf.
  3. Informationen werden eingegeben und wenn sie gültig sind, wird der Registrierungs-Button angezeigt.
  4. Die Bestätigungsansicht wird angezeigt und die Push-Benachrichtigung wird geschlossen.

Beachten Sie, dass die hier angeforderten Informationen ein breites Spektrum abdecken können, wie z. B. die Erfassung von SMS-Nummern – sie müssen nicht E-Mail-spezifisch sein.

Dashboard-Konfiguration

Um eine Push-Benachrichtigung mit Informationserfassung im Dashboard einzurichten, müssen Sie Ihre angepasste Kategorie registrieren und festlegen sowie die benötigten Schlüssel-Wert-Paare bereitstellen. Wie im Beispiel zu sehen, können Sie auch ein Bild in Ihre Push-Benachrichtigung einfügen. Dazu müssen Sie Rich-Benachrichtigungen integrieren, den Benachrichtigungsstil in Ihrer Campaign auf Rich-Benachrichtigung setzen und ein Rich-Push-Bild einfügen.

Eine Push-Nachricht mit drei Schlüssel-Wert-Paaren. 1. „Braze_id“ als Liquid-Aufruf zum Abrufen der Braze-ID gesetzt. 2. „cert_title“ auf „Braze Marketer Certification“ gesetzt. 3. „Cert_description“ auf „Certified Braze marketers drive...“ gesetzt.

Handhabung von Button-Aktionen

Jeder Aktions-Button wird eindeutig identifiziert. Der Code prüft, ob Ihr Antwort-Bezeichner dem actionIdentifier entspricht, und weiß dann, dass die Nutzer:innen den Aktions-Button geklickt haben.

Handhabung von Antworten auf Push-Benachrichtigungs-Aktions-Buttons

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);
  }
}
Push-Benachrichtigungen schließen

Push-Benachrichtigungen können durch Drücken eines Aktions-Buttons automatisch geschlossen werden. Es gibt drei vorgefertigte Optionen zum Schließen von Push-Benachrichtigungen, die empfohlen werden:

  1. completion(.dismiss) – Schließt die Benachrichtigung
  2. completion(.doNotDismiss) – Benachrichtigung bleibt geöffnet
  3. completion(.dismissAndForward) – Push wird geschlossen und die Nutzer:innen werden in die Anwendung weitergeleitet.

Weitere Anwendungsfälle

Nutzereingaben über Push-Benachrichtigungen anzufordern ist eine spannende Möglichkeit, die viele Unternehmen nicht nutzen. In diesen Push-Nachrichten können Sie nicht nur grundlegende Informationen wie Name, E-Mail oder Nummer abfragen, sondern auch Nutzer:innen auffordern, ein unvollständiges Nutzerprofil zu vervollständigen oder sogar Feedback einzureichen.

Bereit, Analytics zu protokollieren?

Besuchen Sie den folgenden Abschnitt, um besser zu verstehen, wie der Datenfluss aussehen sollte.

Analytics protokollieren

Die Protokollierung von Analytics kann nur in Echtzeit erfolgen, indem der Server der Kundin bzw. des Kunden unseren /users/track-Endpunkt aufruft. Um Analytics zu protokollieren, senden Sie den braze_id-Wert im Schlüssel-Wert-Paar-Feld mit (wie im folgenden Screenshot gezeigt), um zu identifizieren, welches Nutzerprofil aktualisiert werden soll.

Eine Push-Nachricht mit drei Schlüssel-Wert-Paaren. 1. „Braze_id“ als Liquid-Aufruf zum Abrufen der Braze-ID gesetzt. 2. „cert_title“ auf „Braze Marketer Certification“ gesetzt. 3. „Cert_description“ auf „Certified Braze marketers drive...“ gesetzt.

Manuelle Protokollierung

Die manuelle Protokollierung erfordert zunächst die Konfiguration von App-Gruppen in Xcode und anschließend das Erstellen, Speichern und Abrufen von Analytics. Dies erfordert etwas angepasste Entwicklungsarbeit auf Ihrer Seite. Die folgenden Code-Snippets helfen Ihnen dabei.

Außerdem ist zu beachten, dass Analytics erst an Braze gesendet werden, wenn die mobile App anschließend gestartet wird. Das bedeutet, dass je nach Ihren Einstellungen zum Schließen oft ein unbestimmter Zeitraum zwischen dem Schließen einer Push-Benachrichtigung und dem Start der mobilen App besteht, in dem die Analytics abgerufen werden. Obwohl dieser Zeitpuffer nicht alle Anwendungsfälle beeinflusst, sollten Nutzer:innen die Auswirkungen berücksichtigen und gegebenenfalls ihre User Journey so anpassen, dass sie das Öffnen der Anwendung einschließt, um dieses Problem zu beheben.

Eine Grafik, die beschreibt, wie Analytics in Braze verarbeitet werden. 1. Analytics-Daten werden erstellt. 2. Analytics-Daten werden gespeichert. 3. Push-Benachrichtigung wird geschlossen. 4. Unbestimmter Zeitraum zwischen dem Schließen der Push-Benachrichtigung und dem Start der mobilen App. 5. Mobile App wird gestartet. 6. Analytics-Daten werden empfangen. 7. Analytics-Daten werden an Braze gesendet.

Schritt 1: App-Gruppen in Xcode konfigurieren

Fügen Sie die Fähigkeit App Groups hinzu. Falls Sie noch keine App-Gruppe in Ihrer App haben, gehen Sie zu den Fähigkeiten des Haupt-App-Targets, aktivieren Sie App Groups und klicken Sie auf „+“. Verwenden Sie die Bundle-ID Ihrer App, um die App-Gruppe zu erstellen. Wenn die Bundle-ID Ihrer App beispielsweise com.company.appname lautet, können Sie Ihre App-Gruppe group.com.company.appname.xyz nennen. Stellen Sie sicher, dass App Groups sowohl für Ihr Haupt-App-Target als auch für das Content-Extension-Target aktiviert sind.

Der Dialog „Neuen Container hinzufügen“ in Xcode zur Konfiguration einer App-Gruppe mit einem Textfeld, das mit „group.“ vorausgefüllt ist.

Schritt 2: Code-Snippets integrieren

Die folgenden Code-Snippets sind eine hilfreiche Referenz zum Speichern und Senden von angepassten Events, angepassten Attributen und Nutzerattributen. Dieser Leitfaden verwendet UserDefaults als Begriffe, aber die Code-Darstellung erfolgt in Form einer Hilfsdatei RemoteStorage. Es gibt auch zusätzliche Hilfsdateien UserAttributes und EventName Dictionary, die beim Senden und Speichern von Nutzerattributen verwendet werden. Alle Hilfsdateien finden Sie am Ende dieses Leitfadens.

Angepasste Events speichern

Um angepasste Events zu speichern, müssen Sie die Analytics als neues Design erstellen. Dies geschieht, indem Sie ein Dictionary erstellen, es mit Metadaten füllen und die Daten über eine Hilfsdatei speichern.

  1. Initialisieren Sie ein Dictionary mit Event-Metadaten
  2. Initialisieren Sie userDefaults, um die Event-Daten abzurufen und zu speichern
  3. Wenn bereits ein Array vorhanden ist, fügen Sie neue Daten an das vorhandene Array an und speichern Sie es
  4. Wenn kein Array vorhanden ist, speichern Sie das neue Array in 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];
  }
}
Angepasste Events an Braze senden

Nachdem das SDK initialisiert wurde, ist der beste Zeitpunkt, um alle gespeicherten Analytics aus einer Notification-Content-App-Extension zu protokollieren. Dies kann durch Durchlaufen aller ausstehenden Events, Prüfen des Schlüssels „Event Name“, Setzen der entsprechenden Werte in Braze und anschließendes Leeren des Speichers für den nächsten Aufruf dieser Funktion erfolgen.

  1. Durchlaufen Sie das Array ausstehender Events
  2. Durchlaufen Sie jedes Schlüssel-Wert-Paar im pendingEvents-Dictionary
  3. Prüfen Sie explizit den Schlüssel auf „Event Name“, um den Wert entsprechend zu setzen
  4. Jedes andere Schlüssel-Wert-Paar wird dem properties-Dictionary hinzugefügt
  5. Protokollieren Sie das einzelne angepasste Event
  6. Entfernen Sie alle ausstehenden Events aus dem Speicher
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];
}
Angepasste Attribute speichern

Um angepasste Attribute zu speichern, müssen Sie die Analytics als neues Design erstellen. Dies geschieht, indem Sie ein Dictionary erstellen, es mit Metadaten füllen und die Daten mithilfe einer Hilfsdatei speichern.

  1. Initialisieren Sie ein Dictionary mit Attribut-Metadaten
  2. Initialisieren Sie userDefaults, um die Attributdaten abzurufen und zu speichern
  3. Wenn bereits ein Array vorhanden ist, fügen Sie neue Daten an das vorhandene Array an und speichern Sie es
  4. Wenn kein Array vorhanden ist, speichern Sie das neue Array in 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];
  }
}
Angepasste Attribute an Braze senden

Nachdem das SDK initialisiert wurde, ist der beste Zeitpunkt, um alle gespeicherten Analytics aus einer Notification-Content-App-Extension zu protokollieren. Dies kann durch Durchlaufen der ausstehenden Attribute, Setzen des entsprechenden angepassten Attributs in Braze und anschließendes Leeren des Speichers für den nächsten Aufruf dieser Funktion erfolgen.

  1. Durchlaufen Sie das Array ausstehender Attribute
  2. Durchlaufen Sie jedes Schlüssel-Wert-Paar im pendingAttributes-Dictionary
  3. Protokollieren Sie das einzelne angepasste Attribut mit dem entsprechenden Schlüssel und Wert
  4. Entfernen Sie alle ausstehenden Attribute aus dem Speicher
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]];
  }
}
Nutzerattribute speichern

Beim Speichern von Nutzerattributen wird empfohlen, ein angepasstes Objekt zu erstellen, um zu entschlüsseln, welcher Attributtyp aktualisiert wird (email, first_name, phone_number usw.). Das Objekt sollte kompatibel mit dem Speichern/Abrufen über UserDefaults sein. Ein Beispiel dafür finden Sie in der Hilfsdatei UserAttribute.

  1. Initialisieren Sie ein codiertes UserAttribute-Objekt mit dem entsprechenden Typ
  2. Initialisieren Sie userDefaults, um die Event-Daten abzurufen und zu speichern
  3. Wenn bereits ein Array vorhanden ist, fügen Sie neue Daten an das vorhandene Array an und speichern Sie es
  4. Wenn kein Array vorhanden ist, speichern Sie das neue Array in 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];
  }
}
Nutzerattribute an Braze senden

Nachdem das SDK initialisiert wurde, ist der beste Zeitpunkt, um alle gespeicherten Analytics aus einer Notification-Content-App-Extension zu protokollieren. Dies kann durch Durchlaufen der ausstehenden Attribute, Setzen des entsprechenden angepassten Attributs in Braze und anschließendes Leeren des Speichers für den nächsten Aufruf dieser Funktion erfolgen.

  1. Durchlaufen Sie das Array der pendingAttributes-Daten
  2. Initialisieren Sie ein codiertes UserAttribute-Objekt aus den Attributdaten
  3. Setzen Sie das spezifische Nutzerfeld basierend auf dem Nutzerattribut-Typ (E-Mail)
  4. Entfernen Sie alle ausstehenden Nutzerattribute aus dem Speicher
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];
}
Hilfsdateien
RemoteStorage-Hilfsdatei
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."];
  }
}
UserAttribute-Hilfsdatei
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
EventName-Dictionary-Hilfsdatei
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


New Stuff!