La macro @State : ce que Xcode 27 refuse désormais de compiler
Les notes de version d’iOS 27 ouvrent l’entrée @State en décrivant un bug que SwiftUI traîne depuis iOS 13 :3 « Un @State déclaré avec une expression comme valeur initiale évaluait cette expression à chaque réinstanciation de la structure de vue. Dans le cas de @State private var model = Model(), cela signifie que Model.init() est appelé de nombreuses fois au cours de la vie de la vue. »1
Le correctif est une réécriture. « Xcode 27 introduit une nouvelle implémentation de @State qui évite cette évaluation répétée. Ce nouveau comportement est rétroporté vers les systèmes alignés sur iOS 17. Le nouveau @State est implémenté à l’aide d’une macro Swift. Il est largement compatible au niveau du code source avec la version property wrapper, à quelques exceptions près. »1
Lisez bien le déclencheur dans cette phrase. Apple nomme Xcode, pas une cible de déploiement.
En bref
- Xcode 27 réimplémente
@Statesous forme de macro Swift, et les pages de symboles d’Apple énoncent la bascule dans les deux sens : la page de la structureStateindique « Lorsque vous compilez avec Xcode 27 ou une version ultérieure, le système utilise la macroState()à la place », et la page de la macroState()indique « Lorsque vous compilez avec Xcode 26 ou une version antérieure, le système utilise le property wrapperStateà la place ».23 - Votre cible de déploiement ne vous permet pas d’échapper à la macro. Celle-ci porte la même disponibilité iOS 13.0 que le property wrapper qu’elle remplace, et la bascule dépend de la version de Xcode plutôt que de la cible. La partie exécution n’est rétroportée que vers les « systèmes alignés sur iOS 17 » : les projets qui prennent en charge iOS 15 ou 16 héritent donc du changement à la compilation sans le comportement d’exécution correspondant.
- L’abandon silencieux de la valeur est un comportement ancien et inchangé. Apple écrit qu’il « n’a pas changé du fait de la macro, mais certains de ces cas ne compilent plus ».1 La macro transforme un bug qui avalait la valeur de votre initialiseur en échec de compilation.
- Deux schémas font céder la compilation : un initialiseur qui affecte une valeur à une propriété
@Stateportant déjà une valeur initiale à la déclaration, et une extension qui appelle l’initialiseur membre à membre synthétisé par le compilateur pour une structure entièrement privée.1 Apple écrit « certains de ces cas », pas tous, et n’énumère jamais lesquels. - J’ai audité quatre applications en production avant d’écrire : 267 déclarations
@State, dont 201 portant une valeur initiale à la déclaration, zéro occurrence de l’un ou l’autre schéma problématique, un cas qui frôle le problème, et une formulegrepqui fabrique de faux résultats propres sous macOS.6
L’entrée figure dans les notes de version d’iOS et iPadOS 27, section SwiftUI, plutôt que dans celles de Xcode 27, qui ne comportent aucune entrée correspondante.7 Un emplacement curieux pour un changement qu’Apple attribue à Xcode, et une bonne raison pour que la nouvelle parvienne tardivement aux développeurs.
Le bug de performance corrigé par Apple
La description qu’Apple donne de l’ancien comportement est d’une franchise inhabituelle pour une note de version. Model.init() « est appelé de nombreuses fois au cours de la vie de la vue ».1 SwiftUI réinstancie les structures de vue en permanence, et chaque réinstanciation réévaluait l’expression située à droite du signe égal. SwiftUI jetait le résultat, puisqu’un état déjà existant l’emporte, mais le travail avait bel et bien lieu.
La page de la macro énonce le nouveau contrat en une phrase : « Une propriété State() instancie sa valeur par défaut la première fois que SwiftUI instancie la vue. »2
La page des nouveautés SwiftUI ajoute une réserve absente de la note de version, et c’est précisément cette réserve qu’il faut intégrer à votre planification : « Compilez votre projet avec Xcode 27 ou une version ultérieure pour que l’attribut @State utilise la macro State() afin de créer une valeur d’état dans un App, une Scene ou une View. Ce changement n’initialise et ne stocke votre propriété qu’une seule fois lorsqu’il s’agit d’une classe. »4
« Lorsqu’il s’agit d’une classe » restreint sensiblement le gain, et pointe droit vers le schéma dont les bases de code SwiftUI modernes sont truffées. Stocker un objet @Observable dans un @State est l’approche documentée par Apple, et l’exemple de la page de la macro fait exactement cela avec une @Observable class Library.2 L’initialiseur d’une structure est généralement peu coûteux. Celui d’une classe qui ouvre un magasin de données, lance une requête ou enregistre un observateur ne l’est pas, et Apple le décrit comme s’exécutant « de nombreuses fois ».1
Ce qui a réellement changé, et ce qui n’a pas bougé
Apple énonce la sémantique et le comportement à la compilation dans une seule phrase, et ses deux moitiés tirent dans des directions opposées : « Si vous fournissez une valeur initiale à la déclaration @State et que vous tentez également de lui affecter une valeur dans un initialiseur, la valeur de l’initialiseur est ignorée. Ce comportement n’a pas changé du fait de la macro, mais certains de ces cas ne compilent plus. »1
Rien n’a changé quant au sens du code. Sous le property wrapper, un initialiseur qui affectait une valeur à une propriété @State portant une valeur à la déclaration ne faisait rien, silencieusement, et compilait sans broncher. La macro laisse la règle intacte et supprime le silence.
Le mode de défaillance ainsi mis à la retraite était le plus coûteux. Un développeur écrit un initialiseur, y fait transiter un titre, constate que le mauvais titre s’affiche, puis part fouiller le corps de la vue. Le compilateur savait depuis le début, sans aucun moyen de le dire.
L’exemple d’Apple porte les deux commentaires qui résument la situation :
struct StickerPageView: View {
@State private var page = StickerPage()
let title: String
init(title: String) {
// `title` won't have any effect
// this also won't compile with @State macro
self.page = StickerPage(title: title)
self.title = title
}
}
« N’aura aucun effet » et « ne compilera pas » occupent deux lignes voisines. La première décrit Xcode 26. La seconde décrit Xcode 27. Même code, même sens, verdict différent.
Le correctif supprime la valeur portée par la déclaration :
struct StickerPageView: View {
@State private var page: StickerPage // no initial value expression
let title: String
init(title: String) {
self.page = StickerPage(title: title) // works!
self.title = title
}
}
Apple ramène la règle à une seule consigne : « Lorsque vous affectez une valeur initiale via un initialiseur, ne fournissez pas de valeur initiale à la déclaration @State. »1
Le résumé exact n’est donc pas que la macro a cassé l’affectation dans l’initialiseur. Cette affectation était déjà cassée, et la macro est la première chose à le dire tout haut. Présenter ce changement comme une régression, c’est prendre le problème à l’envers, même si la conséquence pratique pour une branche de livraison reste identique : des compilations qui passaient hier échouent aujourd’hui.
Une réserve mérite d’être conservée. Apple a écrit « certains de ces cas », pas tous, et n’énumère jamais lesquels. Une compilation propre sous Xcode 27 renseigne sur votre code, elle ne démontre rien sur la règle.
L’initialiseur synthétisé disparaît
La seconde exception n’a rien à voir avec les valeurs initiales, et elle piège du code qui ne mentionne jamais @State sur le site d’appel.
« Lorsque tous les membres stockés d’une structure sont privés, le compilateur synthétise un init privé utilisable dans une extension du même type : »1
struct StickerPageView: View {
@State private var page: StickerPage
private let title: String
...
}
extension StickerPageView {
init(title: String, _ page: StickerPage) {
self.init(page: page, title: title) // using the synthesized init
}
}
« La macro state désactive cet initialiseur synthétisé. Le code ci-dessus ne compile donc plus. Pour y remédier, affectez explicitement une valeur aux membres : »1
extension StickerPageView {
init(title: String, _ page: StickerPage) {
self.title = title
self.page = page
}
}
Ce schéma est plus retors à débusquer que le premier, car le site d’appel qui casse se trouve dans une déclaration différente de celle du @State qui en est la cause. Un grep sur @State ne le fera pas remonter.
Apple énonce l’effet et s’arrête là. La déclaration publiée de la macro concorde avec le symptôme :
@attached(accessor, names: named(init), named(get), named(set))
@attached(peer, names: prefixed(`_`), prefixed(`__`), prefixed(`$`))
macro State()
Une macro d’accesseurs qui fournit get et set modifie la nature même de la propriété du point de vue du compilateur, et la synthèse de l’initialiseur membre à membre s’appuie sur les propriétés stockées.2 Lire la déclaration comme la cause relève de ma déduction et non d’une affirmation d’Apple, et la solution de contournement ne dépend pas du mécanisme : écrivez les affectations à la main.
Inférence générique et composition de property wrappers
Apple consacre une phrase à chacune des exceptions restantes, et toutes deux méritent une ligne dans une liste de contrôle de migration, même si ni l’une ni l’autre ne touchera beaucoup de projets.
L’inférence générique reçoit le traitement le plus vague de toute l’entrée : « Dans de rares situations, l’inférence automatique des arguments génériques de @State est moins souple avec l’implémentation par macro. Écrivez le type de manière plus explicite. »1 Apple ne cite aucun cas et ne propose aucun diagnostic à rechercher. La solution consiste à annoter explicitement le type sur la déclaration, partout où le compilateur proteste.
Apple présente la composition comme une limite plutôt que comme une rupture : « La composition de @State avec d’autres property wrappers ou macros n’est pas prise en charge. »1 À vérifier si vous avez déjà enveloppé @State dans un property wrapper maison, et à retenir : Apple qualifie cela de terrain non pris en charge, et non de quelque chose qui fonctionnait auparavant.
Aucune cible de déploiement n’y échappe
Trois phrases tirées de trois pages Apple ferment toutes les issues.
Le symbole de la macro State() porte une disponibilité à partir d’iOS 13.0, iPadOS 13.0, macOS 10.15, tvOS 13.0, watchOS 6.0 et visionOS 1.0, identique à celle du property wrapper qu’elle remplace : abaisser votre version minimale ne permet donc pas d’esquiver la macro.23 Et la bascule dépend du compilateur, pas de la cible : « Lorsque vous compilez avec Xcode 27 ou une version ultérieure, le système utilise la macro State() à la place. »3
La partie exécution du changement a un plancher que la partie compilation n’a pas. Apple écrit que le nouveau comportement « est rétroporté vers les systèmes alignés sur iOS 17 », ce qui laisse un vide en dessous : un projet qui vise iOS 15 ou 16 obtient la macro à la compilation, et avec elle les exceptions de compatibilité du code source, sans le comportement d’exécution rétroporté.1 Apple ne dit pas ce que ces cibles obtiennent à la place. Si vous prenez en charge un système antérieur à iOS 17, considérez la rupture de compilation comme certaine et le correctif d’initialisation répétée comme non confirmé sur vos appareils les plus anciens.
Ouvrez le projet dans Xcode 27, compilez, et vous avez le nouveau @State. Aucune clé Info.plist, aucun réglage de compilation, aucune vérification de disponibilité n’entre dans la décision.
Le cycle 27 comporte trois ruptures que les développeurs rangent volontiers sous une seule étiquette, alors qu’elles se déclenchent à trois moments distincts. L’exigence d’écran de lancement s’applique aux « applications compilées avec le SDK 27.0 ou ultérieur » et vous coûte un rejet de l’App Store. L’obligation liée au cycle de vie des scènes s’applique aux applications « compilées avec le dernier SDK » et vous coûte une application qui ne se lance pas. La macro @State, elle, se rattache à la version de Xcode et vous coûte une compilation. Apple formule les deux premières par rapport au SDK et la troisième par rapport à la chaîne d’outils, ce qui place @State en tête de file : vous la rencontrez dès la première compilation, avant même d’avoir touché à une clé de plist ou à une cible de déploiement.
La pression pour adopter cette chaîne d’outils arrive selon un calendrier publié, mais rien n’est encore annoncé pour iOS 27. La page des exigences d’Apple indique actuellement : « Depuis le 28 avril 2026, les applications téléversées sur App Store Connect doivent être compilées avec Xcode 26 ou une version ultérieure, en utilisant un SDK pour iOS 26, iPadOS 26, tvOS 26, visionOS 26 ou watchOS 26. »5 Apple n’a publié aucune date équivalente pour le SDK d’iOS 27. Apple a relevé la version minimale du SDK chaque printemps ces dernières années : une échéance en 27 constitue donc une attente raisonnable et non un fait établi, et toute date précise que vous liriez ailleurs relève de la déduction.
Ce que contiennent réellement quatre applications en production
J’ai mené l’audit sur mon propre code avant d’écrire sur celui des autres : quatre applications de l’App Store, toutes en SwiftUI, toutes actuellement compilées avec Xcode 26.6.6
| Application | Fichiers Swift | Déclarations @State |
Avec valeur à la déclaration | Types déclarant @State |
|---|---|---|---|---|
| Reps | 77 | 120 | 83 | 31 |
| Return | 57 | 57 | 42 | 14 |
| Ace Citizenship | 26 | 63 | 54 | 11 |
| Banana List | 55 | 27 | 22 | 8 |
| Total | 215 | 267 | 201 | 64 |
Deux commandes réduisent le champ. La première repère les déclarations @State portant une valeur initiale, et la classe de caractères placée après @State justifie sa présence : [^A-Za-z0-9_] écarte @StateObject des résultats, ce qu’une simple recherche de @State ne fait pas.
grep -rn --include="*.swift" \
--exclude-dir=.build --exclude-dir=DerivedData --exclude-dir=build \
-E '@State[^A-Za-z0-9_][^=]*[^!<>=]=[^=]' .
La commande suppose que l’attribut et la déclaration partagent la même ligne. Une propriété écrite avec @State sur sa propre ligne, au-dessus de private var page = StickerPage(), ne produit aucune correspondance : c’est le même faux résultat propre que celui décrit plus bas, sous un autre déguisement. Lisez les absences comme « pas encore vérifié » et non comme « propre ».
La seconde restreint aux fichiers qui déclarent également un initialiseur, seul endroit où la première exception peut frapper :
find . -name "*.swift" -not -path "*/build/*" -not -path "*/DerivedData/*" -print0 |
while IFS= read -r -d '' f; do
grep -qE '@State[^A-Za-z0-9_][^=]*[^!<>=]=[^=]' "$f" || continue
grep -qE '^[[:space:]]*(private |public |internal |fileprivate )?init[[:space:]]*[(<]' "$f" || continue
echo "$f"
done
La boucle paraît plus lourde qu’un passage par xargs, et sous macOS elle justifie ce poids. Le grep BSD n’émet pas de séparateurs NUL pour -Z comme le fait le grep GNU : grep -rlZ ... | xargs -0 grep -l transmet donc au second grep un unique bloc joint par des retours à la ligne. Sur un projet dont le chemin contient un espace, Banana List par exemple, vous récoltez un écran de « No such file or directory » sur la sortie d’erreur et strictement rien sur la sortie standard, ce qui ressemble exactement à un audit propre.6 Interpréter une sortie vide comme « aucune correspondance » donne la réponse inverse de la bonne, précisément sur les projets qui en auraient le plus besoin.
Sur 215 fichiers Swift, la liste courte s’est arrêtée à 20 fichiers : neuf dans Reps, six dans Return, trois dans Ace Citizenship et deux dans Banana List. La lecture manuelle de ces 20 fichiers a donné le même résultat dans les quatre projets.
- La première forme exacte décrite par Apple, une valeur initiale à la déclaration doublée d’une affectation à cette même propriété dans un initialiseur du même type : zéro.
- La seconde exception, une extension appelant l’initialiseur membre à membre synthétisé d’une structure qui déclare
@State: zéro. - L’exception de composition,
@Stateaccompagné d’un autre property wrapper ou d’une autre macro sur une même déclaration : zéro.
Un cas qui frôle le problème mérite d’être décrit, car il ne se trouve qu’à une ligne de la forme qu’Apple vous demande de supprimer. Une vue de configuration de profil dans Reps déclare @State private var healthWriteStatus: HealthAuthorizationState = .notRequested puis, dans son initialiseur, affecte self._healthWriteStatus = State(initialValue: ...). Les deux endroits portent une valeur : la consigne d’Apple s’applique donc mot pour mot, ne fournissez pas de valeur initiale à la déclaration @State.1 L’affectation emploie la forme préfixée par un tiret bas plutôt que la forme valeur enveloppée de l’exemple d’Apple, et la note de version d’Apple ne mentionne jamais la forme préfixée.
Reste la question à laquelle mon audit n’a pas pu répondre. La formule _x = State(initialValue:) apparaît 15 fois dans trois des quatre applications, et elle demeure la façon standard d’amorcer un état à partir d’un paramètre d’initialiseur. L’entrée d’Apple ne l’aborde pas. La déclaration de la macro génère bien un symbole voisin (peer) préfixé par un tiret bas,2 ce qui est un indice, pas une garantie. Je compile avec Xcode 26.6 (build 17F113) : je n’ai donc pu vérifier ni la formule ni le texte des erreurs de compilation qui en résulteraient.6 Quiconque dispose d’une bêta de Xcode 27 peut trancher les deux en une après-midi. En attendant, cherchez dans votre code la forme de la déclaration plutôt qu’une chaîne d’erreur.
Le constat honnête, sur 267 déclarations, est que la majeure partie du code SwiftUI passe sans une égratignure, ce qui correspond à la formule « largement compatible au niveau du code source » employée par Apple.1 Les vues à risque sont celles dotées d’initialiseurs écrits à la main, et elles se concentrent fortement : moins d’un fichier Swift sur 10, sur quatre bases de code, réunissait à la fois un initialiseur écrit à la main et un @State portant sa propre valeur initiale.
FAQ
Dois-je modifier les appels _page = State(initialValue:) ?
L’entrée d’Apple ne le dit pas. La forme préfixée par un tiret bas affecte directement le stockage projeté au lieu de la valeur enveloppée, et elle n’apparaît nulle part dans la note de version : ni initialValue, ni exemple avec tiret bas, aucune mention dans un sens ou dans l’autre.1 La déclaration publiée de la macro émet bien un symbole voisin (peer) préfixé par _, ce qui est un indice, pas une garantie.2 Trois des quatre applications que j’ai auditées emploient cette formule, 15 fois au total : la réponse concerne donc davantage de bases de code que les deux exceptions documentées.6 Tant qu’Apple ne se prononce pas, compilez le projet sous Xcode 27 et laissez le compilateur répondre plutôt que de refactoriser sur des suppositions.
La macro a-t-elle changé le sort d’une valeur affectée dans un initialiseur ?
Non. Apple est explicite : le comportement d’abandon « n’a pas changé du fait de la macro, mais certains de ces cas ne compilent plus ».1 Sous Xcode 26, un initialiseur affectant une valeur à une propriété @State qui portait déjà une valeur à la déclaration ne faisait rien et compilait. Sous Xcode 27, certains de ces cas échouent à la compilation. La sémantique n’a pas bougé, les diagnostics sont arrivés : une compilation qui casse ici, c’est le compilateur qui signale un bug que vous aviez déjà.
Comment repérer le code à risque dans mon projet ?
Cherchez les déclarations @State portant une valeur initiale, restreignez les résultats aux fichiers qui déclarent aussi un initialiseur, puis lisez cette liste courte à la main. Excluez @StateObject au moyen d’une classe de caractères (@State[^A-Za-z0-9_]), et préférez une boucle find -print0 à grep -rlZ | xargs -0 sous macOS, où le grep BSD n’émet aucun séparateur NUL et où tout chemin contenant un espace produit une sortie vide qui ressemble à un succès.6 Sur quatre applications et 215 fichiers Swift, la liste courte s’est arrêtée à 20 fichiers. Cherchez séparément les extensions qui appellent self.init(...) sur une structure de vue, car la seconde exception ne laisse aucune trace à proximité du @State qui la provoque.
Mon application va-t-elle vraiment gagner en rapidité ?
Uniquement dans des circonstances précises, qu’Apple restreint davantage que ne le laisse entendre la note de version. Celle-ci décrit l’évitement d’une évaluation répétée de manière générale,1 tandis que l’entrée des nouveautés SwiftUI précise que le changement « n’initialise et ne stocke votre propriété qu’une seule fois lorsqu’il s’agit d’une classe ».4 Le gain se joue sur @State private var model = SomeObservableClass(), où l’ancienne implémentation exécutait l’initialiseur de la classe à chaque réinstanciation de la vue avant d’en jeter le résultat. Un @State private var isPresented = false n’a jamais posé de problème.
À retenir
Pour les développeurs iOS :
- Auditez deux formes, pas une seule. La première se loge sur une déclaration @State voisine d’un initialiseur ; la seconde vit dans une extension qui appelle self.init(...) sur une structure de vue dont tous les membres stockés sont privés.1
- Corrigez la première en supprimant la valeur portée par la déclaration, pas en supprimant l’affectation dans l’initialiseur. La consigne d’Apple est de conserver l’initialiseur comme source unique et de laisser la déclaration nue.1
Pour les équipes qui maintiennent du code SwiftUI ancien :
- Calibrez l’effort de revue sur la liste courte, pas sur la base de code entière. Les fichiers réunissant un @State avec valeur initiale et un initialiseur écrit à la main représentaient 20 fichiers sur 215 dans mes quatre applications, et toute occurrence du premier schéma se trouve forcément dans l’un d’eux. Cherchez le second schéma séparément, puisqu’il se cache dans les extensions.6
- Traitez une compilation cassée comme un bug découvert. SwiftUI jetait déjà la valeur de l’initialiseur que votre compilateur rejette désormais : partout où le changement frappe, un comportement était déjà faux.1
Pour les responsables de livraison :
- Planifiez l’audit @State avant les travaux déclenchés par le SDK dans le même cycle. La clé d’écran de lancement et le cycle de vie des scènes se rattachent tous deux au SDK avec lequel vous compilez ; @State se rattache à la version de Xcode avec laquelle vous compilez, il arrive donc en premier.13
- Ne planifiez rien en fonction d’une échéance liée au SDK d’iOS 27. Le minimum publié par Apple reste le SDK d’iOS 26, requis depuis le 28 avril 2026, et Apple n’a rien annoncé pour 27.5
Trois points d’application arrivent dans un même cycle et échouent à trois endroits différents : la clé d’écran de lancement bloque une soumission, l’obligation liée aux scènes bloque un lancement, et la macro @State bloque une compilation. Pour le reste de ce qui arrive dans le même SDK, consultez Les nouveautés de SwiftUI pour iOS 27. Le sommaire complet de la série se trouve sur la page Série Écosystème Apple.
Références
-
Apple, iOS & iPadOS 27 Release Notes, section SwiftUI, New Features (radar 105893279). Source de la description de l’ancien comportement (« A
@Statedeclared with an expression as its initial value used to evaluate the expression each time the view struct re-instantiates. In the case of@State private var model = Model(), this meansModel.init()gets called many times throughout the view’s lifetime »), de la nouvelle implémentation (« Xcode 27 introduces a new@Stateimplementation that avoids this repeated evaluation. This new behavior back-deploys to iOS 17 aligned OSes. The new@Stateis implemented with a Swift macro. It is largely source compatible with the property wrapper version, with a few exceptions »), de la première exception et de sa consigne (« If you provide an initial value at@Statedeclaration, and also try to assign a value to it in an initializer, the initializer value is discarded. This behavior has not changed because of the macro, but some such cases no longer compile » et « When assigning initial value via an initializer, do not provide an initial value at the @State declaration »), des deux blocs de codeStickerPageViewillustrant la première exception, de la seconde exception (« When all stored members of a struct are private, the compiler synthesizes a private init that can be used in an extension of the same type » et « The state macro disables this synthesized initializer. So the code above no longer compiles. To mitigate, assign value to members explicitly ») avec ses deux blocs de code, de la note sur l’inférence générique (« In rare situations, the automatic inference of generic arguments of@Stateis less flexible with the macro implementation. Write the type with more specificity ») et de la note sur la composition (« Composing@Statewith other property wrappers or macros is not supported »). Tous les blocs de code sont reproduits mot pour mot. Vérifié auprès du JSON de documentation d’Apple le 25 juillet 2026, la page HTML affichant son contenu via JavaScript. ↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩ -
Apple, State() macro, référence de macro SwiftUI. Source de la déclaration publiée (
@attached(accessor, names: named(init), named(get), named(set)),@attached(peer, names: prefixed(_), prefixed(__), prefixed($)),macro State()), de la liste de disponibilité (iOS 13.0, iPadOS 13.0, Mac Catalyst 13.0, macOS 10.15, tvOS 13.0, visionOS 1.0, watchOS 6.0), de la précision sur la chaîne d’outils (« When you build with Xcode 26 or earlier, the system uses theStateproperty wrapper instead »), du nouveau contrat d’initialisation (« AState()property instantiates its default value the first time SwiftUI instantiates the view ») et de l’exemple « Store observable objects » qui conserve une@Observable class Librarydans un@State. ↩↩↩↩↩↩↩ -
Apple, State, référence de structure SwiftUI. Toujours déclarée
@frozen @propertyWrapper struct State<Value>avec une disponibilité à partir d’iOS 13.0, iPadOS 13.0, Mac Catalyst 13.0, macOS 10.15, tvOS 13.0, visionOS 1.0 et watchOS 6.0. Source de la précision sur la chaîne d’outils formulée dans l’autre sens : « When you build with Xcode 27 or later, the system uses theState()macro instead. » ↩↩↩↩↩ -
Apple, SwiftUI Updates, juin 2026, General. Source de la réserve sur les classes : « Build your project in Xcode 27 or later so that the
@Stateattribute uses theState()macro to create a state value in anApp,Scene, orView. This change only initializes and stores your property once when it’s a class. » ↩↩ -
Apple, Upcoming requirements, Apple Developer News. Source du minimum actuel du SDK : « Since April 28, 2026 Apps uploaded to App Store Connect must be built with Xcode 26 or later using an SDK for iOS 26, iPadOS 26, tvOS 26, visionOS 26, or watchOS 26. » Consulté le 25 juillet 2026 ; la page ne mentionne aucune exigence relative au SDK d’iOS 27. ↩↩
-
Audit mené par l’auteur sur quatre applications SwiftUI en production (Reps, Return, Ace Citizenship et Banana List) sous macOS 26.5.2 avec Xcode 26.6 (build 17F113), le 25 juillet 2026. Les décomptes proviennent d’un script qui analyse chaque déclaration de type par profondeur d’accolades et délimite les corps d’initialiseurs en son sein, recoupé avec la commande
greppubliée ci-dessus, laquelle a reproduit exactement les décomptes de valeurs à la déclaration pour chacun des quatre projets (83, 42, 54 et 22). Le comportement dugrepBSD a été confirmé directement : sous macOS,grep -rlZproduit une sortie séparée par des retours à la ligne plutôt que par des NUL, si bien quexargs -0reçoit un unique argument joint et que le pipeline échoue avec « No such file or directory » sur tout chemin contenant un espace. Le décompte de la formule_x = State(initialValue:)(15 occurrences réparties entre Reps, Return et Banana List) provient de la même passe. Le comportement sous Xcode 27 n’a pas été testé, et aucun texte d’erreur de compilation n’est rapporté ici, Xcode 27 n’étant pas installé sur la machine utilisée. ↩↩↩↩↩↩↩ -
Apple, Xcode 27 Release Notes. Recherche du radar 105893279 et de toute entrée décrivant la macro
@Stateeffectuée le 25 juillet 2026 ; ni l’un ni l’autre n’y figure. La seule mention de@Stateconcerne une correction MusicKit sans rapport (radar 176947544). ↩