Aller au contenu principal

Développement iOS avec des serveurs de build Mac distants

iOS remote Mac build setup with Cursor

Compiler une app iOS nécessite macOS quelque part dans la boucle, mais cela ne signifie pas que votre éditeur doit tourner sur un Mac. Grâce au support Remote-SSH de Cursor, vous pouvez conserver une expérience d'édition locale rapide sous Windows ou Linux pendant qu'une machine Mac gère la compilation, la signature et l'exécution des simulateurs. Ce guide parcourt les options d'architecture, la configuration, les commandes de build et les pièges qui font perdre le plus de temps.

Pourquoi un Mac est nécessaire​

La chaîne d'outils d'Apple ne tourne que sur macOS, et plusieurs parties du flux de travail iOS en dépendent directement.

  • Signature de code : les identités de signature vivent dans le trousseau macOS et servent à produire des artefacts signés.
  • Profils de provisionnement : les profils qui lient un ID d'app aux appareils et certificats sont gérés via l'outillage Apple.
  • Simulateur iOS : le simulateur est une application macOS ; il n'existe aucun équivalent Linux ou Windows pris en charge pour une fidélité d'appareil réelle.
  • Chaîne d'outils xcodebuild : le compilateur, l'éditeur de liens et les SDK sont livrés dans Xcode sur macOS.
  • Soumission à l'App Store : téléverser vers App Store Connect nécessite l'outillage Apple, généralement via Transporter ou altool/notarytool sur macOS.

Comme tout cela est lié à macOS, la question pratique n'est pas de savoir si vous avez besoin d'un Mac, mais où ce Mac se trouve.

Options d'architecture​

Il n'y a pas de réponse unique idéale ; le bon choix dépend de la taille de votre équipe, de votre budget et de la fréquence à laquelle vous compilez.

OptionFonctionnementAvantagesInconvénients
Remote-SSH vers un MacCursor se connecte via SSH ; les fichiers et le terminal vivent sur le MacSource de vérité unique, accès complet à la chaîne d'outils, vrai simulateurNécessite un Mac persistant et joignable
rsync / git push vers un MacÉditez localement, poussez vers le Mac, compilez là-basFonctionne hors ligne pour l'édition, modèle mental simpleDérive de synchronisation, gros transferts lents, pas de LSP en direct
Runners macOS de CI cloudPoussez vers Git, la CI compile sur un runner macOS hébergéAucun matériel à posséder, reproductible, bon pour les versionsNon interactif, coût à la minute, démarrages à froid
Fournisseurs de Mac cloudLouez un Mac (MacStadium, AWS EC2 Mac) via SSHMatériel réel à la demande, évolutifCoût horaire, surcharge de provisionnement et de démontage

Pour le développement au quotidien, Remote-SSH vers un Mac est généralement le meilleur équilibre. La CI cloud convient aux pipelines de version. Les fournisseurs de Mac cloud conviennent aux équipes qui veulent une flotte sans posséder les postes.

Adaptez l'outil à la tâche

Utilisez Remote-SSH pour le développement interactif, la CI cloud pour les versions et les instances Mac cloud pour l'échelle ou les courtes rafales. La plupart des équipes matures en utilisent au moins deux plutôt que de forcer une seule option à tout faire.

Configurer un Mac distant​

Ces étapes s'exécutent une fois sur le Mac qui servira de serveur de build. Vous avez besoin de droits d'administration et d'une connexion réseau stable.

1. Installer Xcode et les Command Line Tools​

Installez Xcode depuis le Mac App Store, puis installez les command line tools pour que xcodebuild et les binaires associés soient disponibles :

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. Accepter la licence​

Sur une installation fraîche, xcodebuild refuse de s'exécuter tant que la licence n'est pas acceptée :

sudo xcodebuild -license accept

3. Créer un utilisateur de build​

Évitez de compiler avec un compte personnel. Créez un utilisateur dédié afin que les identifiants et les trousseaux restent isolés :

# 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. Activer la connexion à distance​

Activez SSH pour que le Mac accepte les connexions :

sudo systemsetup -setremotelogin on

5. Installer la clé publique SSH​

Ajoutez votre clé publique locale au fichier authorized_keys de l'utilisateur de build afin de vous connecter sans mot de passe :

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
Verrouillez le compte SSH

Un Mac de build exposé à Internet ne doit pas autoriser les connexions par mot de passe pour l'utilisateur de build. Désactivez l'authentification par mot de passe, restreignez les adresses sources autorisées là où c'est possible, et gardez la clé privée uniquement sur votre machine locale.

Connecter Cursor via Remote-SSH​

Une fois le Mac joignable, connectez Cursor à celui-ci. Le distant devient l'environnement où les extensions, les terminaux et les builds s'exécutent.

1. Installer l'extension Remote-SSH​

Ouvrez Cursor, allez dans la vue Extensions (Ctrl+Shift+X) et installez Remote - SSH (l'extension packagée par Microsoft est disponible dans le marketplace de Cursor).

2. Ajouter une entrée de configuration SSH​

Modifiez votre configuration SSH locale, généralement à ~/.ssh/config, et ajoutez un alias d'hôte :

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

Les paramètres keep-alive réduisent les déconnexions sur les réseaux instables. Notez que l'OpenSSH natif de macOS ne prend pas en charge mosh, donc les keep-alives sont votre première ligne de défense.

3. Se connecter​

Ouvrez la palette de commandes (Ctrl/Cmd+Shift+P) et exécutez Remote-SSH: Connect to Host, puis choisissez mac-build. Vous pouvez aussi l'exécuter en une seule étape :

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

4. La fenêtre se recharge​

Cursor se recharge dans une fenêtre distante. La barre d'état affiche le nom de l'hôte distant. Tous les terminaux, tâches et serveurs de langage s'exécutent désormais sur le Mac, tandis que l'interface et le rendu restent locaux.

5. Les extensions s'installent sur le distant​

Lorsque vous ouvrez un fichier Swift, Cursor vous invite à installer les extensions de langage sur le distant. Acceptez-le. L'outillage Swift, les adaptateurs de débogage et l'extension Swift appartiennent au Mac, pas à votre machine locale.

Extensions locales et distantes

Les extensions purement d'interface comme les thèmes s'exécutent localement. Les extensions qui lancent des processus, notamment le serveur de langage Swift et tout adaptateur de débogage, s'exécutent sur le Mac distant. Cursor gère la séparation pour vous si vous le laissez faire.

Éditer localement et compiler à distance​

Le schéma productif est : éditez dans Cursor, lancez les builds dans le terminal intégré, qui est un terminal sur le Mac. Les fichiers vivent toujours sur le Mac, donc il n'y a aucune étape de synchronisation susceptible de dériver.

Build de base​

Compilez un scheme pour la destination iOS générique, ce qui évite d'avoir besoin d'un appareil spécifique :

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

Si vous utilisez un workspace au lieu d'un projet, remplacez -project par -workspace :

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

Archiver pour la version​

L'archivage produit un .xcarchive que l'étape d'export convertit en .ipa pour la distribution :

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

Exporter l'archive​

Après l'archivage, exportez à l'aide d'un plist d'options d'export qui décrit la méthode de distribution :

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

Un ExportOptions.plist pour une distribution de style App Store ressemble à peu près à ceci :

<?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>
Préférez les destinations génériques en CI

Compiler pour generic/platform=iOS évite de lier le build à un appareil ou un UDID de simulateur spécifique, ce qui fait que la même commande fonctionne sur n'importe quelle machine de build sans changement de configuration.

Exécuter les tests et lire les logs​

Les tests s'exécutent de la même manière que les builds, avec simplement l'action test et une destination concrète.

Exécuter les tests​

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

Rendre les logs lisibles​

La sortie brute de xcodebuild est énorme et difficile à parcourir. Faites-la passer par un formateur pour la condenser en lignes lisibles :

# 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

Pour un fichier de log persistant en plus d'un flux lisible, utilisez tee sur la sortie :

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

Comme le terminal est distant, le fichier de log atterrit sur le Mac. Lisez-le depuis la même fenêtre distante avec l'explorateur de fichiers de Cursor, ou diffusez-le localement avec une seconde session SSH si vous préférez.

Prendre des captures d'écran du simulateur​

Comme le simulateur s'exécute sur le Mac, vous le capturez avec xcrun simctl via SSH plutôt qu'en prenant une capture d'écran locale.

Lister et démarrer un simulateur​

# 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

Installer et lancer votre 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

Capturer une capture d'écran​

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

Comme ce fichier est sur le distant, ouvrez-le directement dans la fenêtre distante de Cursor. L'aperçu d'image fonctionne via Remote-SSH, donc vous n'avez pas besoin de le copier en local.

Les simulateurs headless ont besoin d'une session

simctl peut démarrer un simulateur sans interface graphique, mais certains tests pilotés par l'interface et scénarios de capture plus riches attendent une session de bureau connectée. Sur un Mac headless, gardez une session connectée persistante, ou attendez-vous à des limitations sur les fonctionnalités d'interface du simulateur.

Signature de code​

La signature est la partie la plus susceptible d'échouer sur une machine fraîche, donc configurez-la délibérément.

Certificats dans le trousseau​

Les identités de signature vivent dans le trousseau de l'utilisateur de build. Importez un .p12 exporté depuis votre compte développeur 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

Les options -T accordent à codesign et security l'accès à l'identité importée afin que les builds ne se bloquent pas sur une invite d'accès.

Profils de provisionnement​

Placez les profils là où Xcode les attend pour l'utilisateur de build :

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

Signature automatique vs manuelle​

  • Signature automatique : plus simple, exige que l'utilisateur de build soit connecté à un compte développeur Apple et dispose d'un accès réseau aux services d'Apple. Bon pour un seul Mac de confiance.
  • Signature manuelle : plus reproductible pour la CI. Vous spécifiez CODE_SIGN_STYLE=Manual et pointez vers une identité et un profil précis, en évitant un comportement dépendant du compte.
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
Déverrouillez le trousseau en CI

Sur un Mac non surveillé, déverrouillez le trousseau de connexion avant de compiler afin que codesign puisse atteindre l'identité sans invite d'interface. Gardez le mot de passe de déverrouillage dans un coffre de secrets, jamais dans un script commité.

Comparaison des coûts​

Les prix changent, donc considérez ce qui suit comme des ordres de grandeur pour la planification plutôt que des devis actuels.

OptionCoût mensuel approximatifRemarques
Posséder un Mac mini comme serveur de buildAchat du matériel, puis quasi zéro en récurrentLe moins cher à usage régulier ; vous assurez la maintenance
Fournisseur de Mac cloud (MacStadium, EC2 Mac)Horaire, souvent plusieurs centaines par mois en usage 24/7Évolue à la hausse ou à la baisse ; surcharge de provisionnement
Runners macOS de CI cloudÀ la minute, facturé uniquement pendant le buildLe moins cher pour des builds occasionnels ; les démarrages à froid pèsent
rsync / git vers un Mac partagéCoût du Mac uniquementCorrect pour un individu, peu pratique pour des équipes

Un Mac mini dédié gagne généralement pour une petite équipe qui compile toute la journée. La CI cloud gagne lorsque les builds sont peu fréquents ou strictement au moment des versions. Les instances Mac cloud gagnent lorsque vous avez besoin de capacité à la demande et que vous ne voulez pas posséder le matériel.

Pièges courants​

L'essentiel des difficultés de build distant provient d'un petit ensemble d'erreurs récurrentes. Vérifiez-les avant de déboguer quoi que ce soit d'exotique.

Licence non acceptée​

xcodebuild échoue immédiatement avec une erreur de licence. Corrigez-la une fois pour toutes avec :

sudo xcodebuild -license accept

Mauvaise version de Xcode sélectionnée​

Plusieurs installations de Xcode perturbent la chaîne d'outils. Sélectionnez explicitement celle voulue :

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

Identité de signature manquante​

Un build qui s'archive mais échoue à l'export n'a généralement aucune identité ou profil correspondant. Confirmez ce que le trousseau contient réellement :

security find-identity -v -p codesigning

Si la liste est vide pour l'identité dont vous avez besoin, réimportez le .p12 et vérifiez que le profil correspond à l'identifiant de bundle de l'app.

Synchronisation lente​

Si vous synchronisez du code plutôt que d'éditer via SSH, préférez git à rsync. Git transfère des deltas et vous donne l'historique et la gestion des conflits ; rsync déplace des arborescences entières et dérive silencieusement. Lorsque vous éditez directement via Remote-SSH, il n'y a aucune synchronisation, ce qui est l'option la plus rapide.

Coupures réseau cassant SSH​

Les builds longs sur une liaison instable peuvent faire tomber la connexion et tuer le build. Atténuez avec des keep-alives SSH dans votre configuration, et lancez les builds longs dans tmux ou screen sur le Mac pour qu'ils survivent à une déconnexion :

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

Reconnectez-vous plus tard et rattachez-vous avec tmux attach -t build.

Simulateur non fonctionnel en headless​

Certains flux de test supposent un simulateur visible. Sur un Mac headless, démarrez le simulateur avec simctl, et soit gardez une session de bureau connectée, soit acceptez une couverture réduite de l'interface du simulateur.

Enveloppez le build dans tmux dès le premier jour

Si vous compilez sur un réseau que vous ne contrôlez pas entièrement, exécutez chaque xcodebuild long dans tmux. Le build continue de tourner sur le Mac que votre session SSH survive ou non, et vous pouvez vous rattacher pour le regarder se terminer.

Les builds distants transforment une contrainte de plateforme dure en un choix de flux de travail. Choisissez l'architecture adaptée à votre équipe, configurez le Mac une fois, et laissez le Remote-SSH de Cursor garder une expérience d'édition rapide pendant que le Mac fait le travail qu'exige macOS.