Le dépôt du projet approche les 40 Go, et Pods, node_modules et DerivedData réunis pèsent plus lourd que le code source lui-même. Vous ouvrez Xcode en local, avec le projet monté via un partage SMB sur un Mac cloud distant, et voilà que l'indexation tourne pendant plus d'une minute, et changer de branche se bloque au point de vous faire croire à une coupure réseau — c'est le premier mur que rencontrent beaucoup de développeurs dès qu'ils déplacent leur environnement de travail sur un Mac cloud. Monter un partage réseau semble pratique au premier abord, mais l'usage révèle vite que les modèles d'accès aux fichiers des gros projets iOS sont naturellement incompatibles avec les systèmes de fichiers réseau. Cet article détaille comment, sur les nœuds HireVPS, nous avons mis en place une synchronisation bidirectionnelle locale/distante avec Mutagen pour retrouver une expérience proche du local.
Pourquoi le montage d'un partage réseau ne convient pas aux gros projets iOS
Les systèmes de fichiers réseau comme NFS ou SMB partent du principe d'un « accès occasionnel à de gros fichiers ». Or l'indexation de Xcode, SourceKit et le scan du cache CocoaPods font exactement l'inverse : ils effectuent en peu de temps des appels stat, open et close sur des dizaines de milliers de petits fichiers. Chaque opération implique un aller-retour réseau — même avec seulement 20 ms de latence, multiplié par des dizaines de milliers d'appels, cela se traduit par des blocages de plusieurs minutes. Pire encore : la gestion des coupures réseau. Le point de montage se figure littéralement en cas d'instabilité de la connexion, bloquant à la fois le terminal et l'éditeur, ce qui oblige à démonter puis remonter de force.
Comparons trois approches courantes pour mieux visualiser la différence :
| Solution | Réactivité en temps réel | Comportement avec de nombreux petits fichiers | Reprise après coupure | Cas d'usage adapté |
|---|---|---|---|---|
| Montage SMB/NFS | Forte (toujours à jour) | Mauvais, aller-retour réseau par fichier | Mauvais, se bloque facilement | Lecture/écriture occasionnelle de gros fichiers |
| Tâche planifiée rsync | Faible, avec fenêtre de synchronisation | Correct, transfert par lots efficace | Bon, il suffit de relancer la tâche | Sauvegardes régulières, push unidirectionnel |
| Synchronisation bidirectionnelle Mutagen | Quasi temps réel | Bon, cache local + transfert incrémental | Bon, reconnexion et reprise automatiques | Collaboration bidirectionnelle en développement continu |
Une leçon apprise à nos dépens : par simplicité, nous avons d'abord simplement monté le partage, et l'étape « Building workspace » de Xcode a pris en moyenne 15 à 25 secondes de plus. La seule attente quotidienne liée à l'indexation représentait déjà une perte de temps notable pour l'équipe ; après le passage à Mutagen, cette attente a quasiment disparu.
Mettre en place une session de synchronisation bidirectionnelle avec Mutagen
Installation et première connexion
Mutagen se présente sous la forme d'un binaire unique ; aucun service supplémentaire n'est nécessaire en permanence, ni en local ni sur le Mac cloud — l'agent est automatiquement déployé côté distant lors de l'établissement de la connexion. Installez-le d'abord en local :
brew install mutagen-io/mutagen/mutagen
mutagen version
Supposons que SSH soit déjà activé sur votre Mac cloud, avec les identifiants fournis dans l'e-mail de mise en service. Créez une session de synchronisation :
mutagen sync create \
--name=ios-app \
--ignore-vcs \
--symlink-mode=posix-raw \
/Users/me/Projects/ios-app \
ssh://devuser@your-cloud-mac-host/Users/devuser/Projects/ios-app
--ignore-vcs permet de sauter automatiquement les objets internes de .git (Git se synchronise mieux via son propre protocole, inutile que Mutagen refasse le travail), et --symlink-mode=posix-raw conserve tels quels les liens symboliques courants dans CocoaPods, sans les convertir.
Règles d'exclusion : les répertoires à ne jamais synchroniser
Il faut absolument créer un fichier .mutagenignore à la racine du projet, sinon la première synchronisation transférera aussi plusieurs Go de répertoires de cache :
DerivedData/
Pods/
.build/
node_modules/
*.xcuserstate
xcuserdata/
.DS_Store
Le point commun de ces répertoires est qu'ils peuvent être régénérés localement de chaque côté. Les synchroniser gaspille de la bande passante et peut en plus provoquer les problèmes décrits dans la section suivante.
Quelques pièges liés à Xcode
- DerivedData ne doit jamais être synchronisé : le cache de modules (ModuleCache.noindex) et l'index de build sont étroitement liés aux chemins absolus et à l'architecture de la machine. Les synchroniser vers une autre machine revient pratiquement à forcer une réindexation complète, et peut même provoquer des erreurs de compilation incompréhensibles dans les cas graves. Laissez chaque côté (local et cloud) le régénérer indépendamment — de toute façon, c'est un artefact censé être jetable.
- Installez Pods et node_modules séparément de chaque côté : ce qui doit réellement être synchronisé, c'est
Podfile.locketpackage-lock.jsondans le code source. Les répertoires de dépendances eux-mêmes doivent être générés séparément viapod installetnpm installde chaque côté, ce qui économise de la bande passante et évite les incompatibilités binaires entre architectures. - Gardez
.gitignoreet.mutagenignorecohérents : si les deux listes d'exclusion se contredisent, Git affichera « aucune modification » alors que Mutagen transférera en arrière-plan un tas de fichiers inutiles — le diagnostic devient alors très chronophage. Il est conseillé d'écrire un petit script qui compare les deux listes après chaque modification. - Différences de bits de permission : comme l'UID du compte sur le Mac cloud diffère de celui du compte local, en cas d'erreur de permission, vérifiez d'abord si la synchronisation a fait perdre le bit d'exécution.
--permissions-mode=portablerésout la plupart de ces cas.
Performances observées et gestion des conflits
Au quotidien, une synchronisation incrémentale déclenchée par un enregistrement (modification de quelques fichiers Swift) se termine généralement en une à deux secondes — une sensation proche d'un enregistrement local. La toute première synchronisation complète (des dizaines de milliers de fichiers sources), qui doit parcourir l'arborescence entière, est nettement plus lente ; il est recommandé de la lancer avant de commencer à travailler, plutôt que d'attendre en codant en parallèle.
Côté gestion des conflits, le mode bidirectionnel par défaut est two-way-safe : en cas de conflit, rien n'est écrasé automatiquement, la synchronisation s'arrête et attend une intervention manuelle. C'est plus sûr qu'un mode qui force l'écrasement, mais cela entraîne des pauses fréquentes si les deux côtés modifient le même fichier en même temps. Pour un travail d'équipe, on recommande plutôt :
mutagen sync create \
--name=ios-app \
--sync-mode=two-way-resolved \
--default-file-mode-alpha=0644 \
/Users/me/Projects/ios-app \
ssh://devuser@your-cloud-mac-host/Users/devuser/Projects/ios-app
two-way-resolved fait gagner par défaut le côté local (alpha) en cas de conflit. Combiné à la règle « une seule personne modifie le code d'un côté à la fois », cela évite pratiquement toute perte de modification. En cas de véritable conflit sur des ressources binaires (images, polices), mutagen sync list liste les chemins concernés ; il suffit alors de comparer manuellement les horodatages et les tailles de fichiers pour décider laquelle conserver.
Une configuration prête à copier
Mutagen prend en charge un fichier de configuration au niveau du projet. En le plaçant sous le nom mutagen.yml à la racine du projet, chaque membre de l'équipe peut lancer la synchronisation d'une seule commande après avoir cloné le dépôt, sans avoir à retaper tous les paramètres :
sync:
ios-app:
alpha: "."
beta: "ssh://devuser@your-cloud-mac-host/Users/devuser/Projects/ios-app"
mode: "two-way-resolved"
ignore:
vcs: true
paths:
- "DerivedData"
- "Pods"
- "node_modules"
- "xcuserdata"
- ".build"
symlink:
mode: "posix-raw"
Voici les commandes courantes à connaître pour l'usage quotidien :
mutagen sync list
mutagen sync monitor ios-app
mutagen sync pause ios-app
mutagen sync resume ios-app
mutagen sync terminate ios-app
monitor est particulièrement utile pour comprendre pourquoi une modification n'a pas été synchronisée : on voit en temps réel à quelle étape (scan, mise en staging, transfert) le processus est bloqué.
Liste de vérification avant mise en production
.mutagenignoreet.gitignorecouvrent-ils bien les répertoires régénérables comme DerivedData, Pods, node_modules et xcuserdata ?- Le mode de synchronisation choisi est-il
two-way-safeoutwo-way-resolved? Pour un travail d'équipe, privilégiez le second et convenez à l'avance de qui a la priorité en cas de conflit. - La première synchronisation complète a-t-elle été effectuée en dehors de toute tâche urgente ? Le scan initial de dizaines de milliers de fichiers ne se prête pas à un travail simultané.
- Le modèle précis du Mac cloud et le nœud choisi ont-ils été vérifiés dans la console pour confirmer la configuration disponible ? La bande passante et les caractéristiques du disque influencent la vitesse maximale de synchronisation.
mutagen sync monitorest-il déjà devenu une habitude de diagnostic, plutôt qu'un réflexe tardif après la perte de fichiers ?
En passant en revue ces points, l'expérience d'édition sur un Mac cloud se rapproche fortement du local, et l'équipe ne se renverra plus la faute pour savoir « à qui appartient la modification non synchronisée ».
Questions fréquentes
Pourquoi ne pas simplement monter le Mac cloud en SMB ou NFS ?
Un montage réseau transforme chaque accès à un petit fichier (indexation Xcode, recherche Pods) en aller-retour réseau ; ouvrir un gros projet peut alors prendre plusieurs dizaines de secondes. Mutagen garde un cache local et synchronise par incréments, la réactivité de l'éditeur reste proche du local et une coupure réseau ne gèle pas le système de fichiers.
Faut-il synchroniser aussi DerivedData ?
Non. Le cache de modules et les index de DerivedData sont liés à l'architecture de la machine et à des chemins absolus ; les synchroniser déclenche presque toujours une réindexation complète ou des erreurs de build. Ajoutez ce dossier à .mutagenignore et laissez chaque machine le régénérer de son côté.
Que faire en cas de conflit en synchronisation bidirectionnelle ?
Utilisez mutagen sync list pour repérer les chemins en conflit. Pour le code source, le mode two-way-resolved avec priorité au côté local (alpha) suffit dans la plupart des cas ; pour les ressources binaires, comparez manuellement avant de trancher. Lancer git status avant chaque session de synchronisation évite déjà la majorité des conflits.
HireVPS
Essayez dès aujourd'hui un Mac mini dédié dans le cloud
Location à la journée, identifiants SSH/VNC en 2 minutes, configuration évolutive à tout moment.