Aula 29 · Semana 12 · Terça, 24 de novembro de 2026 · 08:30 às 10:30
Lojas e monetização, e sprint 3
Teoria (30 minutos): modelos de negócio em mobile, compras in-app e subscrições, regras das lojas, publicidade, motivos comuns de rejeição e ASO. Prática: sprint 3, com revisão da app do grupo contra as guidelines da App Store.
Review da sprint 3: quarta 02/12 às 16:30
No fim desta aula consegues
Distinguir os cinco modelos de negócio de uma app e dizer o que cada um exige na arquitetura.
Explicar quando uma compra tem de passar pelo StoreKit ou pelo Play Billing, quanto custa em comissões e o que o DMA mudou na UE.
Avaliar o custo da publicidade para a experiência do utilizador e para a privacidade.
Reconhecer os motivos mais comuns de rejeição nas lojas e evitá-los antes de submeter.
Pedir uma avaliação no momento certo com in_app_review e preparar a ficha da app para ser encontrada.
Modelos de negócio
Modelo
Exemplos
O que muda na arquitetura
Pago (compra única)
Minecraft, Procreate, Things
Nada no código: a loja cobra antes de instalar. Cada vez mais raro fora de jogos e ferramentas.
Freemium
Duolingo, Windy, Spotify
Entitlements (a app sabe quem tem o quê), paywall, compras in-app, botão de restaurar compras.
Subscrição
Carrot Weather, Headspace, YouTube Premium
Tudo o do freemium mais o ciclo de vida: trial, renovação, falha de cobrança, cancelamento; validação no servidor.
Publicidade
AccuWeather, Instagram, jogos casuais
SDK de anúncios, consentimento, ATT, platform views; a receita depende de volume.
Empresa ou interna
App de estafetas, app de um banco para os balcões
Distribuição fora da loja pública (Apple Business Manager, managed Google Play), login corporativo, sem monetização direta.
Os modelos misturam-se: o Duolingo é gratuito, mostra anúncios e vende a subscrição que os tira. Cada modelo é um ecrã a mais e uma regra da loja a mais.
Uma app de meteorologia dos Açores com dados públicos tende para gratuita, ou freemium com um extra que valha a fricção (alertas personalizados, previsão a 10 dias). O que se cobra tem de valer mais do que o incómodo de pagar.
Para os testes: o modelo de negócio decide a arquitetura: freemium e subscrição exigem entitlements e restauro de compras, publicidade exige consentimento, apps de empresa distribuem-se fora da loja pública.
Compras in-app e subscrições
Três tipos de produto: consumível (moedas, créditos), não consumível (remover anúncios, versão Pro, comprado uma vez para sempre) e subscrição auto-renovável (mensal ou anual, com trial). Definem-se no App Store Connect e na Play Console, com preço por escalão e nome localizado, não no código.
StoreKit 2 (iOS) e Play Billing Library (Android) são as APIs nativas. No Flutter: in_app_purchase, o plugin oficial que fala com as duas, ou RevenueCat (purchases_flutter), um serviço que valida recibos, gere entitlements e envia webhooks; grátis até um certo volume de receita.
Validação no servidor: o recibo (Apple) ou o purchase token (Google) verifica-se com a App Store Server API ou a Play Developer API. Um telemóvel com root falsifica uma compra em minutos; só o servidor sabe se é real.
Ciclo de vida de uma subscrição: trial, renovação, falha de cobrança com período de tolerância, cancelamento, reembolso. O servidor recebe notificações (App Store Server Notifications, Real-time Developer Notifications) e atualiza o entitlement; a app só pergunta "tem acesso?".
Restaurar compras é obrigatório na Apple: um botão visível que repõe o que o utilizador já comprou noutro telemóvel. Sem ele, rejeição pela guideline 3.1.1.
A regra: conteúdo ou funcionalidade digital consumida na app passa pelo sistema de compras da loja. Bens físicos e serviços fora da app (Uber, Bolt, Zara, uma reserva de restaurante) usam o processador que quiserem: Stripe, MB WAY, cartão.
Para os testes: conteúdo digital consumido na app passa pela loja, bens e serviços físicos não; restaurar compras é obrigatório e a validação é no servidor.
Comissões e o DMA na UE
App Store
Google Play
Comissão padrão
30 %
30 %
Pequenos programadores
15 % com o Small Business Program (até 1 M$ por ano)
15 % no primeiro milhão de dólares de cada ano
Subscrições
15 % a partir do segundo ano de cada subscritor
15 % desde o primeiro dia
Pagamentos alternativos na UE
Permitidos desde o iOS 17.4 (2024), com termos próprios da Apple e taxas adicionais
User choice billing: o utilizador escolhe e a comissão baixa cerca de 4 pontos
Lojas alternativas
Só na UE (AltStore PAL, Epic Games Store), com notarização da Apple
Sempre existiram: sideloading, Galaxy Store, F-Droid
O DMA (Digital Markets Act, em vigor desde março de 2024) classificou a App Store e a Play como gatekeepers: têm de permitir pagamentos alternativos e, no iOS, lojas alternativas. As condições concretas (taxas, avisos ao utilizador) mudam quase todos os anos e continuam em disputa com a Comissão Europeia.
Epic vs Apple (2020): o Fortnite pôs um pagamento direto dentro da app e foi removido em horas. Só em 2025 voltou à App Store nos EUA, depois de cinco anos de tribunais. A regra continua a ser da loja, não tua.
Para um projeto pequeno: sistema da loja, os 15 %, e nada de pagamentos alternativos sem um contabilista ao lado.
Para os testes: 30 % é a comissão padrão, 15 % para pequenos programadores e subscrições; na UE o DMA obriga a alternativas, mas com condições próprias de cada loja.
Publicidade
AdMob (Google) é a rede dominante em mobile; no Flutter usa-se google_mobile_ads. Formatos: banner, interstitial (ecrã inteiro entre duas ações), rewarded (o utilizador vê 30 segundos e recebe algo) e native (imita o conteúdo da app). Mediação para leiloar cada impressão entre várias redes.
Receita: o eCPM, receita por mil impressões, vai de cêntimos a poucos euros conforme o país e o formato. São precisos dezenas de milhares de utilizadores ativos para os anúncios pagarem um café por dia. Uma app de nicho vende antes uma subscrição.
Experiência: um interstitial ao arrancar ou a meio de um gesto é o primeiro motivo de reviews de uma estrela nas apps de meteorologia. O rewarded é o único formato que o utilizador escolhe ver. As guidelines proíbem anúncios que imitam botões do sistema ou aparecem fora da app.
Privacidade: anúncios personalizados dependem do IDFA (iOS) e do GAID (Android), logo do ATT (aula 18) e de consentimento RGPD. No Espaço Económico Europeu a Google exige uma plataforma de consentimento certificada (o UMP SDK serve). Sem consentimento há anúncios, mas contextuais, e pagam menos.
Técnica: cada anúncio é uma platform view (aula 28): custa memória e frames. Carrega o interstitial antes de precisares dele e nunca bloqueies a UI à espera de um anúncio.
Para os testes: a publicidade paga por volume, degrada a experiência quando é intrusiva e exige consentimento (ATT e RGPD) para ser personalizada.
Review guidelines e rejeições comuns
Guideline (Apple)
O que o revisor encontra
Como evitar
2.1 Completude
Crash ao abrir, botão que não faz nada, "em breve", login que não funciona
Testar num telemóvel limpo; conta de demonstração nas review notes; zero placeholders
2.3 Metadata
Screenshots de outra versão, descrição que fala de Android, funcionalidades que não existem
Screenshots do build submetido; descrever só o que existe
3.1.1 Pagamentos
Link para pagar no site, "mais barato em tempoacores.pt"
Conteúdo digital só pela loja; nenhuma referência a preços externos
4.2 Funcionalidade mínima
WebView à volta do site, app que só mostra uma lista estática
Funcionalidade nativa que o site não tem: offline, notificações, mapa, widgets
5.1.1 Privacidade
Login obrigatório sem funcionalidade de conta, permissão sem justificação, dados a mais
Pedir só o que precisas, quando precisas, com texto concreto; labels a bater certo com a app
A Apple revê tudo, com pessoas e automatismos, quase sempre em 24 a 48 horas; a Google é sobretudo automática e demora de horas a dias. Nas duas, a rejeição cita o número da regra.
Google Play, os mais comuns: data safety form em desacordo com o tráfego real, target API level abaixo do exigido, permissões sensíveis sem formulário de declaração (localização em background, QUERY_ALL_PACKAGES) e "comportamento enganoso" (funcionalidade escondida ou diferente da descrita).
Para os testes: as rejeições mais comuns são crashes e funcionalidade incompleta (2.1), metadata enganosa (2.3), pagamento externo (3.1.1), funcionalidade mínima (4.2) e privacidade (5.1.1).
Analogia A loja é ao mesmo tempo a alfândega e a montra. A alfândega (a revisão) verifica as regras antes de deixar a app entrar; a montra (a ficha, os screenshots, o ASO do slide seguinte) decide se alguém repara nela. A Apple tem uma alfândega com inspeção humana a cada versão; a Google inspeciona sobretudo por máquina e é mais rápida, mas exige aos vendedores novos um período de teste fechado antes da primeira venda.
ASO e pedir avaliação no momento certo
ASO (App Store Optimization) é o SEO das lojas. Nome com 30 caracteres nas duas; subtítulo (iOS) e descrição curta (Play) com o benefício; campo de keywords só no iOS (100 caracteres, invisível); a descrição longa conta para a pesquisa na Play, não no iOS. "Tempo Açores: previsão e avisos" vale mais do que "Tempo Açores".
Screenshots: as três primeiras decidem a instalação. Por dispositivo e por idioma, com uma frase de benefício em cima, do build atual. Ícone simples e legível a 48 px.
Ratings pesam no ranking e na conversão; a Play dá mais peso às recentes. Responder às reviews em público conta, e mostra a quem lê que há alguém do outro lado.
in_app_review: requestReview() chama o SKStoreReviewController no iOS e a In-App Review API na Play. O sistema decide se o pedido aparece: a Apple mostra-o no máximo 3 vezes por ano por app; a Play tem uma quota não publicada. Verifica isAvailable() antes; openStoreListing() serve para um botão "Avaliar" nas definições.
O momento certo é a seguir a um sucesso: terceira previsão consultada, aviso recebido a tempo, favorito sincronizado. Nunca no arranque, nunca depois de um erro, nunca a interromper um gesto. As guidelines proíbem prompts personalizados e o padrão "gostas? então avalia" que só deixa passar quem gosta.
final review = InAppReview.instance;
if (previsoesConsultadas == 3 && await review.isAvailable()) {
await review.requestReview(); // o SO pode decidir não mostrar nada
}
Para os testes: pede-se avaliação com a API do sistema, depois de um sucesso e nunca no arranque; o sistema decide se o pedido aparece.
Sprint 3: ponto de situação
Cada grupo, um minuto: o que está em main, o que está em PR, o que fica de fora e já está escrito no README.
A review é na quarta 02/12 às 16:30, na aula 32. Até lá há três aulas: amanhã (tamanho e arranque), segunda (build e assinatura) e a própria review. O que não estiver em main até terça 01/12 à noite não entra na demo.
Pronto na sprint 3, como ficou na aula 26: instalável por um tester fora do grupo, README com screenshots, CI verde; e desde ontem, API v2 com testes verdes nas duas versões.
Prioridade: os mínimos primeiro, um extra no máximo. Um PR pequeno merged vale mais do que uma funcionalidade a meio.
O docente circula pelos grupos. Bloqueios resolvem-se ao vivo, no vosso código.
Calendário apertado Depois da review de quarta há uma semana até ao freeze de 09/12, com o teste 2 pelo meio, na segunda 07/12. O polish e o README não podem ficar para a semana do freeze: entram nesta sprint.
Exercício: a vossa app contra as guidelines
Quinze minutos, por grupo, com a app instalada num telemóvel (não no emulador) e a ficha da loja imaginada como se a submissão fosse amanhã. Cada ponto indica a guideline da App Store em causa.
Arranca e chega ao ecrã principal sem crash num telemóvel acabado de instalar, com rede e em modo avião (2.1).
Nenhum ecrã "em construção", nenhum TODO visível, nenhum botão que não faz nada, nenhuma string em inglês por traduzir (2.1).
Ícone e nome próprios: nada do ícone por defeito do Flutter nem de tempo_acores com underscore por baixo do ícone (2.3).
Os screenshots que fariam da app mostram exatamente o que ela faz hoje; a descrição não fala de Android nem de "em breve" (2.3).
Faz mais do que o site do IPMA faria num browser: cache offline, notificações de avisos, mapa, favoritos (4.2).
Ver previsões não exige conta nem dados pessoais; se houver conta, existe uma funcionalidade que a justifique (5.1.1).
Cada permissão tem uma justificação concreta no Info.plist ("Para mostrar a previsão da localidade mais próxima"), é pedida no momento em que é precisa, e a app continua a funcionar se for recusada (5.1.1).
Conseguem listar tudo o que a app envia para fora (client-id e headers para a API do curso, telemetria, crash reports) para as privacy labels baterem certo (5.1.2).
Nenhum link, texto ou preço a apontar para pagamentos fora da loja; se o extra do grupo for pago, é por compra in-app (3.1.1).
Atribuição do OpenStreetMap visível no mapa e a fonte dos dados meteorológicos identificada nas definições (5.2).
Exercício Percorram a lista na app do grupo e escrevam numa issue os três motivos mais prováveis de rejeição, cada um com o número da guideline, o que o revisor veria e o que vão fazer. O mais grave dos três entra hoje no backlog da sprint 3 com um responsável; os outros dois ficam na issue, ou no README como limitação conhecida se decidirem não os corrigir. Quem acabar antes: NSLocationWhenInUseUsageDescription e as justificações de notificações reescritas em português concreto, num PR de cinco minutos.
Da issue de hoje à review de quarta
O motivo escolhido tem de estar fechado antes da review: é um PR como os outros, revisto por um colega, com CI verde.
As review notes que escreveriam já hoje: como chegar a cada ecrã, que localidade escolher, que a API é do curso e onde estão os dados. Guardem-nas no README, secção "Para o revisor". Na aula 32 vão precisar delas.
Se o vosso extra é pedir avaliação, o momento certo já está decidido: a seguir a um sucesso, com um contador, nunca no arranque.
Amanhã medimos o tamanho da app com flutter build apk --analyze-size: tragam o projeto a compilar em release e um portátil com espaço em disco.
Regra do revisor O revisor da Apple passa uns minutos com a vossa app e nunca leu o vosso código. Tudo o que precisar de explicação já vem tarde: se um ecrã só faz sentido com contexto, o contexto vai nas review notes ou o ecrã sai.
Até amanhã
Para fazer
A sprint 3 continua: o motivo de rejeição escolhido hoje está numa issue com responsável, e a migração para a API v2 está merged em todos os grupos.
PRs abertos revistos por um colega até amanhã; nada entra em main sem revisão e sem CI verde.
Para amanhã: o projeto compila em release (flutter build apk --release sem erros) num portátil do grupo.