O app que eu desenhei e construí, mas nunca lancei: Stocketa
(paulstamatiou.com)- No início do isolamento da COVID, em março de 2020, o Stocketa começou como um projeto pessoal para aprender Swift e SwiftUI e foi desenvolvido por mais de 2 anos, com 10 a 20 horas por semana, mas nunca foi lançado na App Store
- O objetivo era ser um rastreador de portfólio para investidores casuais que reunisse em um só lugar ativos espalhados por várias corretoras e serviços financeiros, mostrando também lucro e prejuízo por operação, além de ganhos realizados e não realizados
- O app focava mais em interações feitas do zero do que em componentes padrão do iOS, implementando pull-to-search, cartões de ações, sheets e menus customizados, ações de swipe, widgets e importação de TSV
- A maior limitação prática que impediu o lançamento foi a API de dados financeiros: havia problemas de qualidade e cobertura na IEX, dúvidas sobre a confiabilidade de um provedor de US$ 100/mês e preços de dados comerciais a partir de US$ 2.000/mês que não faziam sentido para um app independente
- Embora já tivesse LLC, backend em Node/Express, cerca de 1.000 pessoas no TestFlight e milhares na lista de espera, o projeto foi encerrado por causa do custo dos dados, da carga de suporte ao cliente e da falta de tempo; ainda assim, a experiência com SwiftUI foi aplicada diretamente no trabalho posterior com a Rewind AI
O problema que o Stocketa queria resolver
- O Stocketa tinha como objetivo ser um app de acompanhamento de portfólio bem projetado para ver, em uma única tela, ativos de investimento espalhados por vários lugares
- A ideia veio da experiência pessoal de ter dificuldade para acompanhar ativos distribuídos entre várias corretoras e serviços financeiros
- Não era apenas sobre ver o preço atual, mas sobre acompanhar o lucro e prejuízo reais por operação com base no próprio custo médio
- Na época, as opções disponíveis não atendiam ao que eu queria
- Apps simples de ações, como o Apple Stocks, oferecem gráficos e notícias, mas não acompanham quantidade em carteira nem lucro e prejuízo
- Apps de corretoras, como Robinhood, mostram apenas os ativos mantidos dentro daquela própria corretora
- Serviços mais avançados, como TradingView, não eram focados em mobile ou não entregavam uma experiência tão elegante quanto eu queria
- Serviços de consultoria de investimento, como Personal Capital, têm integração de contas, mas dificultam uma visão profunda de cada ativo e são mais focados em vender o próprio serviço de consultoria
- Apps menores de acompanhamento de posições na App Store não satisfaziam em design e UX
- Em geral, os apps financeiros existentes priorizavam densidade de informação, exigindo navegar da lista para telas de detalhe; o Stocketa queria mostrar mais informações já dentro dos cartões da tela inicial
Estrutura do app feita com UI customizada
- O Stocketa foi projetado em torno de um feed único e de cartões de ações, minimizando a UI cromada ao redor
- A ideia era evitar usar tab bar padrão ou cabeçalhos de navegação tradicionais
- A maior parte da funcionalidade ficava dentro dos cartões de ações na timeline inicial
- Quase toda a UI foi construída com componentes customizados em SwiftUI
- Em vez de pull-to-refresh, o app usava pull-to-search para procurar e adicionar novos ativos
- Os cartões de ações mostravam inline quase todas as informações principais, incluindo scrubbing de gráfico, ações de swipe e estatísticas importantes
- Ao tocar em um cartão, ele permanecia no lugar e cartões animados de notícias e detalhes da posição apareciam sobre ele; a implementação central usava
overlayPreferenceValueeanchorPreference - Sheets customizadas incluíam seletor de data, telas explicativas, fundo com estrelas brilhando e interpolação da transparência do fundo durante o arrasto
- Um menu customizado do app oferecia espaço para mostrar plano pago ou status de assinatura e podia ser fechado por toque ou rolagem
- As ações de swipe e os cabeçalhos também foram implementados manualmente
- As ações se moviam conforme a distância do swipe e, ao passar certo limite, animavam para um estado selecionado
- Os botões do cabeçalho apareciam sem borda no topo da página e se transformavam em botões sutis conforme a rolagem subia
- O texto Stocketa desaparecia para cima por interpolação de acordo com o scroll
Rastreamento de operações e recursos do app
- A implementação inicial começou com scroll view dos cartões de ações, gráficos e persistência em banco de dados
- No começo usei Core Data, depois migrei para Firebase
- Foram integradas chamadas e polling de provedores de dados financeiros, usando a IEX como primeiro provedor
- O Stocketa não armazenava apenas a quantidade de ações por ativo; ele implementava rastreamento por operação
- A ideia era mostrar não só lucro e prejuízo totais de compra e venda, mas também o resultado de cada operação
- Em vendas, era preciso conhecer métodos de custo como FIFO e LIFO e calcular de quais operações anteriores as ações estavam saindo
- Em desdobramentos, em vez de alterar operações passadas de forma simples, era necessário oferecer aplicação automática de splits novos e também aplicação manual
- Os recursos se expandiram por todo o app
- Onboarding: em vez de um carrossel comum, usava um fluxo em que as páginas avançavam no eixo z, apresentando componentes centrais como os cartões de ações
- Importação de TSV: operações podiam ser importadas por arquivos TSV criados em planilha, e o estado da importação era gravado de volta no TSV para permitir reimportação após sincronização via iCloud
- Entrada de compra: havia um formulário específico para ações, centrado em quantidade, preço e data; ao mudar a data, o placeholder passava a mostrar o preço histórico daquele dia
- Entrada de venda: suporte a FIFO, LIFO, Average cost, Highest cost, Lowest cost e lot específico
- Tela de assinatura: incluía texto com parallax, desfoque que se desfazia com o scroll, cartões de recursos com movimento e um pequeno efeito de fireworks ao chegar ao fim
- Cartão de assinatura/membership: mostrava o status de uma conta com assinatura ativa em um cartão interativo, com efeito de campo estelar ao fundo e shimmer
- Configuração de exibição dos cartões de ações: oferecia vários formatos e ajustes, com microanimações ao alternar opções
- Cartão de posição consolidada: mostrava informações agregadas com base nas configurações no topo do scroll principal
- Widgets: havia três tipos — ação individual, portfólio e portfólio simples — com grandes gradientes mudando conforme o desempenho
- Período de tolerância do Face ID: por ser um app de ativos financeiros, permitia ajustar um intervalo antes de bloquear novamente ao alternar de app
- Sheet de feriados de mercado: exibia dias sem pregão para contas com ações em carteira, exigindo também código de servidor para refletir isso nos widgets
- Sheet de feedback: mostrava problemas conhecidos e recursos planejados, podendo incluir um token para identificar a conta em perguntas específicas
- Exclusão de conta: em resposta à exigência de atualização da App Store de 2021, permitia apagar completamente a conta dentro do app
Aprendendo com SwiftUI e refazendo tudo
- O Stocketa foi desenvolvido ao mesmo tempo em que eu aprendia Swift e SwiftUI, então muitas telas e partes do app foram constantemente redesenhadas e reimplementadas
- Desde o iOS 13, o SwiftUI evoluiu em desempenho, componentes e recursos, e no iOS 17 passou a oferecer também shaders, keyframes e scroll transitions
- O SwiftUI foi útil não só como ferramenta de código, mas também como ferramenta de design
- Designers conseguem montar layouts rapidamente
- É fácil adicionar interações
- Ferramentas nativas ajudam a validar a sensação real do design
- Os casos em que UIKit foi necessário eram limitados
- Campos de texto customizados para controle mais fino de entrada, estilo e formatação
- CollectionView para reorganizar a ordem dos ativos por arraste
- Emissores de partículas como confetti ou estrelas em um céu noturno
UILongPressGestureRecognizerpara obter a posição inicial de um toque longo
Backend e operação do TestFlight
- No começo, a ideia era um app simples sem backend nem contas de usuário, mas logo ficou claro que seria preciso um backend próprio
- Foi criado um proxy no backend para não colocar as chaves das APIs financeiras dentro do app
- Foram adicionados rate-limiting e throttling para evitar aumento de custos por abuso de API
- Também foi adicionado Sign In With Apple
- Estatísticas por ativo eram armazenadas em cache por um certo período para que várias contas pedindo os mesmos dados não gerassem chamadas repetidas
- O backend, baseado em Node/Express, cresceu até cerca de 13.000 LOC
- Rodava no Cloud Run
- Cuidava de autenticação, cache, notícias, lógica principal de negócio, limpeza de dados, notificações, widgets, várias integrações de API e algum scraping
- A gestão do trabalho era feita no Linear
- Ele era usado para gerenciar tarefas, ideias, recursos, feedback de clientes e marcos
- O TestFlight começou como uma pequena alpha para amigos próximos e foi sendo ampliado aos poucos
- Em certo momento, cerca de 1.000 pessoas foram convidadas
- Havia milhares de pessoas na lista de espera, mas o número não aumentou mais por causa dos custos de API e hospedagem, além da carga de suporte e feedback
- O feedback misturava boa primeira impressão, pedidos de recursos e relatórios de bugs
Design e implementação do site
- O site inicial era uma landing page simples para gerar interesse e coletar emails da lista de espera
- O design inicial incluía elementos ondulados inspirados de forma solta nas subidas e descidas dos gráficos do mercado e mini cartões de ações flutuando
- Esses mini cartões tinham gráficos de linha em SVG e eram animados ao carregar a página
- O movimento suave no fundo foi implementado com
offset-pathem CSS e duas animações keyframe
- A homepage
v1.0foi concluída no começo de 2021- Quando já havia funcionalidades suficientes para mostrar o app, foi criada uma homepage mais completa
- Em vez da estrutura comum de frame de celular + headline, foi tentada uma composição em que um frame 3D de telefone se movia com o scroll enquanto um vídeo teaser do app era reproduzido
- Em vez de Lottie, foi usada uma abordagem em que JavaScript desenhava sequências de frames em
canvaseoffscreenCanvas - Com Rotato, foram criados um curto screencast do Stocketa e vídeos 3D de telefone, além de sequências de frames PNG
- Depois, gerar, otimizar e atualizar essas sequências de frames virou um grande incômodo
- A homepage
v2.0foi redesenhada um ano depois- Para mostrar mais recursos e melhorias, a ideia era trocar módulos longos e repetitivos por uma lista de recursos fácil de escanear
- Foi adotada uma estrutura de dois painéis, com um frame de dispositivo fixo à direita e uma lista rolável de recursos à esquerda
- Ao passar o mouse sobre um item da lista, a captura de tela dentro do frame do dispositivo mudava
- No mobile, o cabeçalho foi transformado em um carrossel rolável de frames de dispositivo
- Também foram adicionados gradiente no texto hero conforme o scroll, mudança de cor dos ícones, gradientes de fundo em hover e emissores de partículas nos ícones
As limitações impostas pelas APIs de dados financeiros
- A qualidade dos dados financeiros de que o Stocketa dependia não estava em nível suficiente para um lançamento
- O primeiro provedor, IEX, tinha vários problemas
- Em alguns casos, os gráficos estavam desatualizados ou os preços eram imprecisos
- O problema era especialmente grave para ativos pouco negociados na bolsa IEX
- Não havia dados do mercado OTC, e seria preciso firmar uma licença cara diretamente com o OTC
- Os dados de ações listadas na Nasdaq também eram muito limitados, e faltavam até dados básicos como pre-market e after-hours
- Dados de fundos mútuos foram interrompidos completamente sem aviso prévio
- Outro provedor com potencial comercial usado depois custava US$ 100/mês, mas tinha problemas ainda piores de dados e confiabilidade
- Endpoints retornavam dados antigos ou incorretos
- Ao relatar problemas, os bugs eram ignorados ou negados
- Alguns dados eram difíceis de obter mesmo com APIs pagas, então foi necessário criar um motor de scraping no backend
- dividend yield
- earnings dates
- dados de OEF
- Dados de mercado de alta qualidade tinham uma estrutura de preço incompatível com um desenvolvedor independente
- Os preços de dados comerciais de alguns provedores importantes do mercado americano começavam em US$ 2.000/mês
- Dados de índices ou opções exigiam custos extras
- Um provedor ofereceu um plano para startups de US$ 499/mês mais cobrança por MAU, mas isso ainda foi considerado caro demais para a necessidade
- Projetos que dependem de APIs de dados financeiros carregam o risco de construir um negócio sobre APIs de empresas externas, algo visto como um problema semelhante às mudanças nas APIs do Twitter e do Reddit
Por que o lançamento foi interrompido
- O Stocketa sempre foi, desde o início, um side project, não um negócio de verdade
- Para virar um negócio real, seriam necessários mais recursos e algum modelo de monetização
- Talvez fosse preciso entrar em áreas como consultoria financeira ou negociação de ações, que são espaços lotados e disputados por grandes empresas
- Isso não combinava com o ponto de partida do projeto, que era criar um app simples e elegante
- Os motivos da interrupção podem ser resumidos em três pontos
- Dados: para lançar com um preço de assinatura razoável, seriam necessários dados financeiros confiáveis, baratos e de alta qualidade, mas o custo e a dificuldade de obtê-los eram altos
- Suporte: pelo uso observado no TestFlight, haveria necessidade de investimento significativo em suporte ao cliente, email, manutenção e desenvolvimento contínuo
- Tempo: a escolha foi focar na Rewind AI, e o Stocketa já tinha consumido noites, fins de semana e até tempo de escrita no blog por anos
- O objetivo original de aprender Swift e SwiftUI foi alcançado
- Mesmo sem lançamento, houve muito aprendizado em desenvolvimento nativo para iOS
- O conhecimento de SwiftUI adquirido com o Stocketa ajudou bastante no trabalho com a Rewind AI
1 comentários
Comentários no Hacker News
Um projeto de vários anos que nunca é usado de verdade quase parece uma medalha de honra. Não uma medalha sábia, mas, para desenvolvedores como eu, que se envolvem demais no bom sentido e são completamente sugados, é quase uma lição necessária.
Quando falo de experiências passadas e da dor que veio delas, sinto que as pessoas ficam meio atônitas. Se você nunca passou mais de 10 horas por dia, por mais de um ano, agarrado a alguma coisa, acho difícil entender a sensação de despejar milhares de horas e não ter nenhum resultado.
Tive muitos projetos bem-sucedidos e eles continuam indo bem, mas esses projetos de vários anos não têm volta. Talvez tenham sido necessários para o aprendizado, mas ainda fico triste e solitário quando penso neles.
Em troca, ganhei as habilidades que me tornaram um programador muito melhor e passei a gostar de Swift. Hoje, graças à evolução das ferramentas de desenvolvimento e APIs da Apple nos últimos 7 anos, acho que eu conseguiria recriar o app inteiro do zero em um fim de semana.
Projetos assim merecem ser celebrados. Programação pode ser arte, e arte, como saída criativa, pode ser feita apenas para si mesmo.
O pensamento orientado a resultados, em que você sofre por projetos passados com conhecimento adquirido depois, não é muito útil. A maioria dos projetos e negócios fracassa, e essa é a realidade.
Lembro de um texto de um ex-profissional do mercado financeiro que dizia: “ninguém é demitido por ganhar dinheiro por acaso em uma operação ruim”. Em uma cultura orientada a resultados, é fácil esquecer que a única coisa que de fato controlamos é o processo.
Aprendi uma lição valiosa, como saber a hora de parar, mas foi uma aula longa demais. Ainda nem consigo pensar nisso direito.
Fico me perguntando se eu recomendaria não começar um projeto de mais de 1 ano a menos que você tenha certeza de que não vai se arrepender mesmo que ninguém use.
Ótimo texto, mas a conclusão me parece triste. Há tantos apps para os quais eu queria que existisse essa “versão sem nada inútil”, e a quantidade de trabalho colocada aqui é impressionante.
Lancei um app iOS mais simples que, por tráfego orgânico, chegou a cerca de 1.000 usuários ativos por dia, mas acabei fechando. Coloquei uma compra dentro do app para remover anúncios por US$ 1,99 ou US$ 2,99 e ganhava cerca de US$ 70 por mês enquanto lidava com relatórios de bugs.
Na época, o SwiftUI ainda não era maduro, então precisei usar UIKit, e eu continuava esbarrando em casos de borda difíceis de corrigir.
O golpe final foi quando o Google alegou que eu tinha clicado nos meus próprios anúncios e suspendeu a veiculação. Na prática, toda a receita acabou, e percebi que, por mais que eu me dedicasse a criar aquilo, no fim dependia dos caprichos das gigantes de tecnologia.
A Apple ficava com 30% de todas as compras dentro do app, a receita de anúncios era troco, e o Google podia desligá-la sem motivo e sem qualquer forma de recurso. Eu não estava tentando ganhar muito dinheiro, mas precisava de um motivo para gastar tempo e energia, e já não dava mais para justificar.
Não entendo por que não deixam o desenvolvedor cadastrar dispositivos excluídos da receita, faixas de IP e listas de locais. No fim você acaba usando seu próprio app, e em muitos casos é quase impossível não tocar sem querer em um anúncio enquanto rola a tela.
Testes em ambiente real também são importantes, e há ao menos uma pequena possibilidade de o anúncio ser realmente interessante e você clicar legitimamente como potencial cliente. Mas o jeito atual basicamente significa que você não deve usar seu próprio app.
Não sei a quem isso beneficia, além dos desenvolvedores ou gerentes do Google que insistem nessa política fundamentalmente idiota. Será que é tão difícil assim lidar com isso de um jeito minimamente decente?
Dá para pensar de forma parecida sobre um músico de quarto que em 20 anos nunca fez show nem lançou álbum, um atleta de infância que nunca mais tocou na bola depois dos 18, um corredor que não participa de provas, ou uma mente intelectual que não ensina.
Partindo da mesma premissa de que “produção externa = valor”, isso poderia até se estender a uma pessoa que viveu décadas sem ter filhos. Não é a minha crença; quero dizer apenas que é para onde a lógica leva.
Mas esse tipo de julgamento é uma construção humana e parece um espelho do impulso biológico de lançar ao mundo “nossos filhos” há muito gestados. Perguntamos por que eles não deveriam ganhar vida própria, e esperamos que continuem produzindo por si mesmos e acrescentando fios ao futuro.
Seguir esse impulso é o auge moral, ético ou do prazer? Para algumas pessoas, sim, e não quero julgar. Mas, para outras, estranhamente, não é.
Fico me perguntando se há interesse em vender este projeto. Gosto do conceito e parece haver clientes interessados, mas você não parece querer assumir o peso do suporte e da comercialização.
Faz cerca de 1 ano que criei um rastreador de patrimônio (https://jch.app), mas em termos de funcionalidades ou experiência do usuário ainda está muito atrás.
É um trabalho enorme, e o registro profundo da jornada também foi ótimo. Também estou curioso para ver o que virá a seguir da rewind.ai.
Não é muito diferente de inúmeras pessoas dedicarem tempo sem fim a inúmeros projetos que não tinham grande significado para mais ninguém. É como hackear uma bicicleta ou mexer com trens em miniatura
A diferença é que apps podem ser publicados, documentados e arquivados tão facilmente assim, enquanto “o tempo gasto cortando e soldando peças estranhas de bicicleta” é difícil de fazer do mesmo jeito, a menos que você coloque tudo no YouTube
Claro que muita gente faz isso, e até tem algum sentido, mas quando você tenta transformar hobbies e buscas pessoais em produtos com algum objetivo além deles mesmos, acaba perdendo um pouco o ponto central
Não quero dizer que textos assim estejam errados. Não parece uma reclamação nem raiva; é mais aquela sensação de “quero deixar isso registrado em algum lugar, de algum jeito”, como foi dito no começo. A maior parte do meu GitHub é igual
Esse ajuste precipitado do critério de avaliação pode não ser muito útil, porque obscurece o resultado real. Neste caso, é fácil o autor sentir vergonha do esforço “desperdiçado”, mas uma reação mais adequada seria gratidão pelo que aprendeu
O aprendizado que se tira de projetos assim é enorme
O que assusta nesse texto é que vejo muito de mim nele. Mergulhar fundo na criação de um ótimo produto, envolver-se na identidade de artesão e sentir rejeição pela abordagem “sem alma” de um MVP rápido e sujo e de testes de mercado iterativos
Se você já visitou o subreddit r/SaaS, aquilo é exatamente o oposto do que eu gosto como desenvolvedor de software
Mas também sei que, se eu conseguir mudar minha mentalidade sobre testes de mercado e iterações rápidas e sujas, isso na verdade vira uma base melhor para ficar tranquilo e focar no artesanato
Entendo. Eu também ainda estou construindo um backend bancário. Já são 5 anos, incluindo core, contabilidade, clientes, contas, pagamentos (SEPA e cartões), notificações (algo tipo ServiceNow), triagem de sanções, motor de risco e monitoramento, fluxo de eventos e relatórios
Estou começando a me perguntar se fui louco ou burro por assumir uma coisa tão gigantesca. Ainda nem projetei a landing page, e o código tem por volta de 150 mil linhas
Mesmo com muitos bugs, limitações e problemas de segurança nas plataformas existentes, levou muito mais de 5 anos para conquistar confiança suficiente para convencer clientes a migrarem para nossa plataforma de core banking
Mas, quando apps mobile ficaram importantes, comecei também a fazer apps nativos, e aquilo foi demais. Acabei em burnout e até falência, então não recomendo megaprojetos
Aprendi muito, mas o desperdício não valeu a pena. Tempo é importante demais para refazer tanta coisa. Hoje faço apenas projetos “pequenos”, de no máximo 6 a 12 meses
Também fico curioso se existe a possibilidade de ser open source
Se você não consegue lançar uma versão básica sem tudo o que ainda quer adicionar, deveria considerar deixar isso de lado por um tempo
Nos últimos mais de 20 anos, criei muitos projetos e produtos, e 2 ou 3 cresceram bastante. Os outros têm milhares de usuários, mas não uma fonte de receita contínua
Um deles, que virou um negócio lucrativo, foi lançado como uma versão “feita no fim de semana” e depois foi construído ao longo de 7 anos
O estado familiar pode virar uma armadilha que faz você continuar encontrando trabalho pré-lançamento. Por isso pode ser bom reenquadrar isso na sua cabeça
Li recentemente que Leonardo DaVinci era um tremendo procrastinador e tinha muitos projetos que não terminou
Eu sempre o admirei por ter tido tanto sucesso em várias áreas, mas, depois de saber disso, minha visão mudou para ver a procrastinação como parte de nós, pessoas criativas
Dá para ver como fracasso, mas também dá para reinterpretar como parte do processo. No fim, gostamos de criar coisas
Na prática, ele aceitou o conselho comum de “crie um MVP, lance rápido e itere” e então fez exatamente o oposto
Excelente texto sobre um side project que parece muito legal. Estava lendo com interesse quando descobri o rewind.ai, que também parece bom
Em algo potencialmente invasivo como gravação automática contínua de tela e áudio, privacidade é essencial; ao ver a seção “Privacy first”, fiquei com uma dúvida
Diz: “Criptografe seus dados com o FileVault. O Apple FileVault funciona com o Rewind. Ao ativá-lo, seus dados serão criptografados”. O Rewind faz algo para que o Apple FileVault “funcione com o Rewind”?
Ou está falando da criptografia de disco comum, que se aplica a tudo? Pergunta sincera. Quero usar o Rewind, mas essa formulação pode ser interpretada como um enfeite desnecessário
A frase “apenas dados relevantes baseados em texto são enviados para a nuvem e criptografados em trânsito” passa uma sensação parecida. “Criptografados em trânsito” quer dizer a criptografia TLS padrão? O Rewind parece bom, mas precisa ser confiável
“Não é necessária integração com a nuvem. Tudo funciona automaticamente, sem precisar conectar vários serviços como Gmail, Dropbox ou Slack”
Mas, ao olhar os detalhes da política oficial de privacidade em https://www.rewind.ai/privacy, está assim:
“[Itens coletados:] Informações geradas pela OpenAI. Como parte da integração da Rewind AI com a OpenAI, também podemos coletar saídas geradas pela OpenAI, como resumos de transcrições de gravações de áudio e outros dados gerados pela integração com a OpenAI”
Então, embora “não seja necessária integração com a nuvem”, meu áudio é compartilhado com a OpenAI?
Além disso, em https://www.rewind.ai/privacy-first diz: “O que acontece quando você pesquisa no Rewind? Todos os dados gravados permanecem localmente”
Não sei qual dos dois é o caso. Quero confiar no Rewind, mas a inconsistência nas explicações sobre como os dados dos usuários são tratados me deixa hesitante