Como Implementar Tracking de Conversões em Apps iOS Pós-ATT
IDFA com opt-in, AdAttributionKit pós-negação, conversion value, Privacy Manifests e S2S — o mapa híbrido para campanhas Apple sem fingerprinting.
Contexto
ROAS no iOS caiu depois do ATT. Meta e Google mostram menos conversões. O MMP entrega postbacks com 24–48 horas de atraso. Produto abre o Firebase e vê funil in-app normal. Engenharia diz que “o SDK está no ar”.
Não existe botão que devolve IDFA para quem recusou rastreamento. A precisão de campanha volta por arquitetura dual: atribuição determinística só com opt-in; para o resto, postbacks agregados do sistema (SKAdNetwork / AdAttributionKit) mais eventos server-side com dado first-party autenticado.
Este guia é implementação — Swift, Info.plist, validação — para dev e mídia fecharem o mesmo contrato de conversão. O papel do MMP no stack está em Firebase vs AppsFlyer; aqui o foco é o que o app iOS precisa fazer pós-ATT.
O que o ATT realmente corta
Para a Apple, tracking é ligar dados do seu app a dados de outra empresa (outro app, site ou offline) para anúncio direcionado ou medição de mídia. Compartilhar com data broker entra na mesma definição.
Sem consentimento — ou com “Permitir que apps solicitem rastreamento” desligado no iOS — o IDFA vira 00000000-0000-0000-0000-000000000000. A partir daí:
- Proibido usar fingerprinting ou identificador alternativo para contornar ATT.
- Proibido inferir identidade cross-app para atribuição de ads.
O pre-prompt (tela antes do alerta nativo) segue as HIG:
- Botão neutro: “Continuar” ou “Avançar” — não “Permitir”.
- Um botão claro que leva ao diálogo do sistema; sem atalho de cancelamento que desvia a decisão.
- Sem recompensa (desconto, feature extra) em troca de opt-in.
- Sem controles duplos: escolha no ATT não pode ser anulada em tela secundária.
Gate de consentimento antes de inicializar SDK de ads/MMP continua obrigatório também na LGPD — ver telemetria no app. ATT e banner jurídico não são a mesma coisa, mas o fluxo precisa estar alinhado.
Arquitetura híbrida
flowchart TD
launch[AppLaunch] --> attCheck{ATT_autorizado}
attCheck -->|sim| idfaPath[IDFA_MMP_deterministico]
attCheck -->|nao| aakPath[AdAttributionKit_postbacks]
idfaPath --> networks[Redes_e_MMP]
aakPath --> networks
inAppEvent[Evento_in_app] --> s2s[Servidor_first_party]
s2s --> capi[CAPI_Meta_Google_etc]
s2s --> product[Firebase_produto]
Autorizado: o app lê IDFA e o MMP faz atribuição determinística (install, eventos in-app com rede/campanha). É o caminho mais próximo do “pré-ATT” — mas só para a fatia que aceitou.
Negado: atribuição de install passa por postbacks assinados do AdAttributionKit (evolução do SKAdNetwork). Eventos de conversão in-app que as redes precisam para otimização vão por servidor first-party → Conversion API (Meta, Google, etc.) usando identificadores do seu login (e-mail ou telefone hasheados SHA-256 no servidor), nunca IDFA. Sem usuário autenticado, você fica com sinal agregado e delayed — não com atribuição user-level.
Hash de e-mail não é anonimato; é pseudonimização. O desenho compliant combina postback agregado da Apple com S2S mínimo — detalhe em anonimização e privacidade.
Telemetria de produto (Firebase, funil, retenção) segue first-party dentro do app, independente do ATT — desde que não vire proxy de tracking cross-company.
SKAdNetwork → AdAttributionKit
O SKAdNetwork 4.0 mede campanhas app-to-app com janelas fixas da plataforma. Redes registradas usam identificador terminado em .skadnetwork.
O AdAttributionKit (iOS 17.4+, expandido no iOS 18) é a evolução operacional:
- Web-to-app e app-to-app no mesmo framework.
- Re-engagement no iOS 18: clique em app publicadora abre sua app via Universal Link com
AdAttributionKitReengagementOpen; você registra conversão comconversionTypes: [.reengagement]. - Assinaturas de anúncio em JWS (JSON Web Signature), mais compactas que SKAN legado.
- Janelas de atribuição configuráveis no Info.plist (
AdAttributionKitConfigurations): click-through 1–30 dias, view-through 1–7 dias, cooldown anti-duplicata. - Redes novas usam identificador em minúsculas terminado em
.adattributionkit.
Se você ainda só integra SKAN 4.0, planeje migração: campanhas web-to-app e reativação dependem do AAK.
Conversion value e janelas
Postback agregado não manda “usuário X comprou”. Manda conversion value que você mapeia para eventos de negócio. Esse mapa é contrato entre produto, mídia e eng — no espírito de um tracking plan, só que com 6 bits (0–63) no fine value.
Crowd Anonymity: a Apple só libera granularidade se o volume da campanha garante anonimato estatístico (tiers 0–3):
- Tier 0 (volume muito baixo): postback sem conversion value — só sinal mínimo.
- Tiers 1–2: libera coarse value (
low,medium,high). - Tier 3: libera fine value (0–63), publisher item ID e source identifier de quatro dígitos.
Três janelas por install:
- Dias 0–2: fine (0–63) ou coarse.
- Dias 3–7: só coarse.
- Dias 8–35: só coarse.
Cada janela gera postback próprio. Em produção, o envio ocorre 24–48 horas após o fim da janela — delay intencional para impedir correlação temporal click → install → compra.
Use lockPostback: true ao chamar Postback.updateConversionValue quando precisar fechar a janela cedo (ex.: campanha de lançamento com budget curto) em vez de esperar o ciclo natural.
Exemplo de mapa (ajuste ao seu funil):
0— install apenas (default).10— cadastro / conta criada.20— trial iniciado.40— add to cart ou paywall visto.63— purchase / assinatura paga.
Documente isso antes de subir build. Mídia otimiza em cima do que você codificou — não do que “acham” que o app manda. Esse mapa também alimenta AEO/VO nas campanhas de install — ver eventos in-app para campanhas.
Privacy Manifests e SDKs
Desde iOS 17, app e cada SDK precisam de PrivacyInfo.xcprivacy no bundle. O Xcode consolida tudo no Privacy Nutrition Label da App Store.
Pontos que bloqueiam submissão:
- Required Reason APIs (UserDefaults, system boot time, file timestamps, disk space, etc.) exigem código de razão aprovado pela Apple em
NSPrivacyAccessedAPITypes. API restrita sem declaração → rejeição automática no Connect. NSPrivacyTrackingeNSPrivacyTrackingDomainsdevem refletir o que o app/SDK realmente faz — divergência entre manifesto e comportamento é risco de review.- SDKs de MMP e ads devem vir como XCFramework assinado com manifesto próprio; audite dependências transitivas.
Não repita aqui o mapa legal completo de bases LGPD — isso está em LGPD no app. No iOS pós-ATT, manifesto errado mata release antes de qualquer discussão de ROAS.
Implementação em Swift
1. Solicitar ATT e ler IDFA
Dispare só quando .notDetermined. Se já negou ou autorizou, não re-peça o alerta — respeite o estado.
import AppTrackingTransparency
import AdSupport
final class PrivacyConsentManager {
static let shared = PrivacyConsentManager()
func requestTrackingAuthorization(completion: @escaping (ATTrackingManager.AuthorizationStatus) -> Void) {
let status = ATTrackingManager.trackingAuthorizationStatus
guard status == .notDetermined else {
completion(status)
return
}
ATTrackingManager.requestTrackingAuthorization { newStatus in
DispatchQueue.main.async {
if newStatus == .authorized {
_ = ASIdentifierManager.shared().advertisingIdentifier
// Inicialize MMP / ads IDFA-dependentes aqui
}
completion(newStatus)
}
}
}
}
Timing: depois de contexto (onboarding, valor do app), não no cold start antes de qualquer tela.
2. Atualizar conversion value (AdAttributionKit)
No iOS 18+, use Postback.updateConversionValue para install e re-engagement:
import AdAttributionKit
final class ConversionTrackingManager {
static let shared = ConversionTrackingManager()
func recordPurchase() async {
let update = PostbackUpdate(
fineConversionValue: 63,
lockPostback: false,
conversionTypes: [.install]
)
try? await Postback.updateConversionValue(update)
}
func finalizeWindow() async {
let update = PostbackUpdate(
fineConversionValue: 63,
lockPostback: true,
conversionTypes: [.install]
)
try? await Postback.updateConversionValue(update)
}
}
Chame nos pontos do funil mapeados no contrato (cadastro, trial, purchase). Para re-engagement, use conversionTypes: [.reengagement] ao processar Universal Link com AdAttributionKitReengagementOpen.
3. Info.plist
Chaves essenciais:
NSAdvertisingAttributionReportEndpoint— URL HTTPS do seu servidor para receber cópia dos postbacks vencedores.AdAttributionKitConfigurations— janelas de click/view eAttributionCooldown(ex.:install-cooldown-hours: 24).
O endpoint deve responder HTTP 200 e expor .well-known/adattributionkit/report-postback para validação de assinaturas JWS da Apple.
Validar antes de produção
Conecte dispositivo físico, ative Settings > Developer > AdAttributionKit / SKAdNetwork Testing. Postbacks de teste chegam em 5–10 minutos — não confunda com latência real de campanha (24–48 h).
Checklist de QA:
- ATT dispara uma vez, no momento certo; IDFA não vaza em log/analytics sem autorização.
- Conversion values batem com o documento acordado com mídia.
- Endpoint de postback retorna 200 e persiste payload assinado.
- Fluxo S2S não envia IDFA para usuários que negaram ATT.
- MMP recebe e decodifica postbacks SKAN/AAK (configuração no painel do vendor).
- Privacy Manifests de app + SDKs auditados antes do upload.
Checklist
- Pre-prompt HIG-compliant; ATT após contexto, não no splash.
- Mapa fine value (0–63) assinado por produto + mídia + eng.
- Target iOS com AdAttributionKit; re-engagement via Universal Link se campanha de reativação.
NSAdvertisingAttributionReportEndpoint+ rota.well-knownfuncionando.- Audit
PrivacyInfo.xcprivacy(app e dependências). - S2S/CAPI só com first-party autenticado quando ATT negado.
- MMP configurado para SKAN/AAK; KPI de install/ROAS com fonte única (MMP), funil in-app no Firebase — não misturar como se fossem o mesmo número.
- Dev test concluído; stakeholders avisados sobre delay de postback em produção.
Próximo passo
Implementação “com SDK no ar” sem mapa de conversion value, endpoint de postback e fluxo S2S condicionado ao ATT é o motivo pelo qual campanhas iOS parecem cegas mesmo com budget alto.
Se o app já tem MMP + Firebase e os painéis brigam, o trabalho não é outro dashboard — é fechar o contrato de valores, alinhar latência agregada à expectativa de mídia e garantir que nada no código tenta substituir IDFA negado.
Se quiser, partimos do seu stack (target iOS, MMP, eventos de conversão) e fechamos: mapa de fine values, ordem do prompt ATT e o que vai para S2S vs postback.