
AppboyKit (también conocido como el SDK de Objective-C) ya no recibe soporte y ha sido sustituido por el SDK de SWIFT. Ya no recibirá nuevas características, correcciones de errores, actualizaciones de seguridad ni soporte técnico—sin embargo, la mensajería y los análisis seguirán funcionando con normalidad. Para obtener más información, consulta Presentación del nuevo SDK de SWIFT de Braze.

¿Buscas la guía básica de integración del desarrollador de notificaciones push? Encuéntrala en Integración de notificaciones push para iOS.
Guía de implementación de notificaciones push
Esta guía de implementación opcional y avanzada cubre formas de aprovechar las extensiones de la aplicación de contenido de notificaciones push para sacar el máximo partido a tus mensajes push. Se incluyen tres casos de uso personalizados creados por nuestro equipo, fragmentos de código que los acompañan y orientaciones sobre el registro de análisis. ¡Visita el repositorio de demostraciones de Braze! Ten en cuenta que esta guía de implementación se centra en una implementación Swift, pero se proporcionan fragmentos de código Objective-C para los interesados.
Extensiones de contenido de notificaciones en la aplicación

Las notificaciones push, aunque parecen estándar en las diferentes plataformas, ofrecen inmensas opciones de personalización más allá de lo que normalmente se implementa en la interfaz predeterminada. Cuando se expande una notificación push, las extensiones de contenido de notificaciones permiten una vista personalizada de la notificación push expandida.
Las notificaciones push se pueden expandir de tres formas diferentes:
- Una pulsación larga sobre el banner push
- Deslizando hacia abajo sobre el banner push
- Deslizando el banner y seleccionando “Ver”
Estas vistas personalizadas ofrecen formas inteligentes de interactuar con los clientes permitiéndote mostrar muchos tipos distintos de contenido, incluyendo notificaciones interactivas, notificaciones con datos de usuario e incluso mensajes push que pueden capturar información como números de teléfono y correos electrónicos. Aunque implementar push de esta forma puede ser poco familiar para algunos, una de nuestras características más conocidas en Braze, Push Stories, es un excelente ejemplo de cómo puede lucir una vista personalizada para una extensión de contenido de notificaciones en la aplicación.
Requisitos

- Notificaciones push integradas correctamente en tu aplicación
- iOS 10 o superior
- Los siguientes archivos generados por Xcode según tu lenguaje de programación:
Swift
- NotificationViewController.swift
- MainInterface.storyboard
Objective-C
- NotificationViewController.h
- NotificationViewController.m
- MainInterface.storyboard
Configuración de categorías personalizadas
Para configurar una vista personalizada en el panel, debes activar los botones de notificación e introducir tu categoría personalizada. La categoría personalizada de iOS previamente registrada que proporciones se verifica contra el UNNotificationExtensionCategory en el .plist del destino de tu extensión de contenido de notificaciones. El valor proporcionado aquí debe coincidir con lo configurado en el panel de Braze.


Dado que los pushes con extensiones de contenido no siempre son evidentes, se recomienda incluir una llamada a la acción para animar a tus usuarios a expandir sus notificaciones push.
Caso de uso y recorrido por la implementación
Se proporcionan tres tipos de extensión de aplicación de contenido para notificaciones push. Cada tipo incluye un recorrido conceptual, posibles casos de uso y una visión de cómo las variables de notificaciones push pueden verse y utilizarse en el panel de Braze:
- Notificación push interactiva
- Notificaciones push personalizadas
- Notificaciones push de captura de información
Notificación push interactiva
Las notificaciones push pueden responder a las acciones del usuario dentro de una extensión de contenido. Para los usuarios que ejecutan iOS 12 o posterior, esto significa que puedes convertir tus mensajes push en notificaciones push totalmente interactivas. Esta interactividad ofrece muchas posibilidades para que tus usuarios interactúen con tus notificaciones. El siguiente ejemplo muestra una notificación push en la que los usuarios pueden jugar un juego de emparejar dentro de la notificación expandida.

Configuración del panel
Para configurar una vista personalizada en el panel, dentro de la configuración de los botones de notificación, introduce la categoría específica que deseas mostrar. A continuación, en el .plist de tu extensión de contenido de notificación, también debes configurar la categoría personalizada en el atributo UNNotificationExtensionCategory. El valor proporcionado aquí debe coincidir con lo configurado en el panel de Braze. Por último, para habilitar las interacciones del usuario en una notificación push, configura la clave UNNotificationExtensionInteractionEnabled como true.


Otros casos de uso
Las extensiones de contenido push son una opción interesante para introducir interactividad en tus promociones y aplicaciones. Algunos ejemplos incluyen un juego para que los usuarios jueguen, una rueda de girar y ganar para descuentos, o un botón de “me gusta” para guardar un anuncio o canción.
¿Listo para registrar análisis?
Visita la siguiente sección para comprender mejor cómo debería verse el flujo de datos.
Notificaciones push personalizadas

Las notificaciones push pueden mostrar información específica del usuario dentro de una extensión de contenido. El ejemplo adjunto muestra una notificación push después de que un usuario ha completado una tarea específica (curso de Braze Learning) y ahora se le anima a expandir esta notificación para comprobar su progreso. La información proporcionada aquí es específica del usuario y puede enviarse cuando se completa una sesión o se realiza una acción específica del usuario aprovechando un disparador de API.
Configuración del panel
Para configurar una notificación push personalizada en el panel, debes registrar la categoría específica que deseas que se muestre y, a continuación, dentro de los pares clave-valor utilizando Liquid estándar, establecer los atributos de usuario apropiados que deseas que muestre el mensaje. Estas vistas pueden personalizarse en función de atributos específicos de un perfil de usuario concreto.

Manejo de pares clave-valor
El siguiente método, didReceive, se llama cuando la extensión de contenido ha recibido una notificación y se encuentra dentro del NotificationViewController. Los pares clave-valor proporcionados en el panel se representan en el código mediante el uso de un diccionario userInfo.
Análisis de pares clave-valor de las notificaciones 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"];
}
}
Otros casos de uso
Las ideas para extensiones de contenido push basadas en progreso y centradas en el usuario son infinitas, algunos ejemplos incluyen añadir la opción de compartir tu progreso en diferentes plataformas, expresar logros desbloqueados, tarjetas de puntos o incluso listas de verificación de incorporación.
¿Listo para registrar análisis?
Visita la siguiente sección para comprender mejor cómo debería verse el flujo de datos.
Notificación push de captura de información
Las notificaciones push pueden capturar información del usuario dentro de una extensión de contenido, permitiéndote llevar al límite lo que es posible con una notificación push. Al examinar el siguiente flujo mostrado, la vista es capaz de responder a cambios de estado. Esos componentes de cambio de estado están representados en cada imagen.
- El usuario recibe una notificación push.
- Se abre la notificación push y solicita información al usuario.
- Se proporciona la información y, si es válida, se muestra el botón de registro.
- Se muestra la vista de confirmación y la notificación push se descarta.
Ten en cuenta que la información solicitada aquí puede ser una amplia variedad de cosas, como la captura del número de SMS, no tiene que ser específica del correo electrónico.
Configuración del panel
Para configurar una notificación push con capacidad de captura de información en el panel, debes registrar y configurar tu categoría personalizada, y proporcionar los pares clave-valor necesarios. Como se ve en el ejemplo, también puedes incluir una imagen en tu notificación push. Para hacer esto, debes integrar las notificaciones enriquecidas, configurar el estilo de notificación en tu campaña como notificación enriquecida e incluir una imagen de push enriquecida.

Manejo de acciones de botones
Cada botón de acción se identifica de forma única. El código comprueba si tu identificador de respuesta es igual al actionIdentifier y, de ser así, sabe que el usuario hizo clic en el botón de acción.
Manejo de respuestas de botones de acción de notificaciones 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);
}
}
Descartar notificaciones push
Las notificaciones push se pueden descartar automáticamente al presionar un botón de acción. Existen tres opciones predefinidas de descarte de notificaciones push que se recomiendan:
completion(.dismiss)- Descarta la notificacióncompletion(.doNotDismiss)- La notificación permanece abiertacompletion(.dismissAndForward)- La notificación push se descarta y el usuario es redirigido a la aplicación.
Otros casos de uso
Solicitar información del usuario a través de notificaciones push es una oportunidad interesante que muchas empresas no aprovechan. En estos mensajes push, puedes no solo solicitar información básica como nombre, correo electrónico o número, sino también animar a los usuarios a completar un perfil de usuario si está incompleto, o incluso a enviar comentarios.
¿Listo para registrar análisis?
Visita la siguiente sección para comprender mejor cómo debería verse el flujo de datos.
Registro de análisis
Registro con la API de Braze (recomendado)
El registro de análisis solo se puede realizar en tiempo real con la ayuda del servidor del cliente que envía solicitudes a nuestro endpoint /users/track. Para registrar análisis, envía el valor braze_id en el campo de pares clave-valor (como se muestra en la siguiente captura de pantalla) para identificar qué perfil de usuario actualizar.

Registro manual
El registro manual requerirá que primero configures los grupos de aplicaciones dentro de Xcode, y luego crees, guardes y recuperes los análisis. Esto requerirá algo de trabajo personalizado del desarrollador de tu parte. Los siguientes fragmentos de código que se muestran te ayudarán a abordar esto.
También es importante tener en cuenta que los análisis no se envían a Braze hasta que la aplicación móvil se inicia posteriormente. Esto significa que, dependiendo de tu configuración de descarte, a menudo existe un período de tiempo indeterminado entre el momento en que se descarta una notificación push y el momento en que se inicia la aplicación móvil y se recuperan los análisis. Aunque este período de espera puede no afectar a todos los casos de uso, los usuarios deben considerar el impacto y, si es necesario, ajustar su recorrido de usuario para incluir la apertura de la aplicación y así abordar esta cuestión.

Paso 1: Configurar grupos de aplicaciones dentro de Xcode
Añade la capacidad App Groups. Si no tienes ningún grupo de aplicaciones en tu aplicación, ve a la capacidad del target principal de la aplicación, activa App Groups y haz clic en “+”. Usa el bundle ID de tu aplicación para crear el grupo de aplicaciones. Por ejemplo, si el bundle ID de tu aplicación es com.company.appname, puedes nombrar tu grupo de aplicaciones group.com.company.appname.xyz. Asegúrate de que App Groups esté activado tanto para el target principal de tu aplicación como para el target de la extensión de contenido.

Paso 2: Integrar fragmentos de código
Los siguientes fragmentos de código son una referencia útil sobre cómo guardar y enviar eventos personalizados, atributos personalizados y atributos de usuario. Esta guía hablará en términos de UserDefaults, pero la representación del código estará en forma de un archivo auxiliar RemoteStorage. También existen archivos auxiliares adicionales UserAttributes y EventName Dictionary que se utilizan al enviar y guardar atributos de usuario. Todos los archivos auxiliares se pueden encontrar al final de esta guía.
Guardar eventos personalizados
Para guardar eventos personalizados debes crear los análisis como un nuevo diseño. Esto se hace creando un diccionario, llenándolo con metadatos y guardando los datos a través de un archivo auxiliar.
- Inicializa un diccionario con metadatos del evento
- Inicializa
userDefaultspara recuperar y almacenar los datos del evento - Si existe un array existente, añade los nuevos datos al array existente y guarda
- Si no existe un array, guarda el nuevo array en
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];
}
}
Enviar eventos personalizados a Braze
Después de que el SDK se inicializa es el mejor momento para registrar cualquier análisis guardado desde una extensión de contenido de notificación de la aplicación. Esto se puede hacer recorriendo todos los eventos pendientes, verificando la clave “Event Name”, estableciendo los valores apropiados en Braze, y luego limpiando el almacenamiento para la próxima vez que se necesite esta función.
- Recorre el array de eventos pendientes
- Recorre cada par clave-valor en el diccionario
pendingEvents - Verifica explícitamente la clave “Event Name” para establecer el valor de acuerdo
- Cada otro par clave-valor se añadirá al diccionario
properties - Registra el evento personalizado individual
- Elimina todos los eventos pendientes del almacenamiento
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];
}
Guardar atributos personalizados
Para guardar atributos personalizados debes crear los análisis como un nuevo diseño. Esto se hace creando un diccionario, llenándolo con metadatos y guardando los datos a través del uso de un archivo auxiliar.
- Inicializa un diccionario con metadatos de atributos
- Inicializa
userDefaultspara recuperar y almacenar los datos de atributos - Si existe un array existente, añade los nuevos datos al array existente y guarda
- Si no existe un array, guarda el nuevo array en
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];
}
}
Enviar atributos personalizados a Braze
Después de que el SDK se inicializa es el mejor momento para registrar cualquier análisis guardado desde una extensión de contenido de notificación de la aplicación. Esto se puede hacer recorriendo los atributos pendientes, estableciendo el atributo personalizado apropiado en Braze, y luego limpiando el almacenamiento para la próxima vez que se necesite esta función.
- Recorre el array de atributos pendientes
- Recorre cada par clave-valor en el diccionario
pendingAttributes - Registra el atributo personalizado individual con la clave y el valor correspondientes
- Elimina todos los atributos pendientes del almacenamiento
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]];
}
}
Guardar atributos de usuario
Al guardar atributos de usuario, se recomienda crear un objeto personalizado para descifrar qué tipo de atributo se está actualizando (email, first_name, phone_number, etc.). El objeto debe ser compatible con ser almacenado/recuperado desde UserDefaults. Consulta el archivo auxiliar UserAttribute para ver un ejemplo de cómo lograr esto.
- Inicializa un objeto
UserAttributecodificado con el tipo correspondiente - Inicializa
userDefaultspara recuperar y almacenar los datos del evento - Si existe un array existente, añade los nuevos datos al array existente y guarda
- Si no existe un array, guarda el nuevo array en
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];
}
}
Enviar atributos de usuario a Braze
Después de que el SDK se inicializa es el mejor momento para registrar cualquier análisis guardado desde una extensión de contenido de notificación de la aplicación. Esto se puede hacer recorriendo los atributos pendientes, estableciendo el atributo personalizado apropiado en Braze, y luego limpiando el almacenamiento para la próxima vez que se necesite esta función.
- Recorre el array de datos
pendingAttributes - Inicializa un objeto
UserAttributecodificado a partir de los datos de atributos - Establece el campo de usuario específico basado en el tipo de atributo de usuario (correo electrónico)
- Elimina todos los atributos de usuario pendientes del almacenamiento
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];
}
Archivos auxiliares
Archivo auxiliar 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."];
}
}
Archivo auxiliar 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
Archivo auxiliar 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