Como compilar e distribuir apps para Mac e iOS sem abrir o Xcode
(scottwillsey.com)- Depois de concluir a configuração inicial da conta Apple e dos certificados, é possível automatizar a compilação, assinatura e distribuição de apps para Mac e iOS usando apenas as ferramentas de CLI dentro do Xcode.app
- Em
project.yml, o XcodeGen recria o.xcodeproj, excodebuild,notarytool,stapleredevicectlcuidam de tudo, da geração do projeto à notarização no Mac e à instalação no iPhone - A distribuição para Mac pode ser executada em ordem com apenas
scripts/release.sh: arquivamento, assinatura com Developer ID, notarização, anexação do ticket, validação no Gatekeeper e instalação em/Applications, interrompendo imediatamente em caso de falha - Para compilação e testes rápidos, é possível usar
CODE_SIGNING_ALLOWED=NO, mas esse build com assinatura temporária não passa pelo Gatekeeper e também não vincula permissões como iCloud KVS e App Group ao Team ID real - Se a chave privada de assinatura e a senha de notarização forem guardadas no keychain de login, e os comandos e regras de distribuição forem registrados em
CLAUDE.mdouAGENTS.md, agentes como o Claude Code podem repetir todo o fluxo de trabalho
O que dá para fazer sem a interface do Xcode
- O próprio Xcode precisa estar instalado, mas não precisa ser aberto;
xcodebuild,notarytool,stapleredevicectlsão todos ferramentas de shell dentro do Xcode.app e podem ser executadas de forma independente - A configuração inicial, como login com Apple ID, criação do certificado Developer ID e armazenamento da senha de notarização, exige GUI ou terminal interativo
- Depois da configuração inicial, é possível fazer sem usar o mouse: geração do projeto, build, archive, exportação do app assinado, notarização, validação, instalação e até distribuição para um iPhone real
- Se travar na configuração ou na escrita de scripts, é possível fornecer os requisitos da tarefa e os valores específicos do projeto ao Claude Code ou a outra ferramenta de programação com LLM para montar o workflow
Selecionar toda a cadeia de ferramentas do Xcode
- Primeiro, é preciso verificar se o resultado de
xcode-select -pé/Applications/Xcode.app/Contents/Developer - Se as Command Line Tools independentes estiverem selecionadas, defina a cadeia completa de ferramentas do Xcode com o comando abaixo
sudo xcode-select -s /Applications/Xcode.app/Contents/Developer
- O
/Library/Developer/CommandLineToolsinstalado comxcode-select --installincluiclangegit, mas não traz todo o conjunto de ferramentas para desenvolvimento de apps, como iOS SDK,notarytooledevicectl - Se o Xcode já estiver instalado, o pacote separado de Command Line Tools não é necessário
- A aceitação da licença e a instalação de componentes adicionais também podem ser feitas pela CLI
sudo xcodebuild -license accept
sudo xcodebuild -runFirstLaunch
Reproduzir o projeto com XcodeGen
- O XcodeGen gerencia as configurações do projeto Xcode em YAML e recria o
.xcodeproja cada build - É possível baixá-lo no GitHub ou instalar via Homebrew
brew install xcodegen
- O
.xcodeprojé uma pasta que no macOS aparece como se fosse um arquivo, e como o Xcode muda constantemente seus arquivos internos e referências, isso pode gerar conflitos ou mudanças desnecessárias no repositório Git - Ao versionar apenas o
project.ymle ignorar o.xcodeprojgerado, a configuração do projeto permanece reproduzível
xcodegen generate
Configuração da conta Apple Developer e dos certificados
- É preciso adicionar o Apple ID em
Settings → Accountsdo Xcode - Para distribuir o app e fazer a notarização, é necessária uma conta paga do Apple Developer
- O certificado de distribuição é criado em
Settings → Accounts → Apple ID → Manage Certificates… → + → Developer ID Application - Apple Development e Developer ID Application têm finalidades diferentes
- Apple Development é usado para rodar no próprio dispositivo ou transferir para o iPhone durante depuração local
- Developer ID Application é necessário para distribuir um
.appnotarizado que passe pelo Gatekeeper também em Macs de outras pessoas
- Quando o certificado Developer ID Application é criado, o certificado e sua chave privada correspondente são instalados no keychain de login
- A chave privada é usada na assinatura real e não pode ser baixada novamente; por isso, não a exclua e faça backup do keychain
Armazenar credenciais de notarização no keychain
- A notarização de apps para Mac é o processo de enviar o app assinado à Apple para inspeção de malware
- O
notarytoolautentica com um perfil armazenado no keychain, e na criação inicial do perfil é preciso digitar interativamente uma senha específica de app
xcrun notarytool store-credentials App-Name \
--apple-id "you@example.com" \
--team-id YOUR-TEAM-ID
- Em vez de reaproveitar o perfil de notarização de outro app, o ideal é criar um perfil com o nome do app, evitando falhas silenciosas em outros ambientes
- A senha específica de app é diferente da senha do Apple ID e pode ser criada em Apple ID site, em
Sign-In & Security → App-Specific Passwords - Se a senha do Apple ID for alterada, a senha específica de app pode expirar sem aviso separado
- Se aparecer
401 invalid credentialsdurante a notarização, antes de suspeitar de toda a configuração, vale verificar se a senha específica de app expirou
- Se aparecer
- É possível validar se o perfil foi salvo com o comando abaixo
xcrun notarytool history --keychain-profile App-Name
- Se a senha específica de app ficar guardada em um cofre do 1Password acessível ao Claude Code, também dá para delegar ao agente a criação do perfil de notarização de um novo app
- Também é possível automatizar a entrada da senha com
--password, mas como isso pode deixar a senha no histórico do shell, o recomendado é digitá-la manualmente uma vez e depois armazená-la no keychain ou no 1Password
Configuração local de assinatura e separação de segredos
- O Team ID real e o prefixo do bundle necessários para a assinatura são armazenados em
Local.xcconfig
cp Local.xcconfig.example Local.xcconfig
BUNDLE_PREFIX = your.real.prefix
DEVELOPMENT_TEAM = YOUR-TEAM-ID
- Adicione
Local.xcconfigao.gitignorepara que as configurações locais de cada projeto não entrem no repositório - Como a chave privada de assinatura fica no keychain de login e a senha específica de app para notarização fica no perfil do keychain do
notarytool, informações sensíveis não ficam registradas no Git
Automatizar a distribuição para Mac com release.sh
- O
scripts/release.shdo repositório executa todo o pipeline de distribuição do app para Mac com um único comando
./scripts/release.sh
-
O script funciona na seguinte ordem
- Verifica previamente se
xcodegene o perfil de notarização existem - Recria o projeto com
xcodegen generate - Gera um archive Release com
xcodebuild archive - Exporta o app assinado com Developer ID usando
ExportOptions.plistexcodebuild -exportArchive - Compacta o app em ZIP e envia à Apple com
notarytool submit --wait - Anexa o ticket de notarização com
stapler staple - Valida se o Gatekeeper permite a execução com
spctl - Encerra processos existentes e copia o app para
/Applications - Registra o app instalado com
lsregister - Revalida o ticket e o status no Gatekeeper do bundle instalado
- Verifica previamente se
-
Mesmo que o modelo conheça o procedimento geral de distribuição, informações específicas do projeto como nome do scheme, Team ID, nome do perfil de notarização e local de instalação precisam ser fornecidas pelo usuário
-
Se o primeiro script falhar, será necessário ajustá-lo iterativamente com base na saída da execução, mas depois que o workflow estiver estável, basta executar o comando de distribuição sem mudanças adicionais
-
Evitar falhas e distribuições incompletas
set -euo pipefailfaz o script parar imediatamente se qualquer comando falhar, evitando que um estado incompleto seja tratado como sucessocd "$(dirname "$0")/.."faz o script ir para a raiz do repositório independentemente do diretório de onde foi chamado- O bloco de pré-verificação valida
xcodegene o perfil de notarização antes do archive, que leva mais tempo - Não só o app exportado, mas também o app copiado para
/Applications, é verificado novamente para detectar casos em que o bundle se corrompe durante a cópia - Se outro perfil de notarização for necessário, ele pode ser sobrescrito por variável de ambiente
TZED_NOTARY_PROFILE=<name> ./scripts/release.sh
Passar regras de distribuição para o agente
- Se o
release.shfaz a distribuição com um único comando, entãoCLAUDE.mdouAGENTS.mdpermite que o agente use esse comando em todas as sessões sem precisar de instruções adicionais - O documento deve registrar a recriação do projeto, os testes unitários, o build rápido no macOS e o comando de distribuição real
xcodegen generate
swift test
xcodebuild -project YOUR-APP-NAME.xcodeproj \
-scheme YOUR-APP-NAME-macOS \
-destination 'platform=macOS' \
CODE_SIGNING_ALLOWED=NO build
./scripts/release.sh
- A diferença entre os dois caminhos de build também precisa estar documentada
- O build com
CODE_SIGNING_ALLOWED=NOé adequado para CI e verificações locais rápidas - Para distribuir de verdade um app de barra de menu, passar por atributos de quarentena e usar sincronização via iCloud, é necessário o
release.sh, que faz assinatura com Developer ID e notarização
- O build com
- Se você instruir que o método usado em um app sirva como referência para outros repositórios, o Claude Code poderá replicar a mesma configuração em apps futuros
Build rápido sem assinatura e testes
- Testes unitários baseados em Swift Package Manager são executados sem build via Xcode
swift test
- Uma verificação rápida de compilação de app macOS pode ser feita com a assinatura desativada
xcodebuild -project TZed.xcodeproj \
-scheme TZed-macOS \
-destination 'platform=macOS' \
CODE_SIGNING_ALLOWED=NO build
- O build para simulador de um app iOS e de extensões de widget também pode ser executado da mesma forma
xcodebuild -project TZed.xcodeproj \
-scheme TZed-iOS \
-destination 'generic/platform=iOS Simulator' \
CODE_SIGNING_ALLOWED=NO build
- O build com assinatura temporária criado com
CODE_SIGNING_ALLOWED=NOpode ser usado para compilar e rodar no simulador - Esse build é rejeitado pelo Gatekeeper e, como não tem prefixo de equipe, permissões como iCloud KVS e App Group não se vinculam corretamente, então ele não serve para distribuição
Distribuição headless para iPhone real
- A distribuição para dispositivos iOS não tem a etapa de notarização dos apps para Mac e usa
xcodebuildedevicectl - Apps para dispositivo real são compilados e assinados com certificado Apple Development e perfil de provisionamento de desenvolvimento
xcodebuild -project TZed.xcodeproj \
-scheme TZed-iOS \
-destination 'generic/platform=iOS' \
-allowProvisioningUpdates \
-derivedDataPath build/ios \
archive -archivePath build/TZed-iOS.xcarchive
- O
.appgerado é instalado especificando o UDID do dispositivo conectado
xcrun devicectl device install app \
--device <DEVICE-UDID> build/ios/…/TZed.app
devicectl list devicesmostra os dispositivos conectados e emparelhados, junto com seus UDIDs- Ao usar
-allowProvisioningUpdates, os perfis de provisionamento de desenvolvimento necessários são buscados automaticamente
Como a assinatura de código funciona sem GUI
-
Ao criar um certificado Developer ID Application, a Apple emite o certificado e o Mac gera a chave privada correspondente, armazenando ambos no keychain de login
-
O
codesignchamado porxcodebuildassina o binário com a chave privada e inclui no app o certificado que leva até a autoridade raiz da Apple, para que seja possível validar quem assinou -
Ao usar
signingStyle: automatic, oxcodebuildescolhe o certificado correspondente ao Team ID e busca na Apple os perfis de provisionamento necessários -
Por isso, não é necessário versionar perfis de provisionamento no repositório
-
Permissões, notarização e stapling
- O arquivo
.entitlementsde cada target contém permissões como sandbox, cliente de rede, iCloud KVS e App Group - Como as permissões são vinculadas no momento da assinatura com o Team ID real, builds com assinatura temporária não fazem iCloud e App Group funcionarem corretamente
- Assinatura e notarização são etapas diferentes
- A assinatura prova quem criou o app
- A notarização é o processo em que a Apple analisa o app assinado em busca de malware e emite um ticket
- O stapling anexa esse ticket ao app para que o Gatekeeper confie nele mesmo offline
- Em apps
LSUIElementde barra de menu que usam UI oculta, a notarização é necessária para evitar detecção pelo XProtect
- O arquivo
-
Comandos de validação manual
codesign -dv --verbose=4 /Applications/TZed.app spctl -a -vvv -t exec /Applications/TZed.app stapler validate /Applications/TZed.appcodesignverifica o signatário e o certificadospctlverifica se o Gatekeeper permitirá a execuçãostapler validatevalida se o ticket de notarização foi anexado
Ferramentas padrão executadas pelo agente
- Não é necessário um servidor MCP separado para build nem plugins dedicados; o Claude Code executa CLIs padrão em um shell não interativo
- As ferramentas usadas são
xcodebuild,xcrun notarytool,xcrun stapler,spctl,codesign,devicectl,xcodegeneswift - O
CLAUDE.mdtransmite ao agente a convenção de nomes do perfil de notarização, a diferença entre build rápido e distribuição real, e a regra de usarrelease.shna hora da distribuição - Apenas o
notarytool store-credentialsinicial permanece como etapa interativa; todo o restante pode ser automatizado
Substituir tarefas da GUI do Xcode pela CLI
- A geração do
.xcodeprojgerenciado pelo Xcode é substituída porproject.ymlexcodegen generate ⌘Bou o botão Run correspondem axcodebuild … buildProduct → Archiveé tratado comxcodebuild … archive- A distribuição de app pelo Organizer pode ser substituída por
xcodebuild -exportArchive - O upload pelo Organizer é feito com
xcrun notarytool submit --wait - Para anexação automática do ticket, use
xcrun stapler staple - A instalação por arrastar e soltar em
/Applicationspode ser feita comcp -Relsregister - A ação Run em um iPhone real é substituída por
xcodebuild archiveedevicectl device install - Depois de instalar Xcode e XcodeGen, configurar as credenciais iniciais e escrever
release.sheCLAUDE.md, novas distribuições de build podem ser executadas com uma frase ou um único comando
1 comentários
Comentários do Hacker News
Há muita coisa que não dá para resolver no sandbox, então eu executo agentes de IA diretamente no Mac, mas depois de ver o caso em que a xAI fez upload até do diretório home com as chaves SSH incluídas, estou repensando seriamente essa escolha
Não me preocupo tanto se a própria máquina quebrar. Quase tudo está em backup ou commitado no Git, então dá para recuperar em poucas horas. Mas eu definitivamente não quero que o diretório
.sshseja enviado para um agente de IA, e isso não deve ser bloqueado por prompt, e sim no nível do sistema. Estou considerando criar um usuário separado e aplicar permissão700ao diretório home. Parece um retrocesso da segurança para os anos 1990, mas, em troca, existe a vantagem de conseguir terminar o trabalho numa velocidade sem precedentes. Eu mesmo uso esse método arriscado, então não estou tentando criticar ninguémComo solução empacotada, dá para usar o open source Tart https://tart.run/ ou VirtualBuddy https://github.com/insidegui/VirtualBuddy, ou até fazer seu próprio vibe coding com uma ferramenta que envolva a API do sistema operacional https://developer.apple.com/documentation/virtualization. Recentemente também surgiu o Apple container https://github.com/apple/container
agent. Há um ano isso falhou por causa de OAuth e do keychain, mas recentemente consegui implementar com a orientação do Claude de que dá para usarCLAUDE_CODE_OAUTH_TOKENAinda é necessária uma ferramenta auxiliar para configurar permissões com ACL, mas no momento funciona no macOS. O nível de risco fica mais ou menos entre o sandbox padrão do Claude e o isolamento completo por contêiner ou máquina. Com a melhora do sandbox do próprio Claude, eu estava duvidando do custo-benefício desse projeto de defesa em camadas, mas, vendo a possibilidade de vazamento do diretório home, há motivo de sobra para continuar. Ainda não publiquei como open source, mas estou disposto a conversar com quem tiver interesse
Estou, por diversão, compilando e testando apps iOS usando só Linux, e é surpreendentemente simples, além de https://github.com/xtool-org/xtool funcionar muito bem
Mesmo sem precisar publicar no TestFlight ou na App Store, também dá para instalar diretamente no iPhone via USB a partir do Linux. Se você ainda não estiver convencido, pode pedir ao agente de código para criar e enviar um app iOS Hello World, e assim começar facilmente
Meu projeto open source Axiom ajuda ferramentas de execução de código a realizar com eficiência tarefas de desenvolvimento para os sistemas operacionais da Apple
Ele inclui várias tecnologias e agentes, além de ferramentas para LLM como
xclog,xcprof,xcsymexcui. Elas expõem os recursos necessários com eficiência de tokens e também são úteis para tecnologias ou agentes fora do Axiom. Axiom: https://charleswiltgen.github.io/Axiom/, ferramentas CLI: https://charleswiltgen.github.io/Axiom/tools/É uma sensação estranha ler a frase “pedi ao Claude Code para criar um script que, sem abrir o Xcode, faz archive, assinatura com Developer ID, notarização, stapling e instalação em
/Applications, interrompendo claramente em qualquer etapa que falhe”. Isso porque o próprio texto que estou lendo também parece o Claude falando conoscoTambém soa estranho que, em quase todas as etapas do texto, se repita algo como pedir de novo para o LLM: “mostre este texto ao Claude Code ou à ferramenta de coding com LLM que você preferir e deixe que ela resolva”, “se não souber, pergunte ao LLM e peça ajuda na configuração”, “o objetivo de usar LLM é evitar trabalho manual indesejado”, “peça que ele crie um workflow”
Como alguém que desenvolve apps para Mac e iOS com agentes há mais de um ano, considero este texto um conselho ruim. Em especial, o excelente Xcode MCP exige que o Xcode esteja aberto
Com as ferramentas MCP, é possível acessar vários recursos de forma muito mais rápida e concisa do que com
xcodebuild, além de permitir a geração e renderização de#Preview, o que é impossível com o Xcode fechado. O MCP e o agente embutido do Xcode 27 também controlam muito bem o simulador via DeviceHub. O DeviceHub substitui o Simulator.app e parece permitir que o agente entenda e opere a API de acessibilidade sem precisar de gambiarras com ferramentas comoaxeO Xcode MCP não é perfeito. O problema de aparecer uma janela de permissão toda vez que o agente é executado eu contornei automatizando a aprovação com o Keyboard Maestro. Ainda assim, o Xcode 27 melhorou muito a usabilidade do vibe coding no Xcode. Agora eu executo o Codex na própria UI do agente do Xcode, sem lidar separadamente com o MCP, e esse ambiente de execução orienta melhor o modelo a usar as ferramentas disponíveis. No fim, o mais importante é obter feedback mais rápido e validação mais robusta
Xcode-mcp-proxy, que faz proxy do Xcode MCP, clica nos diálogos e até restaura a conexão. Fiquei curioso se você usa Peekaboo para testar apps macOS ou se usa automação comum de screenshotsSe você quiser mexer o mínimo possível nas IDEs de iOS e Android, React Native e Expo também são uma boa escolha
Xcode e Android Studio são usados só para instalar os simuladores de dispositivo; até a publicação nas lojas, o restante é resolvido pela toolchain do Expo. Mesmo no Windows, onde não dá para instalar o Xcode, é possível rodar apps iOS diretamente no iPhone usando os apps gerenciados do Expo. Como ponto de partida com RN e Expo, o bem mantido Ignite é excelente: https://github.com/infinitered/ignite
Em termos estritos, isso ainda é usar o Xcode. O Xcode é basicamente uma GUI extremamente bugada que encapsula vários utilitários e apps UNIX de nível de sistema, em geral estáveis
Lançar apps via CLI é uma prática antiga, e pelo menos desde 2012, quando publiquei meu primeiro app com Xcode, isso já era possível
Pelo menos para parte da depuração, todo mundo acaba tendo que usar o Xcode
O build o agente já consegue fazer quase todo pela linha de comando e, se você der instruções explícitas conforme a situação, ele consegue fazer isso mais rápido. Se você usar os grupos de sincronização de sistema de arquivos do Xcode, o XcodeGen pode ser trabalho extra desnecessário
No iOS, a recomendação que eu mais faria é adicionar ao agente a funcionalidade do App Store Connect: https://github.com/rorkai/App-Store-Connect-CLI. Aí, além de não precisar deixar o Xcode sempre aberto, você nem precisa estar perto do MacBook. Dá para fazer alterações no iPhone com o Codex, mandar o
ascgerar o build e subir para o TestFlight, baixar e executar a nova versão, e repetir o processoContinua me incomodando o fato de que desenvolvedores sem orçamento para comprar um Mac ficam, na prática, excluídos do desenvolvimento iOS. Cada vez que os produtos da Apple ficam mais caros, mais gente deixa de poder participar do desenvolvimento
Se a ideia é fazer vibe coding de apps, no longo prazo Expo teria sido uma escolha melhor. Como é baseado em React, há muito mais código React do que Swift nos dados de treino dos LLMs, e a qualidade do código resultante também é maior
Tudo pode ser feito pela linha de comando, e como é uma web view, depurar também é fácil. Depois de compilar como app iOS nativo, a sensação é parecida com a de outros apps nativos. Combinando Expo e Fastlane, dá para automatizar completamente o envio e a distribuição no iOS, a ponto de subir uma nova versão para a App Store com um único comando