Construindo um prompt de avaliação que eu tinha certeza que não ia renderizar no simulador
O Deep Cut Atlas precisava de um prompt de avaliação da App Store em algum lugar, e a regra óbvia é: pergunte depois que a pessoa teve uma vitória de verdade, não no lançamento do app. Escolhi “três adições de álbum bem-sucedidas” como sinal: o suficiente para significar que o app realmente cumpriu seu papel pelo menos uma vez, mas não tanto a ponto do pedido parecer insistência.
A lógica do gate em si não tem nada de especial: um contador em SettingsStore, incrementado a cada adição bem-sucedida nas três abas que podem disparar uma (Discover, History, e as sugestões “More from artist” da Playlist), uma checagem de limiar, e uma flag por versão do app para disparar só uma vez até o próximo bump de versão. O @Environment(\.requestReview) nativo do SwiftUI cuida da sheet do sistema de verdade: você não constrói nenhuma UI para isso, só chama no momento certo.
Antes de começar, eu já presumia que teria que forjar a verificação inteira: o SKStoreReviewController e seu wrapper em SwiftUI são conhecidos por serem pouco confiáveis para disparar até mesmo num dispositivo real, limitados pelo próprio throttling anual por app da Apple, e eu já tinha lido mais de uma vez que ele simplesmente vira um no-op silencioso no simulador. Esperava verificar a lógica do gate com testes unitários e só confiar na única linha que chama requestReview().
Renderizou mesmo assim. Três adições reais pela UI do simulador, e a sheet de avaliação real do sistema apareceu, na primeira tentativa, sem precisar de nenhum entitlement especial ou flag de debug. Não era o que eu esperava entrando nisso, e mudou como verifiquei o resto da feature: em vez de confiar cegamente na chamada, pude tirar print da sheet real, confirmar que não havia novo prompt num relaunch ou numa quarta adição, e tratar o fluxo inteiro como realmente comprovado, em vez de “provavelmente está bom”.
A passada no dispositivo depois disso foi basicamente uma formalidade nesse ponto: responsividade de toque nas adições, sem travamentos, mesmo comportamento do simulador. A descoberta interessante aqui não foi a feature, foi que uma crença recebida sobre como essa API se comporta nas ferramentas de desenvolvimento se provou errada, e eu teria lançado com uma verificação mais fraca se não tivesse simplesmente testado.
Leitura relacionada
Lançando o Deep Cut Atlas: o que quebrou na minha primeira submissão à App Store
Um consumível que deveria ter sido não consumível, um product ID que você nunca pode reutilizar, um archive que silenciosamente não foi rebuildado, e um rename com um pavio de duas semanas.
Uma seção de tracklist, e por que levou 30 minutos
Um método de protocolo, um enum de estado reaproveitado, uma convenção de roteamento que se manteve, e um orçamento de lint que forçou uma divisão que valia a pena fazer de qualquer forma.
Um relato de bug de filtro que virou três issues separadas
O filtro relatado funcionava bem - o problema real era outro filtro, deliberadamente independente, e o fix certo foi uma terceira opção nunca cogitada.
Você também pode achar útil
Proton VPN
VPN comercial com filtragem NetShield e interruptor de desligamento automático.
Como parceiro da Proton, ganho com compras qualificadas dos serviços de privacidade e segurança da Proton (Pass, Mail, VPN, Drive).
Saiba maiseSIM Airalo
eSIM de dados local para viagens - sem necessidade de trocar um SIM físico.
Este é meu link de indicação da Airalo. Você recebe um desconto no seu primeiro eSIM e eu ganho crédito da Airalo para o meu.
Saiba maisAdGuard para iOS
Bloqueio de anúncios e rastreadores em todo o sistema no iOS, sem necessidade de um servidor DNS separado.
Como afiliado da AdGuard, ganho com compras qualificadas.
Saiba mais