Está procurando o guia básico de integração de desenvolvedores de notificações por push? Acesse aqui.
Guia de implementação avançada
Este guia de implementação avançado e opcional aborda maneiras de aproveitar as extensões de app de conteúdo de notificação para obter o máximo de suas mensagens por push.
Este guia fornece três exemplos de implementações de extensões de app de conteúdo de notificação, cada uma com um passo a passo do conceito, casos de uso em potencial e uma visão de como as variáveis de notificação por push podem parecer e ser usadas no dashboard do Braze:
- Notificações por push interativas
- Notificações por push personalizadas
- Notificações por push de captura de informações
Este artigo também fornece orientação sobre análise de dados de registro para essas implementações personalizadas.
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 de app
As extensões de app de conteúdo de notificação oferecem uma ótima opção para a personalização de notificações por push. As extensões de aplicativo de conteúdo de notificação exibem uma interface personalizada para as notificações do seu app quando uma notificação por push é expandida.
As notificações por push podem ser expandidas de três maneiras diferentes:
- Pressione longamente o banner de push
- Deslizar para baixo no banner push
- Deslize o banner para a esquerda e selecione “Exibir”
Essas visualizações personalizadas oferecem maneiras inteligentes de engajamento com os clientes, exibindo tipos distintos de conteúdo, incluindo notificações interativas, notificações preenchidas com dados de usuários e até mesmo notificações por push que podem capturar informações como números de telefone e e-mail. Um de nossos recursos mais conhecidos no Braze, o Push Stories, é um excelente exemplo de como pode ser uma extensão de aplicativo de conteúdo de notificação por push!
Solicitações
- Notificações por push integradas com sucesso em seu app
- Os seguintes arquivos gerados pelo Xcode com base em sua linguagem de codificação:
Rápido
NotificationViewController.swift
MainInterface.storyboard
Objective C
NotificationViewController.h
NotificationViewController.m
MainInterface.storyboard
Notificações por push interativas
As notificações por push podem responder às ações do usuário dentro de uma extensão de app de conteúdo. Para usuários que executam o iOS 12 ou posterior, isso significa que você pode transformar suas notificações por push em mensagens totalmente interativas! Isso oferece uma opção interessante para introduzir interatividade em suas promoções e aplicativos. Por exemplo, sua notificação por push pode incluir um jogo para os usuários jogarem, uma roda para ganhar descontos ou um botão “curtir” para salvar uma listagem ou música.
O exemplo a seguir mostra uma notificação por push em que os usuários podem jogar uma partida dentro da notificação expandida.
Configuração do dashboard
Para criar uma notificação por push interativa, você deve definir uma exibição personalizada em seu dashboard.
- Na página Campanhas, clique em Criar campanha para iniciar uma nova campanha de notificação por push.
- Na guia Criar, ative os botões de notificação.
- Digite uma categoria personalizada do iOS no campo Categoria de notificação do iOS.
- No endereço
.plist
de seu Notification Content Extension Target, defina o atributoUNNotificationExtensionCategory
para sua categoria personalizada do iOS. O valor fornecido aqui deve corresponder ao que está definido no dashboard do Braze em iOS Notification Category (Categoria de notificação do iOS). - Defina a chave
UNNotificationExtensionInteractionEnabled
comotrue
para ativar as interações do usuário em uma notificação por push.
Pronto para fazer a análise de dados?
Visite a seção Análise de registros para entender melhor como deve ser o fluxo de dados.
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. Isso permite criar conteúdo push focado no usuário, como adicionar a opção de compartilhar seu progresso em diferentes plataformas, mostrar conquistas desbloqueadas ou exibir listas de verificação de integração. Este exemplo mostra uma notificação por push exibida a um usuário após ele ter concluído uma tarefa específica no curso do Braze Learning. Ao expandir a notificação, o usuário pode ver seu progresso na jornada de aprendizagem. As informações fornecidas aqui são específicas do usuário e podem ser disparadas quando uma sessão é concluída ou quando uma ação específica do usuário é realizada, aproveitando um acionador da API.
Configuração do dashboard
Para criar uma notificação por push personalizada, você deve definir uma exibição personalizada em seu dashboard.
- Na página Campanhas, clique em Criar campanha para iniciar uma nova campanha de notificação por push.
- Na guia Criar, ative os botões de notificação.
- Digite uma categoria personalizada do iOS no campo Categoria de notificação do iOS.
- Na guia Settings (Configurações ), crie pares de valores-chave usando o Liquid padrão. Defina as atribuições de usuário apropriadas que deseja que a mensagem mostre. Essas visualizações podem ser personalizadas com base em atribuições específicas de um perfil de usuário específico.
- No endereço
.plist
de seu Notification Content Extension Target, defina o atributoUNNotificationExtensionCategory
para sua categoria personalizada do iOS. O valor fornecido aqui deve corresponder ao que está definido no dashboard do Braze em iOS Notification Category (Categoria de notificação do iOS).
Manuseio de pares de valores-chave
O método didReceive
é chamado quando a extensão do app de conteúdo de notificação recebe uma notificação. Esse método pode ser encontrado no site NotificationViewController
. Os pares de valores-chave fornecidos no dashboard são representados no código por meio do uso de um dicionário userInfo
.
Analisando pares de chave-valor de notificações por push
1
2
3
4
5
6
7
8
9
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.")}
...
}
1
2
3
4
5
6
7
8
9
10
11
- (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"];
}
}
Pronto para fazer a análise de dados?
Visite a seção Análise de registros para entender melhor como deve ser o fluxo de dados.
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 app de conteúdo, empurrando os limites do que é possível com um push. Solicitar a entrada do usuário por meio de notificações por push permite não apenas solicitar informações básicas, como nome ou e-mail, mas também solicitar que os usuários enviem feedback ou completem um perfil de usuário inacabado.
No fluxo a seguir, a exibição personalizada é capaz de responder a alterações de estado. Esses componentes de alteração de estado são representados em cada imagem.
- O usuário recebe uma notificação por push.
- O push é aberto. Depois de expandido, o push solicita informações ao usuário. Neste exemplo, o endereço de e-mail do usuário é solicitado, mas você pode solicitar qualquer tipo de informação.
- As informações são fornecidas e, se estiverem no formato esperado, o botão de registro é exibido.
- A visualização de confirmação é exibida e o push é dispensado.
Configuração do dashboard
Para criar uma notificação por push de captura de informações, você deve definir uma exibição personalizada em seu dashboard.
- Na página Campanhas, clique em Criar campanha para iniciar uma nova campanha de notificação por push.
- Na guia Criar, ative os botões de notificação.
- Digite uma categoria personalizada do iOS no campo Categoria de notificação do iOS.
- Na guia Settings (Configurações ), crie pares de valores-chave usando o Liquid padrão. Defina as atribuições de usuário apropriadas que deseja que a mensagem mostre.
- No endereço
.plist
de seu Notification Content Extension Target, defina o atributoUNNotificationExtensionCategory
para sua categoria personalizada do iOS. O valor fornecido aqui deve corresponder ao que está definido no dashboard do Braze em iOS Notification Category (Categoria de notificação do iOS).
Como visto no exemplo, você também pode incluir uma imagem em sua notificação por push. Para isso, você deve integrar notificações ricas, definir o estilo de notificação em sua campanha como Notificação Rich e incluir uma imagem de notificação por push rica.
Manipulação de ações de botões
Cada botão de ação é identificado de forma exclusiva. O código verifica se o identificador da resposta é igual a actionIndentifier
e, em caso afirmativo, sabe que o usuário clicou no botão de ação.
Manipulação de respostas de botões de ação por push
1
2
3
4
5
6
7
func didReceive(_ response: UNNotificationResponse, completionHandler completion: @escaping (UNNotificationContentExtensionResponseOption) -> Void) {
if response.actionIdentifier == "YOUR-REGISTER-IDENTIFIER" {
// do something
} else {
// do something else
}
}
1
2
3
4
5
6
7
- (void)didReceiveNotificationResponse:(UNNotificationResponse *)response completionHandler:(void (^)(UNNotificationContentExtensionResponseOption))completion {
if ([response.actionIdentifier isEqualToString:@"YOUR-REGISTER-IDENTIFIER"]) {
completion(UNNotificationContentExtensionResponseOptionDismiss);
} else {
completion(UNNotificationContentExtensionResponseOptionDoNotDismiss);
}
}
Descarte de push
As notificações por push podem ser automaticamente descartadas a partir do pressionamento de um botão de ação. Há três opções pré-construídas de push dismissal que recomendamos:
completion(.dismiss)
- Descarta a notificaçãocompletion(.doNotDismiss)
- A notificação permanece abertacompletion(.dismissAndForward)
- O push é descartado e o usuário é encaminhado para o aplicativo
Pronto para fazer a análise de dados?
Consulte a seção a seguir para entender melhor como o fluxo de dados deve ser.
Análise de dados de registro
Registro com a API do Braze (recomendado)
A análise de dados de registro pode ser feita em tempo real com a ajuda do endpoint da API da Braze.](/docs/pt-br/api/endpoints/user_data/post_user_track/) Para análise de dados, envie o valor chave-valor braze_id
(como visto na captura de tela a seguir) para identificar qual perfil de usuário deve ser atualizado.
Registro manual
O registro manual exigirá primeiro a configuração dos espaços de trabalho no Xcode e, em seguida, a criação, o salvamento e a recuperação da análise de dados. Isso exigirá algum trabalho de desenvolvedor personalizado de sua parte. Os trechos de código mostrados a seguir ajudarão a resolver esse problema.
É importante notar que a análise de dados não é enviada à Braze até que o aplicativo móvel seja lançado posteriormente. Isso significa que, dependendo das configurações de dispensa, muitas vezes existe um período indeterminado de tempo entre o momento em que uma notificação por push é dispensada e o aplicativo móvel é iniciado e as análises de dados são recuperadas. Embora esse buffer de tempo possa não afetar todos os casos de uso, você deve considerar esse impacto e ajustar a jornada do usuário conforme necessário para incluir a abertura do aplicativo para resolver essa preocupação.
Etapa 1: Configurar grupos de app no Xcode
No Xcode, adicione o recurso App Groups
. Se você não tiver nenhum espaço de trabalho em seu aplicativo, acesse o recurso do direcionamento do aplicativo principal, ative o endereço App Groups
e clique no botão + Adicionar. Em seguida, use o ID do pacote de seu app para criar o espaço de trabalho. Por exemplo, se o ID do pacote do seu app for com.company.appname
, você poderá nomear seu espaço de trabalho como group.com.company.appname.xyz
. Certifique-se de que o endereço App Groups
esteja ativado tanto para o destino principal do app quanto para o destino da extensão de conteúdo.
Etapa 2: Integrar trechos de código
Os seguintes trechos de código são uma referência útil sobre como salvar e enviar eventos personalizados, atributos personalizados e atributos de usuário. Este guia falará em termos de UserDefaults
, mas a representação do código será feita na forma do arquivo auxiliar RemoteStorage
. Há arquivos auxiliares adicionais, UserAttributes
e EventName Dictionary
, que são usados ao enviar e salvar atribuições do usuário.
Salvando eventos personalizados
Para salvar eventos personalizados, você deve criar a análise de dados do zero. Isso é feito com a criação de um dicionário, preenchendo-o com metadados e salvando os dados por meio do uso de um arquivo auxiliar.
- Inicializar um dicionário com metadados de eventos
- Inicialize
userDefaults
para recuperar e armazenar os dados do evento - Se houver um vetor existente, acrescente novos dados a ele e salve-o
- Se não houver um vetor existente, salve o novo vetor em
userDefaults
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
func saveCustomEvent(with properties: [String: Any]? = nil) {
// 1
let customEventDictionary = Dictionary(eventName: "YOUR-EVENT-NAME", properties: properties)
// 2
let remoteStorage = RemoteStorage(storageType: .suite)
// 3
if var pendingEvents = remoteStorage.retrieve(forKey: .pendingCustomEvents) as? [[String: Any]] {
pendingEvents.append(contentsOf: [customEventDictionary])
remoteStorage.store(pendingEvents, forKey: .pendingCustomEvents)
} else {
// 4
remoteStorage.store([customEventDictionary], forKey: .pendingCustomEvents)
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
- (void)saveCustomEvent:(NSDictionary<NSString *, id> *)properties {
// 1
NSDictionary<NSString *, id> *customEventDictionary = [[NSDictionary alloc] initWithEventName:@"YOUR-EVENT-NAME" properties:properties];
// 2
RemoteStorage *remoteStorage = [[RemoteStorage alloc] initWithStorageType:StorageTypeSuite];
NSMutableArray *pendingEvents = [[remoteStorage retrieveForKey:RemoteStorageKeyPendingCustomEvents] mutableCopy];
// 3
if (pendingEvents) {
[pendingEvents addObject:customEventDictionary];
[remoteStorage store:pendingEvents forKey:RemoteStorageKeyPendingCustomAttributes];
} else {
// 4
[remoteStorage store:@[ customEventDictionary ] forKey:RemoteStorageKeyPendingCustomAttributes];
}
}
Envio de eventos personalizados para o Braze
O melhor momento para registrar qualquer análise de dados salva de uma extensão de app de conteúdo de notificação é logo após a inicialização do SDK. Isso pode ser feito percorrendo todos 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.
- Percorrer a matriz de eventos pendentes
- Percorra cada par de valores-chave no dicionário
pendingEvents
- Verifique explicitamente a chave de “Event Name” para definir o valor de acordo
- Todos os outros valores-chave serão adicionados ao dicionário
properties
- Registrar eventos personalizados individuais
- Remover todos os eventos pendentes do armazenamento
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
func logPendingCustomEventsIfNecessary() {
let remoteStorage = RemoteStorage(storageType: .suite)
guard let pendingEvents = remoteStorage.retrieve(forKey: .pendingCustomEvents) as? [[String: Any]] else { return }
// 1
for event in pendingEvents {
var eventName: String?
var properties: [AnyHashable: Any] = [:]
// 2
for (key, value) in event {
if key == 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 {
AppDelegate.braze?.logCustomEvent(eventName, properties: properties)
}
}
// 6
remoteStorage.removeObject(forKey: .pendingCustomEvents)
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
- (void)logPendingEventsIfNecessary {
RemoteStorage *remoteStorage = [[RemoteStorage alloc] initWithStorageType:StorageTypeSuite];
NSArray *pendingEvents = [remoteStorage retrieveForKey:RemoteStorageKeyPendingCustomEvents];
// 1
for (NSDictionary<NSString *, id> *event in pendingEvents) {
NSString *eventName = nil;
NSMutableDictionary *properties = [NSMutableDictionary dictionary];
// 2
for (NSString* key in event) {
if ([key isEqualToString:@"event_name"]) {
// 3
if ([[event objectForKey:key] isKindOfClass:[NSString class]]) {
eventName = [event objectForKey:key];
} else {
NSLog(@"Invalid type for event_name key");
}
} else {
// 4
properties[key] = event[key];
}
}
// 5
if (eventName != nil) {
[AppDelegate.braze logCustomEvent:eventName properties:properties];
}
}
// 6
[remoteStorage removeObjectForKey:RemoteStorageKeyPendingCustomEvents];
}
Salvando atributos personalizados
Para salvar atributos personalizados, você deve criar a análise de dados do zero. Isso é feito com a criação de um dicionário, preenchendo-o com metadados e salvando os dados por meio do uso de um arquivo auxiliar.
- Inicializar um dicionário com metadados de atribuição
- Inicialize o site
userDefaults
para recuperar e armazenar os dados de atribuição - Se houver um vetor existente, acrescente novos dados a ele e salve-o
- Se não houver um vetor existente, salve o novo vetor em
userDefaults
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
func saveCustomAttribute() {
// 1
let customAttributeDictionary: [String: Any] = ["YOUR-CUSTOM-ATTRIBUTE-KEY": "YOUR-CUSTOM-ATTRIBUTE-VALUE"]
// 2
let remoteStorage = RemoteStorage(storageType: .suite)
// 3
if var pendingAttributes = remoteStorage.retrieve(forKey: .pendingCustomAttributes) as? [[String: Any]] {
pendingAttributes.append(contentsOf: [customAttributeDictionary])
remoteStorage.store(pendingAttributes, forKey: .pendingCustomAttributes)
} else {
// 4
remoteStorage.store([customAttributeDictionary], forKey: .pendingCustomAttributes)
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
- (void)saveCustomAttribute {
// 1
NSDictionary<NSString *, id> *customAttributeDictionary = @{ @"YOUR-CUSTOM-ATTRIBUTE-KEY": @"YOUR-CUSTOM-ATTRIBUTE-VALUE" };
// 2
RemoteStorage *remoteStorage = [[RemoteStorage alloc] initWithStorageType:StorageTypeSuite];
NSMutableArray *pendingAttributes = [[remoteStorage retrieveForKey:RemoteStorageKeyPendingCustomAttributes] mutableCopy];
// 3
if (pendingAttributes) {
[pendingAttributes addObject:customAttributeDictionary];
[remoteStorage store:pendingAttributes forKey:RemoteStorageKeyPendingCustomAttributes];
} else {
// 4
[remoteStorage store:@[ customAttributeDictionary ] forKey:RemoteStorageKeyPendingCustomAttributes];
}
}
Envio de atributos personalizados para o Braze
O melhor momento para registrar qualquer análise de dados salva de uma extensão de app de conteúdo de notificação é logo após a inicialização do SDK. 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.
- Percorrer a matriz de atribuições pendentes
- Percorra cada par de valores-chave no dicionário
pendingAttributes
- Registre atributos personalizados individuais com a chave e o valor correspondentes
- Remover todas as atribuições pendentes do armazenamento
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
func logPendingCustomAttributesIfNecessary() {
let remoteStorage = RemoteStorage(storageType: .suite)
guard let pendingAttributes = remoteStorage.retrieve(forKey: .pendingCustomAttributes) as? [[String: Any]] else { return }
// 1
pendingAttributes.forEach { setCustomAttributesWith(keysAndValues: $0) }
// 4
remoteStorage.removeObject(forKey: .pendingCustomAttributes)
}
func setCustomAttributesWith(keysAndValues: [String: Any]) {
// 2
for (key, value) in keysAndValues {
// 3
if let value = value as? [String] {
setCustomAttributeArrayWithKey(key, andValue: value)
} else {
setCustomAttributeWithKey(key, andValue: value)
}
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
- (void)logPendingCustomAttributesIfNecessary {
RemoteStorage *remoteStorage = [[RemoteStorage alloc] initWithStorageType:StorageTypeSuite];
NSArray *pendingAttributes = [remoteStorage retrieveForKey:RemoteStorageKeyPendingCustomAttributes];
// 1
for (NSDictionary<NSString*, id> *attribute in pendingAttributes) {
[self setCustomAttributeWith:attribute];
}
// 4
[remoteStorage removeObjectForKey:RemoteStorageKeyPendingCustomAttributes];
}
- (void)setCustomAttributeWith:(NSDictionary<NSString *, id> *)keysAndValues {
// 2
for (NSString *key in keysAndValues) {
// 3
[self setCustomAttributeWith:key andValue:[keysAndValues objectForKey:key]];
}
}
Salvando atribuições do usuário
Ao salvar atribuições do usuário, recomendamos a criação de um objeto personalizado para decifrar que 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 obter um exemplo de como fazer isso.
- Inicialize um objeto
UserAttribute
codificado com o tipo correspondente - Inicialize
userDefaults
para recuperar e armazenar os dados do evento - Se houver um vetor existente, acrescente novos dados a ele e salve-o
- Se não houver um vetor existente, salve o novo vetor em
userDefaults
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
func saveUserAttribute() {
// 1
guard let data = try? PropertyListEncoder().encode(UserAttribute.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)
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
- (void)saveUserAttribute {
// 1
UserAttribute *userAttribute = [[UserAttribute alloc] initWithUserField:@"USER-ATTRIBUTE-VALUE" attributeType:UserAttributeTypeEmail];
NSError *error;
NSData *data = [NSKeyedArchiver archivedDataWithRootObject:userAttribute requiringSecureCoding:YES error:&error];
if (error != nil) {
// log error
}
// 2
RemoteStorage *remoteStorage = [[RemoteStorage alloc] initWithStorageType:StorageTypeSuite];
NSMutableArray *pendingAttributes = [[remoteStorage retrieveForKey:RemoteStorageKeyPendingUserAttributes] mutableCopy];
// 3
if (pendingAttributes) {
[pendingAttributes addObject:data];
[remoteStorage store:pendingAttributes forKey:RemoteStorageKeyPendingUserAttributes];
} else {
// 4
[remoteStorage store:@[data] forKey:RemoteStorageKeyPendingUserAttributes];
}
}
Envio de atribuições do usuário para o Braze
O melhor momento para registrar qualquer análise de dados salva de uma extensão de app de conteúdo de notificação é logo após a inicialização do SDK. 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 vetor de dados
pendingAttributes
- Inicializar um objeto
UserAttribute
codificado a partir de dados de atribuição - Defina um campo de usuário específico com base no tipo de atribuição do usuário (e-mail)
- Remova todas as atribuições de usuário pendentes do armazenamento
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
func logPendingUserAttributesIfNecessary() {
let remoteStorage = RemoteStorage(storageType: .suite)
guard let pendingAttributes = remoteStorage.retrieve(forKey: .pendingUserAttributes) as? [Data] else { return }
// 1
for attributeData in pendingAttributes {
// 2
guard let userAttribute = try? PropertyListDecoder().decode(UserAttribute.self, from: attributeData) else { continue }
// 3
switch userAttribute {
case .email(let email):
user?.email = email
}
}
// 4
remoteStorage.removeObject(forKey: .pendingUserAttributes)
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
- (void)logPendingUserAttributesIfNecessary {
RemoteStorage *remoteStorage = [[RemoteStorage alloc] initWithStorageType:StorageTypeSuite];
NSArray *pendingAttributes = [remoteStorage retrieveForKey:RemoteStorageKeyPendingUserAttributes];
// 1
for (NSData *attributeData in pendingAttributes) {
NSError *error;
// 2
UserAttribute *userAttribute = [NSKeyedUnarchiver unarchivedObjectOfClass:[UserAttribute class] fromData:attributeData error:&error];
if (error != nil) {
// log error
}
// 3
if (userAttribute) {
switch (userAttribute.attributeType) {
case UserAttributeTypeEmail:
[self user].email = userAttribute.userField;
break;
}
}
}
// 4
[remoteStorage removeObjectForKey:RemoteStorageKeyPendingUserAttributes];
}
Arquivos auxiliares
Arquivo auxiliar RemoteStorage
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
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)
}
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
@interface RemoteStorage ()
@property (nonatomic) StorageType storageType;
@property (nonatomic, strong) NSUserDefaults *defaults;
@end
@implementation RemoteStorage
- (id)initWithStorageType:(StorageType)storageType {
if (self = [super init]) {
self.storageType = storageType;
}
return self;
}
- (void)store:(id)value forKey:(RemoteStorageKey)key {
[[self defaults] setValue:value forKey:[self rawValueForKey:key]];
}
- (id)retrieveForKey:(RemoteStorageKey)key {
return [[self defaults] objectForKey:[self rawValueForKey:key]];
}
- (void)removeObjectForKey:(RemoteStorageKey)key {
[[self defaults] removeObjectForKey:[self rawValueForKey:key]];
}
- (void)resetStorageKeys {
[[self defaults] removeObjectForKey:[self rawValueForKey:RemoteStorageKeyPendingCustomEvents]];
[[self defaults] removeObjectForKey:[self rawValueForKey:RemoteStorageKeyPendingCustomAttributes]];
[[self defaults] removeObjectForKey:[self rawValueForKey:RemoteStorageKeyPendingUserAttributes]];
}
- (NSUserDefaults *)defaults {
if (!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
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
enum UserAttribute: Hashable {
case email(String?)
}
// MARK: - Codable
extension UserAttribute: Codable {
private enum CodingKeys: String, CodingKey {
case email
}
func encode(to encoder: Encoder) throws {
var values = encoder.container(keyedBy: CodingKeys.self)
switch self {
case .email(let email):
try values.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)
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
@implementation UserAttribute
- (id)initWithUserField:(NSString *)userField attributeType:(UserAttributeType)attributeType {
if (self = [super init]) {
self.userField = userField;
self.attributeType = attributeType;
}
return self;
}
- (void)encodeWithCoder:(NSCoder *)encoder {
[encoder encodeObject:self.userField forKey:@"userField"];
[encoder encodeInteger:self.attributeType forKey:@"attributeType"];
}
- (id)initWithCoder:(NSCoder *)decoder {
if (self = [super init]) {
self.userField = [decoder decodeObjectForKey:@"userField"];
NSInteger attributeRawValue = [decoder decodeIntegerForKey:@"attributeType"];
self.attributeType = (UserAttributeType) attributeRawValue;
}
return self;
}
@end
Arquivo auxiliar do dicionário EventName
1
2
3
4
5
6
7
8
9
10
11
12
extension Dictionary where Key == String, Value == Any {
init(eventName: String, properties: [String: Any]? = nil) {
self.init()
self[PushNotificationKey.eventName.rawValue] = eventName
if let properties = properties {
for (key, value) in properties {
self[key] = value
}
}
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
@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