{
 "grupo": "mobile",
 "descricao": "Apps mobile iOS e Android",
 "itens": [
  {
   "id": "mobile-permissao-em-tempo-de-uso",
   "nome": "Permissão em tempo de uso",
   "receita": "peça no momento em que a função precisa, explique o motivo antes e trate a recusa com alternativa",
   "prova": "negar a permissão deixa o app utilizável"
  },
  {
   "id": "mobile-texto-de-permissao-do-ios",
   "nome": "Texto de permissão do iOS",
   "receita": "escreva as chaves NS...UsageDescription com o motivo real e específico",
   "prova": "a revisão da loja não pede esclarecimento"
  },
  {
   "id": "mobile-primeiro-build-do-ios",
   "nome": "Primeiro build do iOS",
   "receita": "configure o time de assinatura, bundle id único e perfil de provisionamento automático",
   "prova": "xcodebuild gera o .app para o simulador e para o aparelho"
  },
  {
   "id": "mobile-assinar-o-app-android",
   "nome": "Assinar o app Android",
   "receita": "crie uma keystore, guarde fora do repositório, configure o signingConfig e use Play App Signing",
   "prova": "bundleRelease gera o .aab assinado"
  },
  {
   "id": "mobile-versionar-e-numerar-builds",
   "nome": "Versionar e numerar builds",
   "receita": "versão de exibição semântica e build number sempre crescente, definido pelo CI",
   "prova": "a loja aceita o upload sem erro de versão repetida"
  },
  {
   "id": "mobile-enviar-para-o-testflight",
   "nome": "Enviar para o TestFlight",
   "receita": "archive, valide, envie pelo Xcode ou fastlane e convide testadores internos",
   "prova": "o build aparece processado no TestFlight"
  },
  {
   "id": "mobile-publicar-no-google-play",
   "nome": "Publicar no Google Play",
   "receita": "faixa interna, depois fechada, depois produção, com a ficha da loja completa",
   "prova": "a versão aparece na faixa interna e instala"
  },
  {
   "id": "mobile-rejeicao-da-loja-causas-comuns",
   "nome": "Rejeição da loja: causas comuns",
   "receita": "confira login de demonstração, política de privacidade, uso de permissão, conteúdo de usuário e pagamento dentro das regras",
   "prova": "checklist de revisão marcado antes do envio"
  },
  {
   "id": "mobile-navegacao-por-abas-e-pilha",
   "nome": "Navegação por abas e pilha",
   "receita": "3 a 5 abas, pilha dentro de cada uma, botão voltar previsível e deep links",
   "prova": "voltar do detalhe leva à lista sem perder rolagem"
  },
  {
   "id": "mobile-deep-link-e-link-universal",
   "nome": "Deep link e link universal",
   "receita": "associe o domínio (apple-app-site-association, assetlinks.json) e trate a rota no app",
   "prova": "abrir o link no celular vai à tela certa"
  },
  {
   "id": "mobile-notificacao-push",
   "nome": "Notificação push",
   "receita": "registre o token, envie por FCM/APNs, trate permissão e abra a tela certa ao tocar",
   "prova": "o push de teste chega com o app fechado"
  },
  {
   "id": "mobile-armazenar-segredo-no-aparelho",
   "nome": "Armazenar segredo no aparelho",
   "receita": "use Keychain (iOS) e Keystore (Android); nunca SharedPreferences ou AsyncStorage para token",
   "prova": "o token não aparece em texto no backup"
  },
  {
   "id": "mobile-funcionar-offline",
   "nome": "Funcionar offline",
   "receita": "guarde localmente (SQLite/Room/Core Data), fila de sincronização e resolução de conflito",
   "prova": "modo avião: o app abre, grava e sincroniza ao voltar"
  },
  {
   "id": "mobile-sincronizacao-com-conflito",
   "nome": "Sincronização com conflito",
   "receita": "versão por registro, última escrita ou mesclagem por campo, e dado do servidor como verdade",
   "prova": "duas edições offline resolvem sem perder dado"
  },
  {
   "id": "mobile-lista-rapida-no-mobile",
   "nome": "Lista rápida no mobile",
   "receita": "célula reciclada (FlatList/RecyclerView/LazyColumn), chave estável e imagem em tamanho final",
   "prova": "rolagem a 60 fps com 5 mil itens"
  },
  {
   "id": "mobile-imagem-leve",
   "nome": "Imagem leve",
   "receita": "peça do servidor o tamanho exato, use cache em disco e formato WebP/AVIF",
   "prova": "o consumo de memória e de dados cai"
  },
  {
   "id": "mobile-reduzir-o-tamanho-do-app",
   "nome": "Reduzir o tamanho do app",
   "receita": "ative minificação e App Thinning/App Bundle, remova recursos e bibliotecas sem uso",
   "prova": "o tamanho do pacote cai em MB"
  },
  {
   "id": "mobile-tempo-de-abertura",
   "nome": "Tempo de abertura",
   "receita": "adie inicializações pesadas para depois da primeira tela e meça a partida fria",
   "prova": "a primeira tela aparece abaixo de 2 segundos"
  },
  {
   "id": "mobile-bateria-e-rede",
   "nome": "Bateria e rede",
   "receita": "agrupe requisições, evite polling, use tarefas em segundo plano nativas",
   "prova": "o perfil de energia mostra menos despertares"
  },
  {
   "id": "mobile-tarefa-em-segundo-plano-no-aparelho",
   "nome": "Tarefa em segundo plano no aparelho",
   "receita": "use BackgroundTasks (iOS) e WorkManager (Android) com restrições, nunca laço próprio",
   "prova": "a tarefa executa com o app fechado"
  },
  {
   "id": "mobile-teclado-e-formulario",
   "nome": "Teclado e formulário",
   "receita": "tipo de teclado certo, autofill, próximo campo no Enter e rolagem para o campo ativo",
   "prova": "o teclado nunca cobre o campo em foco"
  },
  {
   "id": "mobile-area-segura-e-entalhe",
   "nome": "Área segura e entalhe",
   "receita": "use safe area insets em topo e base; teste em aparelhos com notch e ilha dinâmica",
   "prova": "nenhum elemento cortado em iPhone SE e Pro Max"
  },
  {
   "id": "mobile-tema-escuro-no-app-nativo",
   "nome": "Tema escuro no app nativo",
   "receita": "cores semânticas por tema, imagens e ícones que se adaptam, e teste dos dois",
   "prova": "nenhuma tela ilegível no escuro"
  },
  {
   "id": "mobile-acessibilidade-no-mobile",
   "nome": "Acessibilidade no mobile",
   "receita": "rótulos para VoiceOver/TalkBack, ordem de foco, alvo de 44 pt e Dynamic Type",
   "prova": "navegar a tela com leitor de tela faz sentido"
  },
  {
   "id": "mobile-biometria",
   "nome": "Biometria",
   "receita": "LocalAuthentication/BiometricPrompt como segunda etapa, com alternativa de senha",
   "prova": "a digital destrava e a recusa cai no PIN"
  },
  {
   "id": "mobile-compra-dentro-do-app",
   "nome": "Compra dentro do app",
   "receita": "use StoreKit/Billing, valide o recibo no servidor e trate restauração",
   "prova": "a compra de sandbox libera o recurso e restaura em outro aparelho"
  },
  {
   "id": "mobile-crash-reporting",
   "nome": "Crash reporting",
   "receita": "integre Crashlytics ou Sentry com símbolos (dSYM/mapping) enviados",
   "prova": "um crash de teste aparece legível com a linha"
  },
  {
   "id": "mobile-testar-no-aparelho-de-verdade",
   "nome": "Testar no aparelho de verdade",
   "receita": "teste em um modelo antigo e um novo, com rede ruim, antes de enviar",
   "prova": "checklist rodado em 2 aparelhos reais"
  },
  {
   "id": "mobile-atualizacao-forcada",
   "nome": "Atualização forçada",
   "receita": "o servidor informa a versão mínima; o app mostra tela de atualizar com link da loja",
   "prova": "versão antiga é barrada com mensagem clara"
  },
  {
   "id": "mobile-fastlane-para-entregar",
   "nome": "Fastlane para entregar",
   "receita": "lanes para testar, assinar e enviar com variáveis seguras no CI",
   "prova": "um comando gera e envia o build"
  },
  {
   "id": "mobile-compartilhar-e-abrir-arquivo",
   "nome": "Compartilhar e abrir arquivo",
   "receita": "share sheet/Intent para enviar e Document Picker para receber, com tipos declarados",
   "prova": "enviar um PDF do app abre no outro aplicativo"
  }
 ]
}
