
AppboyKit (também conhecido como o SDK Objective-C) não é mais suportado e foi substituído pelo Swift SDK. Não receberá mais novos recursos, correções de bugs, atualizações de segurança ou suporte técnico—no entanto, o envio de mensagens e a análise de dados continuarão a funcionar normalmente. Para saber mais, veja Apresentando o novo SDK Swift da Braze.

Está procurando o guia básico de integração de desenvolvedores de notificações por push? Consulte Integração de notificações por push para iOS.
Guia de implementação de notificações por push
Este guia de implementação opcional e avançado aborda maneiras de aproveitar as extensões de app de conteúdo de notificação por push para obter o máximo de suas mensagens push. Incluídos estão três casos de uso personalizados criados por nossa equipe, trechos de código de acompanhamento e orientações sobre o registro de análise de dados. Consulte o Repositório de Demonstrações da Braze! Note que este guia de implementação está centrado em uma implementação Swift, mas são fornecidos trechos em Objective-C para os interessados.
Extensões de conteúdo de notificação para apps

As notificações por push, embora aparentemente padronizadas entre diferentes plataformas, oferecem imensas opções de personalização além do que é normalmente implementado na interface padrão. Quando uma notificação por push é expandida, as extensões de conteúdo de notificação permitem uma visualização personalizada da notificação por push expandida.
As notificações por push podem ser expandidas de três maneiras diferentes:
- Pressionando e segurando o banner do push
- Deslizando para baixo no banner do push
- Deslizando o banner e selecionando “Visualizar”
Essas visualizações personalizadas oferecem maneiras inteligentes de engajar clientes, permitindo exibir diversos tipos de conteúdo, incluindo notificações interativas, notificações preenchidas com dados do usuário e até mensagens push que podem capturar informações como números de telefone e e-mail. Embora implementar push dessa forma possa ser novidade para alguns, um dos nossos recursos mais conhecidos na Braze, Push Stories, é um ótimo exemplo de como uma visualização personalizada para uma extensão de conteúdo de notificação pode se parecer!
Requisitos

- Notificações por push integradas com sucesso no seu app
- iOS 10 ou superior
- Os seguintes arquivos gerados pelo Xcode com base na sua linguagem de programação:
Swift
- NotificationViewController.swift
- MainInterface.storyboard
Objective-C
- NotificationViewController.h
- NotificationViewController.m
- MainInterface.storyboard
Configuração de categoria personalizada
Para configurar uma visualização personalizada no dashboard, é necessário ativar os botões de notificação e inserir sua categoria personalizada. A categoria iOS personalizada pré-registrada que você fornece é então verificada em relação à UNNotificationExtensionCategory no .plist do seu Notification Content Extension Target. O valor fornecido aqui deve corresponder ao que está definido no dashboard da Braze.


Como pushes com extensões de conteúdo nem sempre são evidentes, é recomendável incluir uma chamada para ação para incentivar seus usuários a expandirem suas notificações por push.
Caso de uso e tutorial de implementação
Existem três tipos de extensões de conteúdo para notificações por push fornecidos. Cada tipo inclui um tutorial do conceito, possíveis casos de uso e uma visão de como as variáveis de notificação por push podem ser exibidas e usadas no dashboard da Braze:
- Notificação por push interativa
- Notificações por push personalizadas
- Notificações por push de captura de informações
Notificação por push interativa
As notificações por push podem responder a ações do usuário dentro de uma extensão de conteúdo. Para usuários com iOS 12 ou posterior, isso significa que você pode transformar suas mensagens push em notificações por push totalmente interativas! Essa interatividade oferece diversas possibilidades para engajar seus usuários com suas notificações. O exemplo a seguir mostra uma push na qual os usuários podem jogar um jogo de combinação dentro da notificação expandida.

Configuração no dashboard
Para configurar uma visualização personalizada no dashboard, nas configurações do botão de notificação, insira a categoria específica que deseja exibir. Em seguida, no .plist da sua Notification Content Extension, você também deve definir a categoria personalizada no atributo UNNotificationExtensionCategory. O valor fornecido aqui deve corresponder ao que está definido no dashboard da Braze. Por fim, para ativar as interações do usuário em uma notificação por push, defina a chave UNNotificationExtensionInteractionEnabled como true.


Outros casos de uso
As extensões de conteúdo push são uma opção empolgante para introduzir interatividade em suas promoções e aplicativos. Alguns exemplos incluem um jogo para os usuários jogarem, uma roleta para descontos ou um botão “curtir” para salvar um anúncio ou música.
Pronto para registrar a análise de dados?
Acesse a seção a seguir para entender melhor como o fluxo de dados deve funcionar.
Notificações por push personalizadas

As notificações por push podem exibir informações específicas do usuário dentro de uma extensão de conteúdo. O exemplo ao lado mostra uma notificação por push após um usuário ter concluído uma tarefa específica (curso do Braze Learning) e agora é incentivado a expandir essa notificação para verificar seu progresso. As informações fornecidas aqui são específicas do usuário e podem ser disparadas quando uma sessão é concluída ou uma ação específica do usuário é realizada, aproveitando um disparo por API.
Configuração no dashboard
Para configurar uma push personalizada no dashboard, você deve registrar a categoria específica que deseja exibir e, em seguida, nos pares chave-valor usando Liquid padrão, definir os atributos de usuário apropriados que deseja que a mensagem mostre. Essas visualizações podem ser personalizadas com base em atributos específicos de um perfil de usuário específico.

Manipulando pares chave-valor
O método a seguir, didReceive, é chamado quando a extensão de conteúdo recebe uma notificação e pode ser encontrado no NotificationViewController. Os pares chave-valor fornecidos no dashboard são representados no código por meio do uso de um dicionário userInfo.
Analisando pares chave-valor de notificações por 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"];
}
}
Outros casos de uso
As ideias para extensões de conteúdo push baseadas em progresso e focadas no usuário são infinitas. Alguns exemplos incluem adicionar a opção de compartilhar seu progresso em diferentes plataformas, expressar conquistas desbloqueadas, cartões fidelidade ou até listas de verificação de integração.
Pronto para registrar a análise de dados?
Acesse a seção a seguir para entender melhor como o fluxo de dados deve funcionar.
Notificação por push de captura de informações
As notificações por push podem capturar informações do usuário dentro de uma extensão de conteúdo, permitindo expandir os limites do que é possível com uma push. Examinando o fluxo a seguir, a visualização é capaz de responder a mudanças de estado. Esses componentes de mudança de estado estão representados em cada imagem.
- O usuário recebe uma notificação por push.
- A push é aberta e solicita informações ao usuário.
- As informações são fornecidas e, se forem válidas, o botão de registro é exibido.
- A visualização de confirmação é exibida e a push é descartada.
Observe que as informações solicitadas aqui podem abranger diversas finalidades, como a captura de número de SMS, e não precisam ser específicas de e-mail.
Configuração no dashboard
Para configurar uma push com capacidade de captura de informações no dashboard, você deve registrar e definir sua categoria personalizada e fornecer os pares chave-valor necessários. Como visto no exemplo, você também pode incluir uma imagem na sua push. Para isso, você deve integrar notificações Rich, definir o estilo de notificação na sua campanha como Notificação Rich e incluir uma imagem de push Rich.

Manipulando ações de botões
Cada botão de ação é identificado de forma única. O código verifica se o identificador de resposta é igual ao actionIdentifier e, se for, sabe que o usuário clicou no botão de ação.
Manipulando respostas de botões de ação de notificações por 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);
}
}
Descartando pushes
As notificações por push podem ser descartadas automaticamente ao pressionar um botão de ação. Existem três opções pré-criadas de descarte de push recomendadas:
completion(.dismiss)- Descarta a notificaçãocompletion(.doNotDismiss)- A notificação permanece abertacompletion(.dismissAndForward)- A push é descartada e o usuário é redirecionado para o aplicativo.
Outros casos de uso
Solicitar a entrada do usuário por meio de notificações por push é uma oportunidade empolgante da qual muitas empresas não tiram proveito. Nessas mensagens push, você pode não apenas solicitar informações básicas como nome, e-mail ou número, mas também pedir aos usuários que completem um perfil de usuário inacabado ou até que enviem feedback.
Pronto para registrar a análise de dados?
Acesse a seção a seguir para entender melhor como o fluxo de dados deve funcionar.
Registro de análise de dados
Registro com a API da Braze (recomendado)
O registro de análise de dados só pode ser feito em tempo real com a ajuda do servidor do cliente acessando nosso endpoint /users/track. Para registrar análise de dados, envie o valor braze_id no campo de pares chave-valor (conforme mostrado na captura de tela a seguir) para identificar qual perfil de usuário deve ser atualizado.

Registro manual
O registro manual exigirá que você primeiro configure os grupos de apps (App Groups) no Xcode e, em seguida, crie, salve e recupere os dados de análise. Isso requer algum trabalho de desenvolvimento personalizado da sua parte. Os trechos de código a seguir ajudarão a resolver isso.
Também é importante observar que os dados de análise não são enviados para a Braze até que o app móvel seja iniciado novamente. Isso significa que, dependendo das suas configurações de descarte, frequentemente existe um período indeterminado de tempo entre o descarte de uma notificação por push e o lançamento do app móvel e a recuperação dos dados de análise. Embora esse intervalo de tempo possa não afetar todos os casos de uso, os usuários devem considerar o impacto e, se necessário, ajustar a jornada do usuário para incluir a abertura do app e lidar com essa questão.

Etapa 1: Configurar grupos de apps no Xcode
Adicione a funcionalidade App Groups. Se você ainda não tem nenhum grupo de apps no seu app, acesse as funcionalidades do target principal do app, ative App Groups e clique em “+”. Use o bundle ID do seu app para criar o grupo de apps. Por exemplo, se o bundle ID do seu app é com.company.appname, você pode nomear seu grupo de apps como group.com.company.appname.xyz. Certifique-se de que App Groups esteja ativado tanto para o target principal do app quanto para o target da extensão de conteúdo.

Etapa 2: Integrar trechos de código
Os trechos de código a seguir são uma referência útil sobre como salvar e enviar eventos personalizados, atributos personalizados e atributos de usuário. Este guia usará termos do UserDefaults, mas a representação em código será na forma de um arquivo auxiliar RemoteStorage. Também existem arquivos auxiliares adicionais UserAttributes e EventName Dictionary que são usados ao enviar e salvar atributos de usuário. Todos os arquivos auxiliares podem ser encontrados ao final deste guia.
Salvando eventos personalizados
Para salvar eventos personalizados, você deve criar os dados de análise como um novo design. Isso é feito criando um dicionário, preenchendo-o com metadados e salvando os dados por meio de um arquivo auxiliar.
- Inicialize um dicionário com metadados do evento
- Inicialize
userDefaultspara recuperar e armazenar os dados do evento - Se já existir um array, adicione os novos dados ao array existente e salve
- Se não existir um array, salve o novo array em
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];
}
}
Enviando eventos personalizados para a Braze
Após a inicialização do SDK, o melhor momento para registrar quaisquer dados de análise salvos de uma extensão de conteúdo de notificação. Isso pode ser feito percorrendo os eventos pendentes, verificando a chave “Event Name”, definindo os valores apropriados na Braze e, em seguida, limpando o armazenamento para a próxima vez que essa função for necessária.
- Percorra o array de eventos pendentes
- Percorra cada par chave-valor no dicionário
pendingEvents - Verifique explicitamente a chave “Event Name” para definir o valor adequadamente
- Todos os outros pares chave-valor serão adicionados ao dicionário
properties - Registre o evento personalizado individual
- Remova todos os eventos pendentes do armazenamento
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];
}
Salvando atributos personalizados
Para salvar atributos personalizados, você deve criar os dados de análise como um novo design. Isso é feito criando um dicionário, preenchendo-o com metadados e salvando os dados por meio de um arquivo auxiliar.
- Inicialize um dicionário com metadados do atributo
- Inicialize
userDefaultspara recuperar e armazenar os dados do atributo - Se já existir um array, adicione os novos dados ao array existente e salve
- Se não existir um array, salve o novo array em
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];
}
}
Enviando atributos personalizados para a Braze
Após a inicialização do SDK, o melhor momento para registrar quaisquer dados de análise salvos de uma extensão de conteúdo de notificação. Isso pode ser feito percorrendo os atributos pendentes, definindo o atributo personalizado apropriado na Braze e, em seguida, limpando o armazenamento para a próxima vez que essa função for necessária.
- Percorra o array de atributos pendentes
- Percorra cada par chave-valor no dicionário
pendingAttributes - Registre o atributo personalizado individual com a chave e o valor correspondentes
- Remova todos os atributos pendentes do armazenamento
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]];
}
}
Salvando atributos de usuário
Ao salvar atributos de usuário, é recomendável criar um objeto personalizado para identificar qual tipo de atributo está sendo atualizado (email, first_name, phone_number, etc.). O objeto deve ser compatível com o armazenamento/recuperação em UserDefaults. Consulte o arquivo auxiliar UserAttribute para um exemplo de como fazer isso.
- Inicialize um objeto
UserAttributecodificado com o tipo correspondente - Inicialize
userDefaultspara recuperar e armazenar os dados do evento - Se já existir um array, adicione os novos dados ao array existente e salve
- Se não existir um array, salve o novo array em
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];
}
}
Enviando atributos de usuário para a Braze
Após a inicialização do SDK, o melhor momento para registrar quaisquer dados de análise salvos de uma extensão de conteúdo de notificação. Isso pode ser feito percorrendo os atributos pendentes, definindo o atributo personalizado apropriado na Braze e, em seguida, limpando o armazenamento para a próxima vez que essa função for necessária.
- Percorra o array de dados
pendingAttributes - Inicialize um objeto
UserAttributecodificado a partir dos dados do atributo - Defina o campo específico do usuário com base no tipo de atributo de usuário (e-mail)
- Remova todos os atributos de usuário pendentes do armazenamento
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];
}
Arquivos auxiliares
Arquivo 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."];
}
}
Arquivo 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
Arquivo 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