Pular para o conteúdo principal

Desenvolvimento iOS com Servidores Mac Remotos de Build

iOS remote Mac build setup with Cursor

Compilar um app iOS exige um macOS em algum ponto do fluxo, mas isso não significa que seu editor precisa rodar em um Mac. Com o suporte a Remote-SSH do Cursor, você pode manter uma experiência de edição local rápida no Windows ou Linux enquanto uma máquina Mac cuida da compilação, da assinatura e das execuções no simulador. Este guia percorre as opções de arquitetura, a configuração, os comandos de build e as armadilhas que mais consomem tempo.

Por Que um Mac É Necessário​

A cadeia de ferramentas da Apple só roda no macOS, e várias partes do fluxo de trabalho iOS dependem dela diretamente.

  • Assinatura de código: as identidades de assinatura vivem no keychain do macOS e são usadas para produzir artefatos assinados.
  • Perfis de provisionamento: os perfis que vinculam um app ID a dispositivos e certificados são gerenciados pela ferramenta da Apple.
  • iOS Simulator: o Simulator é um aplicativo macOS; não há equivalente suportado no Linux ou Windows para fidelidade real de dispositivo.
  • Cadeia de ferramentas xcodebuild: o compilador, o linker e os SDKs vêm dentro do Xcode no macOS.
  • Envio à App Store: o upload para o App Store Connect exige a ferramenta da Apple, tipicamente via Transporter ou altool/notarytool no macOS.

Como todos esses itens são presos ao macOS, a pergunta prática não é se você precisa de um Mac, mas onde esse Mac vive.

Opções de Arquitetura​

Não há uma única resposta melhor; a escolha certa depende do tamanho da sua equipe, do seu orçamento e da frequência com que você compila.

OpçãoComo funcionaPrósContras
Remote-SSH em um MacO Cursor se conecta via SSH; arquivos e terminal vivem no MacFonte única da verdade, acesso completo à cadeia de ferramentas, Simulator realPrecisa de um Mac persistente e acessível
rsync / git push para um MacEdite localmente, envie para o Mac, compile láFunciona offline para edição, modelo mental simplesDeriva de sincronização, transferências grandes lentas, sem LSP ao vivo
Runners macOS de CI na nuvemEnvie para o Git, o CI compila em um runner macOS hospedadoNenhum hardware para possuir, reproduzível, bom para releasesNão interativo, custo por minuto, partidas a frio
Provedores de Mac na nuvemAlugue um Mac (MacStadium, AWS EC2 Mac) via SSHHardware real sob demanda, escalávelCusto por hora, sobrecarga de provisionamento e desmontagem

Para o desenvolvimento do dia a dia, o Remote-SSH em um Mac costuma ser o melhor equilíbrio. O CI na nuvem se encaixa em pipelines de release. Provedores de Mac na nuvem se encaixam em equipes que querem uma frota sem possuir bancadas.

Combine a ferramenta com a tarefa

Use Remote-SSH para desenvolvimento interativo, CI na nuvem para releases e instâncias Mac na nuvem para escala ou surtos curtos. A maioria das equipes maduras usa pelo menos duas dessas opções, em vez de forçar uma única opção a fazer tudo.

Configurando um Mac Remoto​

Estas etapas são executadas uma vez no Mac que atuará como servidor de build. Você precisa de direitos de admin e de uma conexão de rede estável.

1. Instale o Xcode e as Command Line Tools​

Instale o Xcode pela Mac App Store e depois instale as command line tools para que xcodebuild e binários relacionados fiquem disponíveis:

xcode-select --install

# Point the toolchain at the full Xcode app, not the standalone CLT
sudo xcode-select -s /Applications/Xcode.app/Contents/Developer

2. Aceite a Licença​

Em uma instalação nova, o xcodebuild se recusa a rodar até que a licença seja aceita:

sudo xcodebuild -license accept

3. Crie um Usuário de Build​

Evite compilar como uma conta pessoal. Crie um usuário dedicado para que credenciais e keychains fiquem isolados:

# Create a build user (adjust the short name and full name)
sudo dscl . -create /Users/builduser
sudo dscl . -create /Users/builduser UserShell /bin/zsh
sudo dscl . -create /Users/builduser RealName "Build User"
sudo dscl . -create /Users/builduser UniqueID 510
sudo dscl . -create /Users/builduser PrimaryGroupID 20
sudo dscl . -create /Users/builduser NFSHomeDirectory /Users/builduser
sudo passwd builduser

4. Ative o Login Remoto​

Ligue o SSH para que o Mac aceite conexões:

sudo systemsetup -setremotelogin on

5. Instale a Chave Pública SSH​

Adicione sua chave pública local ao authorized_keys do usuário de build para que você possa se conectar sem senha:

mkdir -p /Users/builduser/.ssh
# Paste your public key (id_ed25519.pub) into authorized_keys
chmod 700 /Users/builduser/.ssh
chmod 600 /Users/builduser/.ssh/authorized_keys
chown -R builduser:staff /Users/builduser/.ssh
Reforce a segurança da conta SSH

Um Mac de build exposto à internet não deve permitir login por senha para o usuário de build. Desative a autenticação por senha, restrinja os endereços de origem permitidos quando possível e mantenha a chave privada apenas na sua máquina local.

Conectando o Cursor via Remote-SSH​

Assim que o Mac estiver acessível, conecte o Cursor a ele. O remoto passa a ser o ambiente onde extensões, terminais e builds são executados.

1. Instale a Extensão Remote-SSH​

Abra o Cursor, vá para a visão de Extensões (Ctrl+Shift+X) e instale Remote - SSH (a extensão empacotada pela Microsoft está disponível no marketplace do Cursor).

2. Adicione uma Entrada no Config SSH​

Edite seu config SSH local, tipicamente em ~/.ssh/config, e adicione um alias de host:

# ~/.ssh/config
Host mac-build
HostName 203.0.113.10
User builduser
IdentityFile ~/.ssh/id_ed25519
ServerAliveInterval 30
ServerAliveCountMax 4
TCPKeepAlive yes

As configurações de keep-alive reduzem desconexões em redes instáveis. Observe que o OpenSSH nativo do macOS não suporta mosh, então os keep-alives são sua primeira linha de defesa.

3. Conecte​

Abra a Paleta de Comandos (Ctrl/Cmd+Shift+P) e execute Remote-SSH: Connect to Host, depois escolha mac-build. Como alternativa, execute em uma única etapa:

cursor --remote ssh-remote+mac-build /Users/builduser/projects/MyiOSApp

4. A Janela É Recarregada​

O Cursor recarrega em uma janela remota. A barra de status mostra o nome do host remoto. Todos os terminais, tarefas e servidores de linguagem agora são executados no Mac, enquanto a interface e a renderização permanecem locais.

5. As Extensões São Instaladas no Remoto​

Quando você abre um arquivo Swift, o Cursor solicita que você instale as extensões de linguagem no remoto. Aceite isso. A ferramenta Swift, os adaptadores de depuração e a extensão Swift pertencem ao Mac, não à sua máquina local.

Extensões locais vs remotas

Extensões apenas de interface, como temas, rodam localmente. Extensões que iniciam processos, incluindo o servidor de linguagem Swift e qualquer adaptador de depuração, rodam no Mac remoto. O Cursor gerencia a divisão para você se você deixar.

Editando Localmente e Compilando Remotamente​

O padrão produtivo é: editar no Cursor, executar os builds no terminal integrado, que é um terminal no Mac. Os arquivos sempre vivem no Mac, então não há etapa de sincronização para derivar.

Build Básico​

Compile um scheme para o destino genérico iOS, o que evita precisar de um dispositivo específico:

xcodebuild \
-project MyApp.xcodeproj \
-scheme MyApp \
-configuration Debug \
-destination 'generic/platform=iOS' \
build

Se você usa um workspace em vez de um project, troque -project por -workspace:

xcodebuild -workspace MyApp.xcworkspace -scheme MyApp -destination 'generic/platform=iOS' build

Arquivando para Release​

Arquivar produz um .xcarchive que a etapa de exportação converte em um .ipa para distribuição:

xcodebuild \
-workspace MyApp.xcworkspace \
-scheme MyApp \
-configuration Release \
-destination 'generic/platform=iOS' \
-archivePath build/MyApp.xcarchive \
archive

Exportando o Arquivo​

Após arquivar, exporte usando um plist de opções de exportação que descreve o método de distribuição:

xcodebuild -exportArchive \
-archivePath build/MyApp.xcarchive \
-exportPath build/export \
-exportOptionsPlist ExportOptions.plist

Um ExportOptions.plist para distribuição no estilo App Store se parece mais ou menos com isto:

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN"
"http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>method</key>
<string>app-store-connect</string>
<key>teamID</key>
<string>ABCDE12345</string>
<key>signingStyle</key>
<string>automatic</string>
<key>uploadSymbols</key>
<true/>
</dict>
</plist>
Prefira destinos genéricos no CI

Compilar para generic/platform=iOS evita vincular o build a um UDID específico de dispositivo ou simulador, o que faz com que o mesmo comando funcione em qualquer máquina de build sem mudanças de configuração.

Executando Testes e Lendo Logs​

Os testes são executados da mesma forma que os builds, apenas com a ação test e um destino concreto.

Executando Testes​

xcodebuild test \
-workspace MyApp.xcworkspace \
-scheme MyApp \
-destination 'platform=iOS Simulator,name=iPhone 16,OS=latest'

Tornando os Logs Legíveis​

A saída bruta do xcodebuild é enorme e difícil de ler. Envie-a por um formatador para reduzi-la a linhas legíveis:

# xcbeautify: modern, fast formatter
xcodebuild test -workspace MyApp.xcworkspace -scheme MyApp \
-destination 'platform=iOS Simulator,name=iPhone 16' | xcbeautify

# xcpretty: older but widely available alternative
xcodebuild test -workspace MyApp.xcworkspace -scheme MyApp \
-destination 'platform=iOS Simulator,name=iPhone 16' | xcpretty

Para um arquivo de log persistente mais um stream legível, use tee na saída:

xcodebuild build -scheme MyApp -destination 'generic/platform=iOS' \
2>&1 | tee build.log | xcbeautify

Como o terminal é remoto, o arquivo de log fica no Mac. Leia-o na mesma janela remota com o explorador de arquivos do Cursor, ou transmita-o localmente com uma segunda sessão SSH se preferir.

Capturando Capturas de Tela do Simulador​

Como o Simulator roda no Mac, você o captura com xcrun simctl por SSH em vez de tirar uma captura de tela local.

Liste e Inicie um Simulador​

# List available devices
xcrun simctl list devices available

# Boot a specific simulator by UDID or name
xcrun simctl boot "iPhone 16"

# Open the Simulator UI if a desktop session is available
open -a Simulator

Instale e Inicie Seu App​

# Install a built .app onto the booted simulator
xcrun simctl install booted ./build/Build/Products/Debug-iphonesimulator/MyApp.app

# Launch it by bundle identifier
xcrun simctl launch booted com.example.MyApp

Capture uma Captura de Tela​

# Save a screenshot to a file on the Mac
xcrun simctl io booted screenshot /Users/builduser/shots/home.png

Como esse arquivo está no remoto, abra-o diretamente na janela remota do Cursor. A visualização de imagens funciona por Remote-SSH, então você não precisa copiá-lo para baixo.

SIMuladores headless precisam de uma sessão

O simctl pode iniciar um simulador sem interface gráfica, mas alguns testes orientados por interface e cenários de captura de tela mais ricos esperam uma sessão de desktop com login ativo. Em um Mac headless, mantenha uma sessão persistente com login ativo ou espere limitações nos recursos de interface do Simulator.

Assinatura de Código​

A assinatura é a parte mais propensa a falhar em uma máquina nova, então configure-a com cuidado.

Certificados no Keychain​

As identidades de assinatura vivem no keychain do usuário de build. Importe um .p12 exportado da sua conta de desenvolvedor Apple:

security import /path/to/distribution.p12 \
-k /Users/builduser/Library/Keychains/login.keychain-db \
-P "p12-password" \
-T /usr/bin/codesign \
-T /usr/bin/security

As flags -T concedem acesso do codesign e do security à identidade importada, para que os builds não parem em um prompt de acesso.

Perfis de Provisionamento​

Coloque os perfis onde o Xcode espera encontrá-los para o usuário de build:

mkdir -p /Users/builduser/Library/MobileDevice/Provisioning\ Profiles
cp ~/profiles/MyApp_AppStore.mobileprovision \
/Users/builduser/Library/MobileDevice/Provisioning\ Profiles/

Assinatura Automática vs Manual​

  • Assinatura automática: mais simples, exige que o usuário de build esteja conectado a uma conta de desenvolvedor Apple e tenha acesso de rede aos serviços da Apple. Boa para um único Mac confiável.
  • Assinatura manual: mais reproduzível para CI. Você especifica CODE_SIGN_STYLE=Manual e aponta para uma identidade e um perfil específicos, evitando comportamento dependente de conta.
xcodebuild archive \
-workspace MyApp.xcworkspace \
-scheme MyApp \
-destination 'generic/platform=iOS' \
CODE_SIGN_STYLE=Manual \
CODE_SIGN_IDENTITY="Apple Distribution: Example Corp (ABCDE12345)" \
PROVISIONING_PROFILE_SPECIFIER="MyApp AppStore Profile" \
-archivePath build/MyApp.xcarchive
Desbloqueie o keychain no CI

Em um Mac sem supervisão, desbloqueie o keychain de login antes de compilar, para que o codesign consiga alcançar a identidade sem um prompt gráfico. Mantenha a senha de desbloqueio em um cofre de segredos, nunca em um script commitado.

Comparação de Custos​

Os preços mudam, então trate o que segue como faixas aproximadas para planejamento, e não como cotações atuais.

OpçãoCusto mensal aproximadoObservações
Possuir um Mac mini como servidor de buildCompra do hardware e depois quase zero recorrenteMais barato em uso constante; você cuida da manutenção
Provedor de Mac na nuvem (MacStadium, EC2 Mac)Por hora, muitas vezes algumas centenas por mês em uso 24/7Escala para cima ou para baixo; sobrecarga de provisionamento
Runners macOS de CI na nuvemPor minuto, cobrado apenas durante o buildMais barato para builds ocasionais; partidas a frio atrapalham
rsync / git para um Mac compartilhadoApenas o custo do MacBom para indivíduos, desajeitado para equipes

Um Mac mini dedicado normalmente vence para uma equipe pequena que compila ao longo do dia. O CI na nuvem vence quando os builds são infrequentes ou estritamente na hora do release. Instâncias Mac na nuvem vencem quando você precisa de capacidade sob demanda e não quer possuir hardware.

Armadilhas Comuns​

A maior parte da dor do build remoto vem de um pequeno conjunto de erros recorrentes. Verifique estes antes de depurar qualquer coisa exótica.

Licença Não Aceita​

O xcodebuild falha imediatamente com um erro de licença. Corrija uma vez com:

sudo xcodebuild -license accept

Versão Errada do Xcode Selecionada​

Múltiplas instalações do Xcode confundem a cadeia de ferramentas. Selecione a pretendida explicitamente:

sudo xcode-select -s /Applications/Xcode-16.app/Contents/Developer
xcodebuild -version

Identidade de Assinatura Ausente​

Um build que arquiva mas falha ao exportar geralmente não tem identidade ou perfil correspondente. Confirme o que o keychain realmente contém:

security find-identity -v -p codesigning

Se a lista estiver vazia para a identidade de que você precisa, importe o .p12 novamente e verifique se o perfil corresponde ao bundle identifier do app.

Sincronização Lenta​

Se você estiver sincronizando código em vez de editar por SSH, prefira git a rsync. O git transfere deltas e dá histórico e tratamento de conflitos; o rsync move árvores inteiras e deriva em silêncio. Quando você edita diretamente por Remote-SSH, não há sincronização alguma, que é a opção mais rápida.

Quedas de Rede Quebrando o SSH​

Builds longos em um link instável podem derrubar a conexão e matar o build. Mitigue com keep-alives de SSH no seu config e execute builds longos dentro de tmux ou screen no Mac para que sobrevivam a uma desconexão:

tmux new-session -s build 'xcodebuild archive -workspace MyApp.xcworkspace -scheme MyApp -destination "generic/platform=iOS" -archivePath build/MyApp.xcarchive'

Reconecte depois e reanexe com tmux attach -t build.

Simulador Não Rodando em Modo Headless​

Alguns fluxos de teste presumem um Simulator visível. Em um Mac headless, inicie o simulador com simctl e mantenha uma sessão de desktop com login ativo ou aceite cobertura reduzida da interface do Simulator.

Envolva o build em tmux desde o primeiro dia

Se você compila por uma rede que não controla totalmente, execute todo xcodebuild longo dentro de tmux. O build continua rodando no Mac independentemente de sua sessão SSH sobreviver, e você pode reanexar para assistir à conclusão.

Builds remotos transformam uma restrição rígida de plataforma em uma escolha de fluxo de trabalho. Escolha a arquitetura que se encaixa na sua equipe, configure o Mac uma vez e deixe o Remote-SSH do Cursor manter a experiência de edição rápida enquanto o Mac faz o trabalho que o macOS exige.