Comment résoudre les erreurs de migration vers Swift 6.3 ? Vérification de la concurrence et mise à niveau par étapes en 2026

Une mise à niveau vers Swift 6.3 déclenche des diagnostics de concurrence dans plusieurs modules, alors que la branche stable compilait encore sans erreur.

La solution la plus rapide consiste à conserver le compilateur récent sans basculer immédiatement toutes les cibles en Swift 6 : fixez d’abord une référence, classez les diagnostics, migrez une cible isolée, puis validez la concurrence, les dépendances et la chaîne CI avant d’élargir le changement.

Cette procédure s’adresse à vous si vous développez des applications iOS ou macOS confrontées à une avalanche de diagnostics liés à Swift Concurrency, si vous maintenez plusieurs modules avec du code Swift, Objective-C ou C, ou si vous devez prouver dans la CI que la migration Swift 6.3 reste réversible.

Dernière mise à jour : 24 août 2026. Les règles de migration et la disponibilité de Swift 6.3 ont été vérifiées dans la publication officielle de Swift 6.3 et le guide officiel de migration Swift.

Commencez par séparer le compilateur du mode de langage

La confusion initiale explique une grande partie des erreurs de migration Swift 6.3. Un projet peut être compilé avec un outil récent tout en conservant, pour certaines cibles, un mode de langage antérieur. À l’inverse, une seule cible peut adopter le Swift language mode strict alors que les autres restent inchangées.

Ces deux réglages ne jouent pas le même rôle :

Élément Ce qu’il contrôle Risque de confusion Vérification à effectuer
Version du compilateur Analyse, compilation et génération des artefacts Croire que toutes les cibles utilisent automatiquement les règles Swift 6 Relever la version réellement appelée par la CI et par l’environnement local
Swift language mode Règles de langage appliquées à une cible Attribuer chaque diagnostic à Swift 6.3 lui-même Inspecter le réglage de chaque cible, module et configuration
Niveau de diagnostic de concurrence Intensité de la vérification des accès concurrents Traiter un avertissement comme une preuve de bug identique partout Classer le diagnostic selon l’accès, l’isolation et la frontière asynchrone

Avant toute modification, exportez ou consignez les réglages de compilation, les dépendances verrouillées, les cibles concernées, les tests disponibles et les journaux de la branche stable. Le document de compatibilité des versions Swift aide à distinguer la capacité de l’outil de la compatibilité effective du code.

Ne comparez pas seulement « compilation réussie » et « compilation échouée ». Votre référence doit aussi inclure les tests unitaires, les tests d’intégration, les tests de concurrence, la signature et le contenu des artefacts produits. Le nombre d’erreurs ou la durée d’une compilation ne doit être présenté comme un résultat mesuré que si vous disposez d’une mesure issue de votre propre projet ; aucune valeur générique ne permet de prédire le comportement de votre base de code.

Le cas typique : une cible migrée trop tôt

Imaginez une application organisée autour d’un module réseau, d’un module de traitement audio, d’une interface graphique et d’une cible de tests. Le module audio conserve un état mutable partagé, tandis que la couche réseau expose des callbacks hérités. Si vous activez simultanément le mode Swift 6 sur toutes les cibles, le compilateur signale des problèmes dans des zones très différentes. Vous ne savez alors plus si la cause vient de l’isolation, d’un type transmis entre tâches ou d’une dépendance binaire.

La bonne approche est de choisir d’abord le module qui possède le moins de dépendances et la meilleure couverture de tests. Vous obtenez un périmètre lisible, puis vous faites remonter les contraintes vers les interfaces communes au lieu d’ajouter des annotations destinées uniquement à faire disparaître les messages.

Classez les diagnostics de concurrence avant de corriger le code

Les erreurs Swift 6.3 liées à la concurrence ne forment pas une seule catégorie. Une correction valable pour un acteur ne convient pas nécessairement à une collection mutable partagée ou à une API Objective-C appelée depuis une tâche asynchrone.

Utilisez quatre familles de diagnostic :

  1. État mutable partagé : plusieurs tâches peuvent lire ou modifier la même valeur sans propriétaire d’isolation clairement défini. Cherchez les singletons, caches, registres et propriétés globales.
  2. Isolation par acteur : une méthode ou une propriété appartient à un acteur, mais un appel intervient depuis un contexte qui ne respecte pas cette isolation. La correction consiste souvent à déplacer la frontière d’appel ou à rendre l’attente explicite.
  3. Sendable : une valeur traverse une frontière concurrente sans garantie suffisante sur sa sécurité de transfert. Examinez son contenu réel avant d’ajouter une conformité.
  4. Frontière asynchrone : une fermeture, un callback ou une API héritée conserve un contexte d’exécution que le nouveau modèle ne peut pas prouver sûr.

La documentation officielle sur les acteurs précise leur rôle de protection de l’état isolé. Pour une adoption progressive, consultez aussi le guide d’adoption incrémentale de la concurrence.

Corrigez le risque, pas le compteur

Une annotation non sûre peut faire taire le compilateur tout en masquant une véritable course de données. Elle est particulièrement dangereuse lorsqu’elle est ajoutée à un type partagé par plusieurs modules, car le contournement se diffuse alors dans les interfaces publiques.

Pour chaque diagnostic, demandez-vous :

  • Quelle donnée traverse réellement la frontière ?
  • Qui en est propriétaire pendant l’exécution ?
  • Deux tâches peuvent-elles l’écrire simultanément ?
  • Le type est-il immuable, isolé ou protégé par une synchronisation vérifiable ?
  • Le test échoue-t-il si l’ordre d’exécution change ?

Les corrections acceptables sont généralement plus structurantes : déplacer l’état dans un acteur, réduire la durée de vie d’une référence mutable, retourner une valeur immuable, séparer une interface synchrone d’une interface asynchrone ou adapter une API héritée dans un module dédié. L’objectif n’est pas de rendre la liste de diagnostics visuellement vide ; il est de rendre le modèle de propriété et d’exécution compréhensible.

Isolez les dépendances anciennes derrière une frontière

Une dépendance qui ne prend pas en charge Swift 6.3 peut provoquer un échec de compilation, une erreur de conformité Sendable ou un conflit de module binaire. Ces problèmes ne se corrigent pas tous de la même manière.

Commencez par consulter le dépôt officiel de la dépendance et sa documentation de version. Recherchez une version compatible, une correction déjà publiée ou une implémentation source que votre équipe peut reconstruire. Si le composant est livré sous forme binaire, vérifiez également que son module peut être importé par l’outil utilisé dans la branche de migration ; une simple mise à jour du code appelant ne réparera pas un artefact construit avec des hypothèses incompatibles.

Situation rencontrée Décision recommandée Ce qu’il faut éviter
Une version compatible est officiellement disponible Mettre à jour dans une branche dédiée, verrouiller la version et exécuter la suite complète Modifier plusieurs dépendances en même temps sans pouvoir attribuer la régression
Le composant est maintenu mais aucune version prête n’existe Créer un adaptateur local et limiter la surface exposée Répandre des contournements dans tous les modules
Le composant binaire échoue à l’import Demander un artefact compatible ou passer temporairement à une construction source contrôlée Signer et distribuer un binaire dont la compatibilité n’a pas été vérifiée
Le composant est abandonné Évaluer une réécriture ou une alternative après comparaison fonctionnelle Remplacer à la hâte une bibliothèque centrale pendant la migration

Le module d’adaptation doit convertir les types de la dépendance en types internes stables. Il doit aussi concentrer les annotations de compatibilité temporaire, les conversions entre callbacks et tâches, ainsi que les tests de frontière. Quand la dépendance sera mise à jour, vous remplacerez ce module sans devoir réviser toute l’application.

Pour une application audio ou vidéo, cette séparation est essentielle : les tampons, les flux et les callbacks temps réel ne doivent pas être rendus artificiellement transférables uniquement pour satisfaire le compilateur. Testez séparément la propriété des données, la durée de vie des ressources et le comportement lorsque le traitement asynchrone est retardé.

Migrez les cibles et les interfaces dans un ordre contrôlé

Un grand projet ne doit pas basculer toutes ses cibles le même jour. L’ordre le plus sûr part d’un module peu dépendant, doté de tests déterministes, puis progresse vers les bibliothèques partagées et les cibles applicatives.

Voici une séquence opérationnelle :

  1. Créez une branche de migration à partir de la dernière branche publiée et verrouillez les versions du compilateur, des dépendances et des outils de signature.
  2. Relevez, pour chaque cible, le mode de langage, les réglages de concurrence, les imports, les dépendances directes et indirectes, ainsi que les tests exécutables.
  3. Choisissez une cible pilote qui ne contient ni état global critique ni dépendance binaire difficile à reconstruire.
  4. Activez le mode Swift 6 uniquement sur cette cible et produisez un journal classé par famille de diagnostic, plutôt qu’une liste d’erreurs copiée sans contexte.
  5. Corrigez d’abord les frontières publiques : types envoyés entre modules, acteurs appelés depuis une interface, fermetures conservées et callbacks hérités.
  6. Faites passer les tests unitaires, d’intégration et de concurrence de la cible pilote avant de migrer son premier consommateur.
  7. Ajoutez ensuite une cible dépendante, puis répétez l’analyse ; si un module partagé concentre les erreurs, revenez à son interface au lieu d’appliquer des annotations dans chaque appelant.
  8. Ne retirez l’ancien mode de langage qu’après validation de toutes les cibles prévues et de toutes les configurations de distribution.

Les projets mixtes demandent une vérification supplémentaire. Définissez précisément la frontière entre Swift, Objective-C et C : propriété des pointeurs, durée de vie des objets, nullabilité, conventions de fermeture et possibilité d’appel depuis un contexte asynchrone. Une interface qui paraît innocente côté Swift peut encapsuler une mutation non protégée dans une couche C ou Objective-C.

Pour les cibles de test, ne vous contentez pas de compiler le code de production. Les tests utilisent souvent des doublures globales, des registres partagés ou des fermetures conservées plus longtemps que prévu. Ils peuvent donc révéler une faiblesse de conception que la cible applicative ne montre pas encore.

Vérifiez l’ancienne et la nouvelle chaîne dans la CI

La migration Swift 6.3 doit rester indépendante de la livraison courante. Si vous n’avez qu’un seul nœud de compilation, ne remplacez pas son environnement de publication par la branche expérimentale. Dupliquez l’environnement validé ou utilisez un nœud séparé, avec les mêmes secrets nécessaires mais des identifiants et des artefacts isolés.

Votre pipeline doit comparer au moins les éléments suivants :

  • compilation de la branche stable avec son mode de langage connu ;
  • compilation de la branche de migration avec les cibles déjà basculées ;
  • tests unitaires, d’intégration et de concurrence ;
  • résolution des dépendances et vérification du fichier de verrouillage ;
  • signature et validation des artefacts destinés à la distribution ;
  • comportement de l’application dans les scénarios audio, vidéo ou de traitement en arrière-plan concernés.

La procédure officielle de création de code signé pour la distribution doit être vérifiée séparément de la compilation. Une branche peut compiler correctement tout en produisant un artefact impossible à signer ou à distribuer à cause d’un réglage, d’un certificat ou d’une phase d’archivage différente.

Le contenu consacré à la concurrence dans Swift constitue une référence utile pour vérifier que votre correction correspond au modèle d’exécution attendu, plutôt qu’à un simple changement syntaxique.

Les avantages et les limites de la migration parallèle

Avantages

  • Vous conservez une voie de publication pendant que les cibles sont corrigées.
  • Une régression peut être attribuée à une cible, une dépendance ou une interface précise.
  • Les résultats de compilation et de test deviennent comparables entre branches.
  • Le retour arrière ne demande pas de réinstaller immédiatement tout l’environnement de développement.

Limites

  • Vous devez maintenir deux chemins de validation pendant la transition.
  • Les équipes peuvent corriger un même module différemment si les responsabilités ne sont pas documentées.
  • Une dépendance mise à jour dans une branche peut modifier les résultats sans rapport direct avec le mode Swift 6.
  • Les environnements locaux risquent de diverger si la version du compilateur et les réglages ne sont pas consignés.

Pour formaliser les responsabilités, documentez dans le dépôt la cible migrée, la famille de diagnostic, le propriétaire de la correction, le test de preuve et la condition de retour arrière. Une page interne ou un centre d’aide Macstripe peut également servir à structurer les procédures d’accès et de maintenance de vos environnements de travail, mais la décision technique doit rester dans vos scripts et votre dépôt.

Validez la fin de migration avec une checklist d’exploitation

La migration est terminée lorsque les preuves sont réunies, pas lorsque la dernière erreur de compilation disparaît. Utilisez cette checklist avant de supprimer l’ancien nœud d’outillage :

  • [ ] Le compilateur utilisé localement et dans la CI est consigné et reproductible.
  • [ ] Le Swift language mode de chaque cible migrée est documenté.
  • [ ] Les diagnostics critiques ont été classés et corrigés selon leur cause réelle.
  • [ ] Chaque annotation de contournement possède une justification et un test associé.
  • [ ] Les dépendances compatibles sont verrouillées et leur provenance est connue.
  • [ ] Les dépendances non compatibles sont isolées derrière une interface d’adaptation.
  • [ ] Les frontières Swift, Objective-C et C ont été testées avec leurs appels asynchrones réels.
  • [ ] Les tests de concurrence couvrent les états partagés, les acteurs et les fermetures conservées.
  • [ ] La compilation stable et la compilation de migration ont fonctionné en parallèle.
  • [ ] Les tests, la signature et les artefacts de distribution ont été vérifiés dans la CI.
  • [ ] Un retour vers la branche stable a été exécuté sur un environnement séparé.
  • [ ] Les cibles restantes ont une date, un responsable et une condition d’acceptation documentés.

Si une case concernant la signature, les artefacts ou le retour arrière reste vide, gardez l’ancien chemin de publication. Si seuls des modules expérimentaux restent en ancien mode, vous pouvez poursuivre la migration sans les inclure dans la version destinée à la distribution, à condition que leurs interfaces soient explicitement surveillées.

Choisissez un environnement de migration qui reste réversible

Effectuer toute la transition sur votre unique poste ou sur le seul nœud publiable expose l’équipe à trois coûts difficiles à distinguer : interruption des livraisons, perte de reproductibilité et mélange des certificats ou artefacts. Cette organisation devient encore plus fragile lorsque plusieurs développeurs travaillent sur des cibles audio, vidéo ou graphiques qui ne possèdent pas les mêmes dépendances et les mêmes tests.

Un environnement Mac séparé ne remplace pas votre analyse du code, mais il permet de conserver une chaîne stable pendant que vous vérifiez le nouveau compilateur, le mode de langage, les simulateurs, la signature et les tests de régression. Avant de réserver une configuration, consultez les informations de configuration et de commande Macstripe afin de vérifier que l’environnement correspond bien à vos contraintes de construction et d’accès.

La location n’est toutefois pas le meilleur choix pour une équipe qui exécute durablement une charge lourde, qui doit conserver des périphériques physiques spécifiques ou qui a besoin d’un contrôle matériel permanent. Dans ces cas, l’achat et l’administration d’un nœud dédié peuvent être plus cohérents. En revanche, pour une branche de migration temporaire, une validation CI indépendante ou une répétition de retour arrière, votre environnement actuel présente souvent les défauts les plus risqués : il monopolise le nœud de publication, mélange les configurations et rend la comparaison entre ancien et nouveau mode plus difficile. Louer un Mac auprès de Macstripe vous permet alors de tester la migration sur une machine isolée, sans transformer votre chaîne stable en laboratoire expérimental.