VPSSpark Blog
← Retour au journal dev

Optimiser le build iOS CI : GitHub Actions + Xcode 28→9 min

Notes serveur · 2026.06.06 · ~10 min

Développeur consultant les résultats d'optimisation du build iOS CI
28 minutes par push semblaient normales—jusqu'à ce qu'on décompose la timeline.

Dans un projet iOS classique avec GitHub Actions et Xcode, une durée de build CI iOS élevée est souvent prise pour une fatalité. En réalité : iOS CI slow ≠ Xcode slow — la cause est une pipeline sans état, sans cache et conçue en série.

Cet article s'appuie sur un cas réel (équipe iOS de 8 personnes, 460 000 lignes Swift, 28 Pods, 14 packages SPM, 30+ push/jour) : passage de GitHub Actions macOS CI de 28 à 9 minutes, avec un plan complet d'iOS CI optimization.

28→9
Temps de build moyen (min)
68%
Réduction totale
74%
grâce au cache + parallélisation

1. Cadrage : pourquoi la CI iOS est-elle lente ?

Beaucoup d'équipes acceptent 20–30 minutes d'attente. Le goulot n'est pas Xcode, mais l'architecture de la pipeline CI.

1.1 Le problème structurel de GitHub Actions CI

Les runners macOS GitHub Actions sont un environnement éphémère (ephemeral environment). Chaque exéction implique :

  • ❌ Perte de DerivedData — pas de build incrémental
  • ❌ CocoaPods réinstallé à chaque run
  • ❌ SPM re-résolu à chaque run
  • ❌ Impossible de réutiliser le contexte de build

Résultat : chaque run est un cold build.

1.2 Décomposition de la baseline (28 minutes)

Lancement à froid via workflow_dispatch, 3 runs, médiane par phase :

PhaseDuréeType
Checkout45sfixe
pod install3m 12sréseau
SPM resolve1m 44srésolution
xcodebuild build11m 08scompilation à froid
xcodebuild test6m 22sCPU
xcodebuild archive5m 30sséquentiel bloquant
upload/sign1m 05sfixe
Total29m 46s

Goulots clés : trois causes d'une CI iOS lente

  1. Cold Build (39 %) — sans DerivedData, 460 000 lignes Swift recompilées à chaque run
  2. Dependency Re-resolve (17 %) — Pods et SPM retéléchargés et re-résolus à chaque fois
  3. Serial Pipeline (19 %) — test et archive enchaînés sans dépendance réelle

2. Objectif et résultats

Résultats d'optimisation (vue d'ensemble iOS CI optimization)

ÉtapeTemps de build
baseline28 min
+ cache20 min
+ parallèle12 min
+ Apple Silicon9 min

Répartition des gains

  • Cache : 42 % (environ 8 minutes)
  • Parallélisation : 32 % (environ 6 minutes)
  • Apple Silicon : 26 % (environ 3 minutes)

Contexte du cas (E-E-A-T)

ParamètreValeur
Code Swift~460 000 lignes (tests inclus)
CocoaPods / SPM28 Pods + 14 packages
Runner baselineGitHub macos-latest (Intel, 4 cœurs)
Équipe / fréquence8 personnes, 30+ push/jour
Méthodehorodatage par step, 3 runs à froid, médiane

Tests à variable unique (10 runs par config, médiane) : cache seul −29 %, parallélisation seule −21 %, Apple Silicon seul −18 %. L'optimisation structurelle (cache + parallèle) apporte 74 % — le matériel fixe le plafond, ce n'est pas le premier levier. p95 passé de 33 à 12 min, taux de hit DerivedData à 80 %.

3. Levier 1 : cache CI (gain maximal)

3.1 Pourquoi le cache est central

Le cœur de l'iOS CI optimization : réactiver le build incrémental Xcode en CI. Il faut persister DerivedData, le cache CocoaPods et le cache Swift Package Manager.

3.2 Trois répertoires à cacher

  • DerivedData : ~/Library/Developer/Xcode/DerivedData
  • Cache CocoaPods : ~/Library/Caches/CocoaPods
  • Cache SPM : ~/.spm-cache

3.3 Configuration GitHub Actions

.github/workflows/ios-ci.yml (DerivedData + CocoaPods + SPM)
- name: Cache DerivedData
                  uses: actions/cache@v4
                  with:
                    path: ~/Library/Developer/Xcode/DerivedData
                    key: deriveddata-${{ runner.os }}-${{ hashFiles('**/Podfile.lock','**/Package.resolved') }}

                - name: Cache CocoaPods
                  uses: actions/cache@v4
                  with:
                    path: ~/Library/Caches/CocoaPods
                    key: pods-${{ runner.os }}-${{ hashFiles('**/Podfile.lock') }}

                - name: Cache SPM
                  uses: actions/cache@v4
                  with:
                    path: ~/.spm-cache
                    key: spm-${{ runner.os }}-${{ hashFiles('**/Package.resolved') }}

3.4 Conception des clés de cache

Le taux de hit détermine si le DerivedData cache est réellement efficace. Trois stratégies courantes :

Stratégie de cléTaux de hitProblème
runner.os seul~100 %Pods obsolètes possibles, artefacts incohérents
hash des lockfiles (recommandé)~80 %hit si dépendances inchangées, rebuild uniquement à la mise à jour
SHA de commit inclus~0 %miss à chaque push — cache inutile

Recommandation : hash de Podfile.lock / Package.resolved comme clé principale, plus restore-keys pour le préfixe. En cas de miss principal, le dernier cache valide reste disponible — build incrémental plutôt que cold build total. En CI, utiliser pod install --no-repo-update pour ne pas invalider le cache en réécrivant les lockfiles.

Voir la documentation GitHub Actions cache ; comparaison cache distant vs. disque local dans DerivedData / Pods / sccache comparés.

3.5 Impact du cache

Étapeavantaprès
pod install3m 12s50s
SPM resolve1m 44s15s
build11m3–4m

8 à 10 minutes gagnées en moyenne.

4. Levier 2 : parallélisation CI (pipeline optimization)

4.1 Le problème

CI par défaut : build → test → archive (séquentiel). Or test et archive n'ont pas de dépendance — ils peuvent tourner en parallèle.

4.2 Jobs parallèles

.github/workflows/ios-ci.yml (test / archive en parallèle)
jobs:
                  build-and-test:
                    runs-on: macos
                    steps:
                      - run: xcodebuild build test

                  archive:
                    runs-on: macos
                    if: github.ref == 'refs/heads/main'
                    steps:
                      - run: xcodebuild archive

Archive uniquement sur main ; chaque job restaure son cache indépendamment. Certificats via Fastlane Match. Isolation des ressources : 3 Cloud Mac pour 500 builds iOS CI par jour.

4.3 Impact

ModeTemps
séquentiel20 min
parallèle12 min

5 à 7 minutes gagnées.

5. Levier 3 : runner Apple Silicon

5.1 Pourquoi le matériel vient en dernier

Le matériel n'est pas le premier levier contre une CI iOS lente. Sans cache, Apple Silicon n'apporte qu'environ 18 % ; après optimisation structurelle, on passe de 12 à 9 minutes.

5.2 Gains mesurés (cache + parallèle actifs)

PhaseIntelApple Silicon
build3m 40s1m 55s
test6m 22s3m 48s
archive5m 30s3m 10s

5.3 Conclusion

3 à 5 minutes supplémentaires. Apple Silicon accélère surtout la compilation Swift et le linking — voir la documentation Apple sur les builds incrémentaux.

5.4 Comparaison de trois options de runner macOS

Après optimisation structurelle, les équipes choisissent généralement entre trois approches GitHub Actions self-hosted runner macOS :

OptionTemps de buildFile d'attenteCoût de maintenance
GitHub macos-latest~20 minélevée (pics : 10–20 min d'attente)nul
Mac mini dédié (Intel)~14 minaucuneélevé
Cloud Mac (Apple Silicon)~9 minaucunefaible

Les runners hébergés GitHub sont gratuits, mais la file d'attente des runners macOS devient un goulot ; le cache est limité à 10 Go/repo — les gros projets subissent des évictions fréquentes. Le self-hosted supprime la file, mais l'Intel plafonne. À 30+ push/jour, un nœud Apple Silicon dédié offre souvent le meilleur ROI : optimisez la structure d'abord, évaluez matériel et file ensuite.

6. Arbre de décision iOS CI optimization

iOS CI pipeline optimization checklist
CI > 15 min?
                  ↓
                cold build > 40%?
                  → YES: enable cache (DerivedData + Pods + SPM)
                  ↓
                pipeline serial?
                  → YES: split jobs (test / archive)
                  ↓
                still slow?
                  → upgrade Apple Silicon runner

Runbook reproductible (5 étapes) :

  1. Établir la baseline : echo "::notice::$(date -u +%H:%M:%S)" par step, 3 runs à froid, médiane
  2. Activer le cache : DerivedData + Pods + SPM, 10 runs, viser un taux de hit > 60 %
  3. Découper le workflow : jobs test / archive séparés, archive uniquement sur main
  4. Évaluer le matériel : runner Apple Silicon seulement si la structure laisse > 12 min
  5. Surveiller le p95 : objectif ≤ 1,5× la moyenne, alerte à +20 % au-dessus de la baseline

7. Erreurs fréquentes (iOS CI Optimization Mistakes)

❌ Xcode incremental build in CI works automatically

Faux. Sans cache DerivedData explicite, pas de build incrémental en CI.

❌ upgrade Mac solves CI slow

Faux. Sans cache, gain limité (~18 %).

❌ increase -jobs always faster

Au-delà du nombre de cœurs CPU, ça ralentit. Sur M2, -jobs 6~8 est recommandé.

❌ cache key should be exact

Le SHA de commit force un cache miss. Utiliser le hash de Podfile.lock / Package.resolved.

8. FAQ (questions fréquentes GitHub Actions iOS CI)

Q1 : Pourquoi GitHub Actions iOS CI est-il si lent ?

Les runners macOS sont stateless et éphémères : DerivedData, Pods et cache SPM disparaissent à chaque run. En local, Xcode compile vite avec un cache chaud ; en CI, chaque run repart de zéro.

Q2 : Quelle est la méthode la plus efficace pour l'iOS CI optimization ?

Cache DerivedData + CocoaPods + SPM — 42 % du gain, 8–10 minutes en moyenne. C'est la première priorité pour l'Xcode build time optimization en CI.

Q3 : Apple Silicon résout-il seul la lenteur CI ?

Non — il fixe seulement le plafond de performance. Sans cache ~18 % ; avec cache et parallélisation, de 12 à 9 minutes.

Q4 : Pods ou SPM — lequel est plus lent ?

CocoaPods en général (3m 12s vs. 1m 44s) à cause des téléchargements réseau. Les deux doivent être cachés — les gains s'additionnent.

Q5 : Ordre d'optimisation optimal ?

Cache → Parallélisation → Apple Silicon. Combinaison : 68 % de réduction ; ne pas sauter d'étape.

9. Conclusion

Une CI iOS lente n'est pas un problème de performance pur, mais :

La pipeline ne réutilise aucun résultat de calcul antérieur
Chemin optimal : ① Cache (gain maximal) → ② Parallélisation (structure) → ③ Apple Silicon (plafond).

Après l'optimisation structurelle, pour un runner self-hosted Apple Silicon dédié sans ops matériel : voir la solution Cloud Mac (zéro attente en file, cache sans limite 10 Go/repo).

Offre limitée

CI iOS plus rapide : Cloud Mac Apple Silicon

Runners dédiés · abonnement mensuel · sans ops matériel

Accueil
Offre limitée Voir les plans