Visão geral
NottiSDK é o SDK nativo oficial de iOS para notificações push do Notti (APNs). Ele cuida do registro de dispositivo, identidade do usuário, tags, eventos de engajamento (received / opened / clicked), mídia rica em uma Notification Service Extension e mensagens in-app, para que a integração nunca precise montar chamadas REST manualmente contra a API de Devices. Ele é distribuído em dois produtos: NottiSDK para o app e NottiSDKExtension para a Notification Service Extension.
A versão 0.1.0 (lançada em 2026-10-26) é a primeira. Ela porta o react-native-notti 0.8.0 para iOS, com mensagens in-app incluídas, o mesmo contrato com o backend e o mesmo armazenamento no dispositivo. O checklist de dispositivo passou nos aparelhos do mantenedor e a validação da App Store passou; um beta em um app hospedeiro em produção vem a seguir. Veja Limitações conhecidas.
Por padrão, o SDK se comunica com o Notti (https://app.zeepnotti.app), então Notti.initialize só precisa do ID do seu app e da chave de cliente.
Requisitos
- iOS 15.0+.
- Xcode 15.3 ou mais recente. O target da extensão usa
nonisolated(unsafe), que exige Swift 5.10. Apenas o Xcode 26.6 e o 27.1 são testados. - Um backend do Notti que aceite o que o SDK envia: v0.10.0 ou mais recente para os campos de perfil do dispositivo, v0.11.0 ou mais recente para o evento
opened, v0.23.0 ou mais recente para mensagens in-app e v0.24.0 ou mais recente para a desvinculação nologout(veja Limitações conhecidas). Um backend mais antigo rejeita esses campos ou eventos e o SDK os descarta. - Uma conta Apple Developer com push notifications e uma chave APNs configurada no seu App do Notti.
- Um App do Notti e seus ID do app/chave de cliente - veja Primeiros passos com o Notti para criar um.
Instalação
O SDK é distribuído via Swift Package Manager e CocoaPods. Para um app nativo, o caminho recomendado é o Swift Package Manager; o CocoaPods também é suportado.
Swift Package Manager
No Xcode: File → Add Package Dependencies… e informe:
https://github.com/zeeplabs/zeep-notti-ios-sdk.gitUse a regra de dependência Up to Next Major Version a partir de 0.1.0.
- Adicione o produto
NottiSDKao target do seu app. - Adicione o produto
NottiSDKExtensionao target da sua Notification Service Extension (opcional, para mídia rica).
Em um Package.swift:
.package(url: "https://github.com/zeeplabs/zeep-notti-ios-sdk.git", from: "0.1.0")CocoaPods
target 'YourApp' do
pod 'NottiSDK', '~> 0.1'
end
target 'YourNotificationServiceExtension' do
pod 'NottiSDKExtension', '~> 0.1'
endDepois rode pod install.
Linkar o NottiSDKExtension no target do app compila, mas não expõe nenhuma API de app. Use o NottiSDK ali. Na extensão, use import NottiSDKExtension, a mesma linha usada com SPM. Os dois pods funcionam com frameworks dinâmicos, frameworks estáticos e bibliotecas estáticas.
Capabilities
No target do app, em Signing & Capabilities:
- Push Notifications
- Background Modes → Remote notifications (necessário para pushes silenciosos)
A Notification Service Extension não precisa de nenhuma capability própria.
Inicializando e pedindo permissão
Chame Notti.initialize uma vez na inicialização com o ID do app e a chave de cliente (nunca a chave REST - veja a Referência da API de Apps para entender o porquê) e depois peça a permissão quando fizer sentido para o seu usuário. requestPermission retorna se o usuário concedeu a permissão.
Notti.initialize(appId: "YOUR_APP_ID", clientKey: "YOUR_CLIENT_KEY")
Task {
let granted = await Notti.Notifications.requestPermission()
print("push permission granted: \(granted)")
}Sem opções, o SDK se comunica com o host de produção https://app.zeepnotti.app. Para usar outro host, passe Notti.Options(baseUrl:). Notti.Options(opensInAppURLs: false) impede o SDK de abrir links web de mensagens in-app (veja Mensagens in-app). O SDK nunca pede permissão de localização.
Identidade e tags
login vincula o dispositivo ao ID do seu próprio usuário. E-mail, telefone e tags são opcionais e só são enviados quando você os define. logout limpa o vínculo no dispositivo, envia external_user_id: null para desvinculá-lo no backend e limpa e-mail e telefone; as tags permanecem.
- A desvinculação fica guardada no dispositivo até o backend confirmá-la: um
logoutfeito offline, antes deNotti.initializeou cuja requisição falhou é reenviado no próximo registro (umloginposterior, feito depois deNotti.initialize, o substitui). - Chamadas de
login, de tags e desetSubscriptionfeitas antes deNotti.initializesão descartadas, cada uma com um aviso (nívelwarn): chame-as depois deinitialize.logouté a única chamada de identidade mantida quando feita antes deinitialize. - Depois de
initializee antes do primeiro registro, só a última chamada deloginoulogouté enviada. - A desvinculação no backend exige o backend do Notti v0.24.0 ou mais recente: veja Limitações conhecidas.
Notti.login("user-123")
Notti.User.setEmail("person@example.invalid")
Notti.User.addTags(["plan": "premium", "locale": "pt-BR"])
Notti.User.removeTag("locale")
Notti.logout()Outras chamadas: Notti.User.setPhone, clearEmail, clearPhone, addTag, removeTags, Notti.User.setSubscription(_:) e Notti.setLocationSharingEnabled(_:) (desativado por padrão, veja Privacidade, rótulos da App Store e LGPD).
O compartilhamento de localização envia apenas o código do país, resolvido no dispositivo por geocodificação reversa de uma posição que o seu app já tem. O SDK nunca pede permissão de localização; a geocodificação reversa em si é feita pelo serviço da Apple.
Tratando eventos de notificação
Os listeners recebem o payload de um push que chegou com o app em execução (received) e de um toque (clicked). O SDK nunca abre a URL de um push: o data do push chega intacto ao seu código e você decide o que um link significa. (Mensagens in-app são diferentes: o SDK abre uma ação urlhttp/https de mensagem in-app e, para um botão push_permission, a página de configurações de notificação do sistema: veja Mensagens in-app.)
let received = Notti.Notifications.addReceivedListener { payload in
print("received with \(payload.data.count) data keys")
}
let clicked = Notti.Notifications.addClickedListener { payload in
// `payload.data` is the push data, untouched. Decide what a link means here.
print("clicked with \(payload.data.count) data keys")
}
// A tap that launched the app is kept until you take it.
if let initial = Notti.Notifications.takeInitialClick() {
print("launched from a push: \(initial.data.count) data keys")
}
received.remove()
clicked.remove()Um toque vai para os listeners de clique quando há pelo menos um registrado naquele momento. Um toque que chega sem nenhum listener registrado é guardado, e takeInitialClick() retorna o último toque guardado uma única vez. No UIKit, o toque que abriu o app chega depois que application(_:didFinishLaunchingWithOptions:) retorna, então um listener adicionado ali o recebe ao vivo e takeInitialClick() retorna nil; sem listener ainda, um toque posterior também é retornado por takeInitialClick(). Trate a abertura pelos dois caminhos, o que vier primeiro.
Remova um listener com remove() na assinatura retornada. Notti.addDeviceIdChangedListener chama seu handler na main thread sempre que o ID do dispositivo muda e é removido da mesma forma.
Os eventos de entrega (received, opened, clicked) ficam em fila no dispositivo e são reenviados com backoff (veja a política de retentativas em Limitações conhecidas).
Mensagens in-app
Links. Por padrão, o SDK abre no Safari uma ação
urlde mensagem in-app cujo valor é um linkhttpouhttps, depois que o seu listener de clique rodou: não abra o link de novo no listener. Ele nunca abre umdeeplink, nem umaurlcom qualquer outro esquema (tel:,sms:, esquemas próprios): esses ficam com você, pelo listener de clique. Sem listener, um toque nesse botão ainda reportaclickao Notti e fecha a mensagem, nada é aberto e o SDK registra um aviso (nívelwarn). Para abrir links web por conta própria, passeNotti.Options(opensInAppURLs: false)paraNotti.initialize.
As mensagens são criadas no painel do Notti (veja Mensagens in-app) e exigem o backend do Notti v0.23.0 ou mais recente. O SDK pergunta ao backend uma vez por sessão em foreground, depois que este processo registrou o dispositivo, quais mensagens este dispositivo pode ver (POST /v1/apps/<appId>/in-app-messages/eligible). Ele exibe a primeira mensagem session_start imediatamente e mantém as mensagens event em memória até que o seu app reporte um evento correspondente. No máximo uma mensagem fica visível, e cada mensagem aparece no máximo uma vez por sessão. Se ela aparece de novo em uma sessão posterior é decisão do backend: o SDK não guarda estado de "já exibida". O lado do servidor dessa troca está descrito no contrato das mensagens in-app.
// Add it before `Notti.initialize`, so a message shown right after launch already has a listener.
let inAppClicks = Notti.InAppMessages.addClickListener { click in
// nil for a `push_permission` button and for a button without a valid action.
guard let action = click.action else { return }
// `action.value` is the author's link, untouched.
if action.type == .deeplink {
// The SDK never opens a deeplink: route it inside the app.
print("route the deeplink inside the app")
} else {
// The SDK opens an http(s) url in Safari after this listener returns: do not open it again.
print("url click, opened by the SDK")
}
}
// Shows the oldest waiting message whose trigger is this event name (exact, case-sensitive).
Notti.trackEvent("checkout_completed")
inAppClicks.remove()Notti.trackEvent(_:)faz a correspondência apenas no dispositivo: o nome nunca é enviado, armazenado ou registrado em log. Um nome vazio ou só com espaços não faz nada. Uma chamada antes deNotti.initialize, ou em background depois que uma sessão terminou, é ignorada. Uma chamada depois deNotti.initializee antes de a primeira sessão abrir é lembrada para essa sessão (até 32 nomes distintos), entãoinitializeseguido detrackEventna inicialização funciona. Essa ordem na inicialização ainda não foi comprovada em dispositivo.- O listener de clique roda na main thread para um toque em um botão, em uma imagem tocável ou no corpo de uma mensagem
topoubottomque tem botões (ele age como o primeiro botão).NottiInAppClicktrazmessageId,buttonId(imagepara a imagem) eaction: umNottiInAppActioncomtype(urloudeeplink) e ovalueintacto, ou nil. Guarde a assinatura retornada e chameremove()para parar. - Links web. Com
opensInAppURLsverdadeiro (o padrão), depois que os listeners rodaram e a mensagem fechou, o SDK abre uma açãourlno navegador externo (UIApplication.open) quando o valor é uma URLhttpouhttps(em qualquer caixa) com host e sem espaços. Um universal linkhttpsabre o app correspondente, conforme o iOS decidir. Qualquer outro valor não é aberto e registra uma linha fixa no níveldebug; uma abertura que falha não é repetida. O listener recebe todos os cliques de qualquer forma, com o valor intacto, e o SDK nunca registra o link em log. Umdeeplinknunca é aberto pelo SDK. Um segundoNotti.initializecom o mesmo ID do app, chave de cliente e URL base e outroopensInAppURLsaplica a opção sem reiniciar o SDK. TodoNotti.initializeválido define a opção com o valor que carrega, como obaseUrl: uma chamada posterior sem opções a reativa, então passe o mesmoNotti.Optionsem todas as chamadas (em Objective-C, use o seletoropensInAppURLs:todas as vezes). - Botões
push_permissionexecutam o fluxo de permissão do sistema em vez de um link: o listener recebe o clique comaction == nil, a mensagem fecha e então o SDK pede a permissão de push se ela nunca foi pedida, abre as configurações de notificação do app no app Ajustes se a permissão foi negada (iOS 16 e mais recentes; a página de configurações do app no iOS 15) e não faz mais nada se ela já foi concedida. Essa página de configurações é uma constante fixa do sistema, nunca um dado do autor. O backend entrega mensagenspush_permissionao iOS0.1.0e mais recentes, mas só a um dispositivo que informasdk_namecomoios, e esta versão do SDK ainda não o envia. - Eventos. O SDK reporta
impression(uma vez por exibição, sempre primeiro),click(com o ID do botão) edismiss(motivoclose_button,swipe,outside_tapouauto). Eles ficam em fila no dispositivo, são enviados em ordem com umevent_idgerado no cliente que sobrevive a retentativas e reinícios, e são descartados após 7 dias; acima de 100 eventos em fila, o mais antigo é descartado. - Apresentação. Quatro layouts: barras
topebottom, um cardcentersobre um fundo escurecido efulldentro da safe area. Cada mensagem tem um controle de fechar (44 pt, rótulo "Close", ou "Fechar" quando o primeiro idioma preferido é português). As barras fecham com um swipe,centercom um toque no fundo efullsó com o controle de fechar ou um botão. Umdismiss_after_secondsopcional (1 a 300) fecha a mensagem sozinho; a contagem pausa em background. - Janela própria. A mensagem aparece em uma
UIWindowseparada no nível.alert(acima das janelas do seu app, como noreact-native-notti) que nunca vira a key window, então não tira o foco do teclado do seu app. Toques fora de uma barra chegam ao seu app. A janela segue o root view controller da sua key window quanto às orientações suportadas e à status bar. Como ela se comporta junto com o teclado, a Central de Controle e o Picture in Picture ainda não foi comprovado em dispositivo. - Animação. Uma barra desliza 24 pt a partir da borda enquanto aparece em fade (0,25 s);
centerefullaparecem em fade (0,25 s); toda saída some em fade em 0,2 s, e os toques passam através dela enquanto isso. Com Reduzir Movimento ativado, entrada e saída são um cross-fade de 0,15 s sem movimento. - Somente texto puro.
titleebodysão exibidos caractere por caractere: HTML, Markdown e links não são renderizados. As cores do painel (#RRGGBB) são aplicadas como enviadas; um valor inválido volta ao padrão, e os padrões seguem o modo escuro. O texto escala com o Dynamic Type; textos longos rolam enquanto o controle de fechar e os botões continuam acessíveis. - Acessibilidade. O título é um cabeçalho, seguido do corpo e dos botões.
centerefullsão modais para o VoiceOver, recebem o foco e fecham com o gesto de escape (Z com dois dedos) comoclose_button; as barras são anunciadas e não recebem o foco. - Imagens. Apenas imagens
https, de no máximo 5 MiB, baixadas sem a sua chave de cliente, token ou cookies. Se a imagem não ficar pronta em 3 segundos, a mensagem aparece sem ela.
Notificações push ricas (Notification Service Extension)
Adicione um target de Notification Service Extension, linke o NottiSDKExtension e encaminhe os dois callbacks. A extensão baixa a URL de mídia trazida pelo push (chave notti_image_url, nunca parte de payload.data) e a anexa. A URL precisa ser https, o arquivo ter no máximo 10 MiB e o tipo ser jpg, png, gif, mp4 ou mp3, pelo MIME type ou pela extensão da URL. Um arquivo de tipo desconhecido ainda é tentado como jpg, então pode ou não ser renderizado. O download tem um prazo de 20 segundos; se o tempo acabar, a extensão entrega o conteúdo que já tem.
import NottiSDKExtension
import UserNotifications
@objc(NotificationService)
final class NotificationService: UNNotificationServiceExtension {
private var contentHandler: ((UNNotificationContent) -> Void)?
private var bestAttemptContent: UNNotificationContent?
override func didReceive(
_ request: UNNotificationRequest,
withContentHandler contentHandler: @escaping (UNNotificationContent) -> Void
) {
self.contentHandler = contentHandler
bestAttemptContent = request.content
NottiNotificationServiceExtension.didReceive(request, withContentHandler: contentHandler)
}
override func serviceExtensionTimeWillExpire() {
guard let contentHandler else { return }
NottiNotificationServiceExtension.serviceExtensionTimeWillExpire(
for: bestAttemptContent, contentHandler: contentHandler
)
}
}A Notification Service Extension não é exercitada no Simulador do iOS pelos testes do próprio SDK - teste mídia rica em um dispositivo físico.
Integração com o delegate e desativação do swizzling
Por padrão, o SDK se instala na inicialização e observa os callbacks de push no delegate do seu app e no delegate do UNUserNotificationCenter, então nenhum código de encaminhamento é necessário. Isso funciona com um delegate UIKit e com o @UIApplicationDelegateAdaptor do SwiftUI. Um app SwiftUI sem o adaptor não é suportado pelo swizzling: veja Limitações conhecidas. Quando o app deixa o delegate do UNUserNotificationCenter como nil, o SDK atribui o seu ao fim da inicialização; um delegate que você definir depois é observado, não substituído.
Para desativar isso, adicione esta chave ao Info.plist do seu app. Só um Boolean true conta; uma string ou um número é ignorado e o SDK registra um aviso.
<key>NottiDisableSwizzling</key>
<true/>Com a chave definida, você mesmo define o delegate do notification center e encaminha estes cinco callbacks:
final class ForwardingAppDelegate: NSObject, UIApplicationDelegate, UNUserNotificationCenterDelegate {
func application(
_ application: UIApplication,
didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]? = nil
) -> Bool {
UNUserNotificationCenter.current().delegate = self
return true
}
func application(
_ application: UIApplication,
didRegisterForRemoteNotificationsWithDeviceToken deviceToken: Data
) {
Notti.Notifications.didRegisterForRemoteNotifications(deviceToken: deviceToken)
}
func application(
_ application: UIApplication,
didFailToRegisterForRemoteNotificationsWithError error: Error
) {
Notti.Notifications.didFailToRegisterForRemoteNotifications(error: error)
}
func application(
_ application: UIApplication,
didReceiveRemoteNotification userInfo: [AnyHashable: Any],
fetchCompletionHandler completionHandler: @escaping (UIBackgroundFetchResult) -> Void
) {
completionHandler(Notti.Notifications.didReceiveRemoteNotification(userInfo))
}
func userNotificationCenter(
_ center: UNUserNotificationCenter,
willPresent notification: UNNotification,
withCompletionHandler completionHandler: @escaping (UNNotificationPresentationOptions) -> Void
) {
completionHandler(Notti.Notifications.willPresent(notification))
}
func userNotificationCenter(
_ center: UNUserNotificationCenter,
didReceive response: UNNotificationResponse,
withCompletionHandler completionHandler: @escaping () -> Void
) {
Notti.Notifications.didReceive(response)
completionHandler()
}
}Objective-C
O SDK pode ser usado em Objective-C pelo mesmo módulo. A classe de entrada é NottiSDK, não Notti, para poder conviver no mesmo app com o react-native-notti, cuja classe de módulo é Notti. As demais classes são NottiUser, NottiNotifications e NottiInAppMessages.
@import NottiSDK;
[NottiSDK initializeWithAppId:@"YOUR_APP_ID" clientKey:@"YOUR_CLIENT_KEY" baseUrl:nil];
// Or, to open In-App web links in your own code instead of the SDK:
// [NottiSDK initializeWithAppId:@"YOUR_APP_ID" clientKey:@"YOUR_CLIENT_KEY" baseUrl:nil opensInAppURLs:NO];
[NottiNotifications requestPermission:^(BOOL granted) { /* ... */ }];
[NottiSDK login:@"user-123"];
[NottiUser addTag:@"plan" value:@"premium"];
NottiSubscription *sub = [NottiNotifications addClickedListener:^(NSDictionary *payload) { /* ... */ }];
[sub remove];
[NottiSDK trackEvent:@"checkout_completed"];
// payload: messageId, buttonId, action (@{@"type": @"url" or @"deeplink", @"value": ...} or NSNull)
NottiSubscription *clicks = [NottiInAppMessages addClickListener:^(NSDictionary *payload) { /* ... */ }];Este bloco Objective-C não é compilado pela CI do SDK; os snippets Swift desta página são.
Privacidade, rótulos da App Store e LGPD
Esta seção descreve o que o SDK faz. Isto não é aconselhamento jurídico e não afirma que o seu uso está em conformidade com a LGPD - valide com seu próprio time jurídico/DPO antes de publicar.
Enviado ao backend do Notti
As requisições vão para /v1/apps/<appId>/devices (registro), /v1/apps/<appId>/devices/<deviceId> (toda atualização posterior, então o ID do dispositivo vai no path), /v1/apps/<appId>/notifications/<id>/events, /v1/apps/<appId>/in-app-messages/eligible e /v1/apps/<appId>/in-app-messages/<messageId>/events, autenticadas com a sua chave de cliente. Toda atualização de dispositivo também leva o token de push.
| Quando | Campos |
|---|---|
| Registro | token (token de push do APNs), platform (ios) |
login | external_user_id |
setEmail, setPhone | email, phone |
addTag, addTags, removeTag, removeTags | tags (seus próprios pares chave/valor) |
Permissão e setSubscription | permission_status, subscribed, last_unsubscribed_at |
| Perfil do dispositivo, sincronizado quando muda | app_version, sdk_version, device_os, device_model, timezone_id, language |
| Sessões | first_session_at, last_session_at, session_count, session_time_seconds |
| Compartilhamento de localização ativado, e o seu app já tem permissão de localização | apenas country (null quando você desativa o compartilhamento) |
| Um push é recebido, aberto ou clicado | delivery_id, type (received, opened, clicked), token, reportados sob o ID da notificação |
| Busca de mensagens in-app, uma vez por sessão em foreground | device_id, token |
| Uma mensagem in-app é exibida, tocada ou fechada | device_id, token, event_id, type (impression, click, dismiss), occurred_at, button_id (só em click, o ID definido pelo autor), reason (só em dismiss, um valor fixo), reportados sob o ID da mensagem |
Não coloque dados pessoais em chaves ou valores de tags, a menos que a sua própria base legal de tratamento cubra isso.
Armazenado no dispositivo
O SDK guarda seu estado na suíte notti_prefs do UserDefaults, sem criptografia, com os mesmos nomes de chave do react-native-notti 0.8.0. Isso inclui e-mail, telefone, ID de usuário externo e tags. A lista completa de chaves está no README do SDK.
Os logs passam por um único logger, com nível padrão error. Eles nunca contêm e-mail, telefone, token de push, ID do dispositivo, ID de usuário externo ou valores de tags, nem IDs, títulos, corpos, rótulos de botões, links, URLs de imagens de mensagens in-app ou nomes de trackEvent, e os erros são registrados como domain#code.
Privacy manifests
O NottiSDK inclui um PrivacyInfo.xcprivacy com NSPrivacyTracking falso e nenhum domínio de rastreamento. Ele declara a API de motivo obrigatório UserDefaults (CA92.1) e estes tipos de dados coletados, todos vinculados ao usuário e nenhum usado para rastreamento: Device ID, User ID, Email Address, Phone Number, Coarse Location, Product Interaction, Other Diagnostic Data e Other Data Types. As finalidades são App Functionality para todos os tipos, exceto Product Interaction, que declara apenas Analytics; Coarse Location declara as duas. O NottiSDKExtension inclui seu próprio manifest, sem dados coletados e sem API de motivo obrigatório. As mensagens in-app não acrescentam tipo de dado: seus eventos impression, click e dismiss são Product Interaction, e o ID do dispositivo e o token de push enviados em cada requisição in-app são Device ID.
Other Diagnostic Data e Other Data Types são a escolha mais próxima para campos que a lista da Apple não nomeia. Confirme-os com o responsável por privacidade.
O que o seu app declara no App Store Connect
As respostas de privacidade do seu app precisam cobrir o que o SDK coleta na sua configuração, além da sua própria coleta. Com base no manifest acima: Device ID; User ID se você chamar login; Email e Phone se você os definir; Coarse Location só se você ativar o compartilhamento de localização; Product Interaction; Other Diagnostic Data; e Other Data Types se você definir tags. Vinculado ao usuário: sim. Usado para rastreamento: não.
LGPD
Antes de publicar, peça aos seus times jurídico e de privacidade que revisem os dados acima, a base legal, o aviso exibido aos usuários e a retenção e exclusão no seu backend do Notti. Essa revisão inclui as mensagens in-app: cada requisição in-app leva o ID do dispositivo e o token de push, e cada mensagem que o usuário vê, toca ou fecha vira um evento vinculado ao registro do dispositivo. Os nomes de trackEvent ficam no dispositivo.
O SDK não tem API de consentimento na 0.1.0: controle você mesmo quando chamar Notti.initialize e Notti.login.
Limitações conhecidas
- Mídia rica só em dispositivo. A Notification Service Extension não é exercitada no Simulador pelos testes do próprio SDK.
logoute novo registro exigem o backend do Notti v0.24.0 ou mais recente. A partir dessa versão, umPATCHcomexternal_user_id: nulldesvincula o dispositivo, e um registro que omiteexternal_user_idetagsos mantém. Contra um backend mais antigo, a desvinculação responde 2xx mas o backend mantém o valor armazenado, então o dispositivo continua vinculado ao usuário anterior até a próxima inicialização (pushes para esse usuário podem continuar chegando em um dispositivo compartilhado); o SDK conta o 2xx como entregue e não tenta de novo. Um backend mais antigo também resetaexternal_user_ide tags a cada registro na inicialização, então chamelogineaddTagsde novo a cada inicialização. Oreact-native-notti0.8.0 tem os mesmos limites em um backend mais antigo.- As tags sobrevivem ao
logout.logoutnão limpa as tags no backend; limpe-as você mesmo (removeTags) se elas descrevem o usuário. - Backups podem levar a identidade do dispositivo para outro aparelho. A suíte
notti_prefsfaz parte do backup do dispositivo iOS. Restaurá-la em outro telefone pode direcionar atualizações para o registro do dispositivo original antes de o SDK se registrar de novo. Se isso importa para o seu app, exclua-a do backup. - SwiftUI sem
@UIApplicationDelegateAdaptor. O SDK não faz swizzling do app delegate interno do SwiftUI. Ele registra um aviso (nívelwarn, então você não vê nada no nível padrãoerror) e não recebe o token do APNs, então o dispositivo nunca se registra pelo SDK. Use@UIApplicationDelegateAdaptorcom o seu próprio delegate, ou definaNottiDisableSwizzlinge encaminhe as cinco chamadas você mesmo. - Delegates só com o método legado não recebem
receivedde push silencioso. Um app delegate que implementa sóapplication(_:didReceiveRemoteNotification:)(não a variante comfetchCompletionHandler) não recebe uma substituição, então um push silencioso não gera eventoreceived. Implemente o método moderno ou use a API manual. - Observação dupla. Se o seu delegate sobrescreve um callback de push e chama
super, o SDK pode observar o mesmo callback duas vezes. - Sem
receivedpara pushes visíveis semnotification_id. Um push de outro remetente, ou sem esse campo, não é reportado comoreceived. - Mensagens in-app em dispositivo. Renderização, VoiceOver, o nível da janela em relação ao teclado, à Central de Controle e ao Picture in Picture, apps legados sem scenes, multitarefa no iPad, o salto para as configurações do
push_permissione a ordem deinitialize,trackEvente da primeira sessão na inicialização ainda não foram comprovados separadamente em dispositivo. - Mensagens
push_permissionainda não são entregues a este SDK. O backend tem um mínimo por SDK para elas (iOS0.1.0), mas o aplica ao SDK que o dispositivo informa emsdk_name. Esta versão do SDK não o informa, então o backend trata o dispositivo como React Native e oculta a mensagem até uma versão do SDK enviarsdk_name. - Um
403in-app não refaz o registro. Em um403das rotas in-app, o SDK registra um aviso por sessão e rota, para de buscar mensagens pelo resto da sessão (a próxima sessão em foreground busca de novo) e mantém os eventos em fila, que são reenviados no próximo envio. Ele não refaz o registro como o contrato do servidor sugere. - Sem API de consentimento na 0.1.0. Não há chamada para segurar a coleta até o usuário consentir. Controle você mesmo
Notti.initializeeNotti.login, e chamelogindepois deinitialize: antes disso, ologiné descartado (um aviso). - Dados pessoais no
UserDefaults. E-mail, telefone, ID de usuário externo e tags são armazenados sem criptografia na suítenotti_prefs. - Sem
Retry-After. Uma requisição é tentada até 5 vezes, com esperas de 2, 4, 8 e 16 segundos entre as tentativas. Só erros de rede, 5xx, 408, 429 e (no registro) um 2xx sem um corpo de dispositivo válido são repetidos; os demais 4xx são definitivos. Eventos que esgotam as tentativas continuam em fila e são tentados de novo em um envio posterior. Um headerRetry-Afteré ignorado.
Código-fonte e changelog
- Código-fonte, README e app de exemplo: github.com/zeeplabs/zeep-notti-ios-sdk
- CHANGELOG, incluindo notas de migração a partir do
react-native-notti - Checklist de dispositivo