PDM · DAW 2

Aula 33 · Semana 13 · Sexta, 4 de dezembro de 2026 · 08:30 às 12:30

Revisão para o teste 2 e workshop de finalização

Primeira parte: revisão da matéria das aulas 21 a 32, com dez perguntas-tipo resolvidas em conjunto e os erros mais frequentes. Segunda parte: finalização do projeto: checklist de entrega, correções, README, build final assinado e upload aos testers. O teste é na segunda-feira seguinte; o freeze na quarta.

No fim desta aula consegues

Mapa da matéria (1 de 2): aulas 21 a 26

AulaTemaA ideia que o teste pergunta
21Localização e mapasPrecisão vs bateria; when-in-use vs always; negação permanente; OSM grátis vs Google Maps com chave e custos.
22Deep linksCustom scheme vs Universal/App Links; verificação de domínio com AASA e assetlinks; o router decide o ecrã.
23BackgroundO SO limita por bateria; WorkManager com restrições e sem hora exata; iOS sem garantias; silent push.
24Hardware e sensoresCada sensor tem permissão, consumo e limites por plataforma; NFC mais restrito no iOS; BLE precisa de permissões de scan.
25 e 26Testes, CI e observabilidadePirâmide; fakes em vez de rede; CI como definição de pronto; crash-free, ANR, feature flags e kill switch.

Mapa da matéria (2 de 2): aulas 27 a 32

AulaTemaA ideia que o teste pergunta
27Distribuição a testersApp Distribution sem revisão; TestFlight externo com revisão beta; internal testing até 100; nunca distribuir debug.
28Platform channels e APIsMethodChannel e tipos do codec; avaliar um package; versionar uma API sem partir apps instaladas.
29Lojas e monetizaçãoIAP obrigatório para conteúdo digital; motivos de rejeição; ASO e pedir avaliação no momento certo.
30Tamanho, arranque e atualizaçõesAAB e split por ABI; medir antes do primeiro frame; code push com limites; forced update.
31 e 32Build, assinatura e consolasAssinatura prova autoria e integridade; versionCode nunca desce; tracks, rollout gradual, data safety e privacy labels, vitals.

Como responder a escolha múltipla

Nos próximos dez slides o formato é o do teste. Votas, e só depois vemos a resposta.

Pergunta 1 · Localização

A Tempo Açores quer abrir na localidade mais próxima do utilizador quando ele abre a app. Que permissão deve pedir?

  1. Localização always, para funcionar também em segundo plano.
  2. Localização when in use, com precisão aproximada a chegar.
  3. Localização em segundo plano com um foreground service para não falhar.
  4. Nenhuma: as coordenadas do GPS são públicas e não precisam de permissão.

Pergunta 2 · Deep links

Porque é que um link de "login mágico" enviado por e-mail não deve usar um custom scheme como tempoacores://login?token=...?

  1. Porque os custom schemes não funcionam no Android.
  2. Porque qualquer app instalada pode registar o mesmo scheme e receber o link com o token.
  3. Porque o iOS só permite schemes registados na App Store.
  4. Porque os custom schemes exigem HTTPS e um certificado.

Pergunta 3 · Background

Configuras o WorkManager para atualizar a cache de previsões de 6 em 6 horas. O que o Android garante?

  1. Que a tarefa corre exatamente de 6 em 6 horas, mesmo em Doze.
  2. Que a tarefa corre pelo menos uma vez em cada período, quando as restrições permitirem, e sobrevive a reinícios.
  3. Que a tarefa corre só enquanto a app estiver em primeiro plano.
  4. Que a mesma configuração funciona no iOS com a mesma garantia.

Pergunta 4 · Hardware

Uma app quer fazer scan de dispositivos Bluetooth Low Energy num Android atual. O que precisa?

  1. Nada: o scan BLE é livre porque não identifica o utilizador.
  2. Só a permissão de NFC, porque BLE e NFC partilham o rádio.
  3. A permissão BLUETOOTH_SCAN (Android 12 e posteriores) e, em versões anteriores, a permissão de localização.
  4. A permissão de câmara, porque o scan usa o leitor de códigos.

Pergunta 5 · Testes

Na pirâmide de testes há muitos testes unitários e poucos de integração ou E2E. Porquê?

  1. Porque os unitários são rápidos, baratos e apontam diretamente a causa da falha.
  2. Porque o Flutter não suporta testes de integração.
  3. Porque os unitários testam a interface e os E2E só testam a lógica.
  4. Porque os golden tests substituem os testes E2E.

Pergunta 6 · Observabilidade

O Crashlytics mostra "crash-free users: 99,2 %" na última semana. O que isto significa?

  1. Que 0,8 % das sessões terminaram num crash.
  2. Que 99,2 % dos crashes já foram corrigidos.
  3. Que 0,8 % dos utilizadores tiveram pelo menos um crash nesse período.
  4. Que a app tem 0,8 % de código com bugs.

Pergunta 7 · Distribuição a testers

Qual é a diferença essencial entre distribuir um build a testers externos pelo TestFlight e pelo Firebase App Distribution?

  1. O Firebase App Distribution só funciona para iOS.
  2. O TestFlight externo passa por uma revisão beta da Apple; o Firebase entrega o ficheiro sem revisão.
  3. O TestFlight não tem limite de testers; o Firebase está limitado a 25.
  4. Não há diferença: ambos instalam através da App Store.

Pergunta 8 · Platform channels

Que tipos podes enviar diretamente por um MethodChannel entre Dart e Kotlin ou Swift?

  1. Qualquer objeto Dart, porque o canal serializa automaticamente com reflexão.
  2. Só String; tudo o resto tem de ir em JSON.
  3. Primitivos, String, listas e mapas suportados pelo StandardMessageCodec; objetos próprios convertem-se em mapas.
  4. Widgets, para que o lado nativo os possa desenhar.

Pergunta 9 · Lojas e monetização

A Tempo Açores passa a vender uma subscrição "Previsão a 10 dias" dentro da app iOS. O que a Apple exige?

  1. Nada: a app pode aceitar cartão com qualquer fornecedor de pagamentos.
  2. Que a compra de conteúdo digital use o sistema de compras in-app da Apple, com a comissão respetiva.
  3. Que a subscrição seja vendida só no site e a app nunca a mencione.
  4. Que a app seja paga na loja em vez de ter subscrição.

Pergunta 10 · Build e versionamento

Carregaste um app bundle com versionCode 7 para o track de teste interno. Corriges um bug e queres carregar para produção. O que o novo bundle precisa?

  1. Nada: os tracks são independentes e o 7 pode repetir-se.
  2. versionCode 7 e um versionName diferente.
  3. versionCode 8 ou superior; o versionName pode ser o mesmo.
  4. Um keystore novo, porque a chave de teste não serve para produção.

Erros frequentes no teste

O erroPorque está errado
Pedir localização always "para garantir"A app não precisa, as lojas rejeitam sem justificação e o utilizador nega. When in use chega para quase tudo.
Tratar um custom scheme como seguroNão tem dono nem verificação de domínio. Para tokens e login, Universal/App Links.
Contar com o WorkManager a uma hora certaGarante execução dentro de restrições, não hora. O SO junta e adia trabalho por bateria.
Achar que code push muda tudo sem lojaSó código Dart; nada de nativo, assets ou permissões. E a Apple exige que não mude o propósito da app.
Reutilizar o versionCodeA Play recusa o upload e o telemóvel recusa o downgrade. Sobe sempre; o versionName é que pode repetir.

Para os testes: quando uma opção promete uma garantia absoluta (sempre, exatamente, sem limites) num sistema móvel, desconfia.

Workshop de finalização: checklist de entrega

Como vai correr 10:35 a 10:50: cada grupo percorre a checklist e marca o que falta na milestone. 10:50 a 12:00: trabalho, com o docente a rodar. 12:00 a 12:30: uploads e verificação cruzada com outro grupo.

README e documentação: o que tem de lá estar

  1. Cabeçalho: nome da app, os membros com o utilizador GitHub, e um screenshot ou GIF da app a correr no telemóvel.
  2. Como correr: versão do Flutter, flutter pub get, o que pôr no --dart-define ou no env/dev.json, e como obter o client-id (não o próprio segredo).
  3. Arquitetura em dez linhas: camadas, pastas por feature, onde vive o estado, como a cache funciona. Um diagrama simples vale mais do que dez parágrafos.
  4. O que está feito e o que não está: os mínimos, os extras, a milestone "Depois". Honestidade aqui é avaliada; esconder um bug conhecido não é.
  5. Como obter o build: link de convite do App Distribution, versão atual, e quem tem o keystore.
  6. Retrospetiva: as duas frases da aula 32.
Exercício Troca o README com outro grupo. Sem falar com eles, tenta correr a app só com o que lá está escrito. Cada coisa que tiveste de perguntar é uma linha que falta.

Build final assinado e upload ao App Distribution

  1. Sobe o build number no pubspec.yaml: version: 1.0.0+N, com N maior do que o último que distribuíste.
  2. Gera o APK de release com o flavor prod e a configuração de prod.
    flutter build apk --release --flavor prod --dart-define-from-file=env/prod.json
  3. Faz o upload com o CLI do Firebase, para o grupo de testers da turma que criaste na aula 27.
    firebase appdistribution:distribute \
      build/app/outputs/flutter-apk/app-prod-release.apk \
      --app 1:123456789012:android:0123456789abcdef \
      --groups turma-pdm \
      --release-notes "v1.0.0 (N): build final para a defesa"
  4. Confirma no telemóvel de outro grupo que a notificação do App Distribution chegou e que a app instala por cima da anterior, sem desinstalar.
Exercício Build distribuído a pelo menos 3 testers fora do grupo, com o link do release colado no README. Se a instalação por cima falhar, a causa está na aula 31: chave ou versionCode.

TestFlight, para quem tem iOS

  1. Conta paga e Mac. O docente faz o upload com a conta dele para os grupos que tenham a app a compilar em iOS; o bundle id tem de ser o combinado com ele.
  2. Gera o ficheiro .ipa e abre-o no Xcode ou no Transporter.
    flutter build ipa --release --flavor prod --dart-define-from-file=env/prod.json
    open build/ios/archive/Runner.xcarchive
  3. Distribute App → App Store Connect → Upload. O build aparece em "Processing" durante alguns minutos.
  4. Testers internos não precisam de revisão: adiciona os Apple IDs dos colegas ao grupo de teste interno, e eles recebem o convite na app TestFlight.
  5. Sem Mac: não há caminho. Fica no README que o build iOS não foi distribuído e porquê; não é penalizado.

Até à próxima aula

Para fazer

  • Estudar a componente teórica das aulas 21 a 32. O teste é segunda, 07/12, às 16:00, em papel e sem consulta.
  • Fechar o que ficou por marcar na checklist de hoje. O freeze é quarta às 18:30, com tag v1.0.0.
  • Confirmar que os 3 testers fora do grupo instalaram mesmo o build.

Para ler

  • Request location permissions (developer.android.com): foreground vs background, precise vs approximate, com as regras por versão (aula 21).
  • Deep linking (docs.flutter.dev): custom schemes, App Links e Universal Links, e a verificação de domínio com assetlinks.json e AASA (aula 22).
  • WorkManager (developer.android.com): o que o sistema garante e o que não garante a uma tarefa em background (aula 23).
  • Build and release an Android app (docs.flutter.dev): assinatura, app bundle e versionamento; revê os passos se algum upload falhou hoje (aula 31).
  • App Store Review Guidelines: a 2.1, a 3.1.1, a 4.2 e a 5.1.1, que são as que mais rejeitam (aulas 29 e 32).