Le cycle de vie du consentement : ce qui vient après la vérification de l'âge
Le 4 novembre 2025, Apple a ajouté aux App Store Server Notifications un type de notification qui se déclenche sur une décision parentale plutôt que sur un paiement : RESCIND_CONSENT, qui signale que « le parent ou le tuteur a retiré son consentement à l’utilisation de l’app par l’enfant ».12
C’est le seul des 23 types du service dont la charge utile transporte un objet appData au lieu d’informations de transaction, et appData contient une transaction d’app signée qui existe « même si un client ne réalise aucun achat intégré ».319 Une app qui n’a jamais rien vendu se découvre donc une première raison d’exploiter un point de terminaison de notification.
La déclaration sur les réseaux sociaux traite de la lecture de la tranche d’âge d’une personne : l’entitlement, les seuils, et ce que signifient les bornes renvoyées. Considérez ce point comme acquis et commencez une étape plus loin. La vérification de l’âge dit quel âge a quelqu’un. Les trois API dont il est question ici disent qui approuve, si une approbation déjà accordée survit à une modification de votre app, et ce qui se passe lorsqu’un tuteur la reprend.21012
En bref
- Trois API se situent en aval de la vérification de l’âge, et Apple les livre comme un ensemble : l’API Significant Change de PermissionKit,
AppStore.ageRatingCodedans StoreKit et la notification serveurRESCIND_CONSENT.45 - Apple rattache sa liste de quatre outils au Texas, à l’Utah et à la Louisiane, et non au seul Texas ; toutes les dates annoncées pour ces trois États sont passées, et Apple limite l’obligation à « certaines régions, lorsque la loi l’exige ».5815
- Ce qui décide si vous exécutez quoi que ce soit de tout cela, c’est
AgeRangeService.requiredRegulatoryFeatures, nouveau en 26.4 ; et le flux qu’il conditionne coûte iOS 26.5 à écrire entièrement.736 - Apple refuse de définir ce qu’est un changement significatif et le répète à quatre endroits, en vous renvoyant à votre conseil juridique. Le seul exemple concret qu’elle publie, elle l’attribue à la loi : Apple écrit que le droit texan considère comme tel un changement de classification d’âge de votre app.48
- Votre classification peut changer sans aucune compilation de votre part. Apple a supprimé le palier 15+ en Australie et doté le Vietnam d’un barème à quatre niveaux le 18 juin 2026, sans demander à aucun développeur de soumettre quoi que ce soit.937
- La révocation est la branche que personne ne construit, et la documentation d’Apple ne la construit guère davantage :
RESCIND_CONSENTn’apparaît dans aucun des tableaux de cycle de vie de la page que consulte une équipe serveur.2
Accorder, ré-accorder, révoquer. Apple documente les deux premiers chemins avec un projet d’exemple et une matrice de tests en sandbox. Le troisième reçoit une valeur d’énumération et quatre champs, ce qui correspond à peu près, il s’est avéré, à l’attention que mon propre code lui accorde.
Le calendrier qu’Apple a publié quatre fois
Toutes les dates ci-dessous proviennent des actualités développeurs d’Apple, et c’est la séquence qui compte, davantage que chaque entrée prise isolément.
Apple a annoncé la loi texane SB2420 le 8 octobre 2025, en la datant du « 1er janvier 2026 », et a décrit sa propre réponse plutôt que le texte de la loi : les nouveaux comptes Apple des utilisateurs de moins de 18 ans rejoindraient un groupe de partage familial, et « les parents ou tuteurs devront donner leur consentement pour tous les téléchargements sur l’App Store, tous les achats d’apps et toutes les transactions effectuées par le mineur via le système d’achats intégrés d’Apple ».13 Le 4 novembre, Apple a nommé les outils et les a livrés dans les bêtas 26.2.4 Le 23 décembre, le plan s’est arrêté : « Une injonction récemment prononcée par un tribunal de district a suspendu l’application de la loi texane SB2420 […]. Apple met en pause les plans de mise en œuvre annoncés précédemment ».14 Le 3 juin 2026, il a redémarré : « À la suite d’une décision de justice levant l’injonction visant la loi texane SB 2420 […]. Ces changements entreront en vigueur à partir du 4 juin 2026 ».5
Huit mois de coups de barre sur une seule loi, et les API n’ont pas bougé une seule fois. Elles ont été livrées en 26.2, sont restées disponibles pour les tests en sandbox pendant toute la durée de l’injonction, et attendaient là au moment de sa levée.14 Quiconque a lu la pause de décembre comme une autorisation de reporter a perdu cinq mois d’avance.
Le reste de la carte est arrivé le 24 février 2026, et déborde largement le Texas.
| Juridiction | Date indiquée par Apple | Ce qu’Apple déclare applicable |
|---|---|---|
| Australie, Brésil, Singapour | 24 février 2026 | Apple bloque le téléchargement des apps classées 18+ « à moins que les utilisateurs n’aient été confirmés comme adultes par des méthodes raisonnables »15 |
| Utah | 6 mai 2026 | Catégories d’âge communiquées sur demande pour les nouveaux comptes Apple15 |
| Texas | 4 juin 2026 | Les nouveaux comptes Apple « sont désormais soumis à la loi » : consentement pour les téléchargements, les achats intégrés et les changements significatifs au nom des mineurs de moins de 18 ans, avec possibilité de révocation par le tuteur5 |
| Louisiane | 1er juillet 2026 | Catégories d’âge communiquées sur demande pour les nouveaux comptes Apple15 |
Le Texas fait exception sur le seuil, pas sur l’outillage. Apple décrit le consentement texan comme donné « au nom des mineurs de moins de 18 ans » et publie ses catégories sous la forme « moins de 13 ans, 13-15, 16-17 ou plus de 18 », ce qui correspond exactement à ce que produit l’API : « Vous pouvez spécifier jusqu’à trois seuils d’âge, qui créent jusqu’à quatre tranches d’âge possibles ».4529 Pour l’Utah et la Louisiane, Apple mentionne le partage des catégories d’âge sur demande, puis indique que les quatre outils « ont été étendus pour aider les développeurs à satisfaire leurs obligations de conformité en Louisiane et en Utah », en nommant l’API Significant Change parmi eux.15 L’outillage n’est pas réservé au Texas. Quant à savoir si les obligations le sont, Apple refuse de se prononcer : elle limite le devoir à « certaines régions, lorsque la loi l’exige » et renvoie la question au conseil juridique.8
Ne lisez pas ce tableau comme un exposé du droit ni comme une appréciation de conformité. Il consigne ce qu’Apple a annoncé et la date qu’elle y a attachée. Je suis ingénieur, pas juriste, et tout ce qui suit s’arrête à la frontière de l’API.
La foire aux questions qui renvoie les questions de conformité au conseil juridique apporte une meilleure nouvelle pour la planification des versions : interrogée sur un éventuel impact sur la validation, Apple répond « Non, le processus d’App Review reste inchangé ».8 Les obligations tombent à l’exécution, dans des juridictions nommées, et non au moment de la soumission.
Le partage est donc net. Savoir si votre app doit recueillir un consentement au Texas, en Utah ou en Louisiane est une question pour un avocat inscrit là-bas. Savoir avec quel SDK compiler n’en est pas une : Apple précise qu’il « faut compiler votre app avec les SDK iOS 26.2 et iPadOS 26.2, ou ultérieurs, avec Xcode 26.2 (17C52) ou ultérieur » ne serait-ce que pour atteindre ces frameworks, et que les comptes existants sous iOS 18 ou antérieur « ne seront pas concernés ».8
Apple ne vous dira pas ce que « significatif » veut dire
Le trou définitionnel se trouve au cœur de la fonctionnalité, et quatre surfaces d’Apple vous le renvoient au lieu de le combler : la page du symbole (« C’est à vous de déterminer ce qui constitue une mise à jour significative en fonction de la réglementation applicable »),12 les deux actualités (« il revient au développeur de déterminer quand son app connaît un changement significatif »),45 et la foire aux questions, interrogée directement sur le cas d’un changement de conditions ou de confidentialité (« Cela dépend. C’est à vous de déterminer ce qui constitue une mise à jour significative de l’app en fonction des lois applicables »).8
Apple ne fournit qu’un seul exemple travaillé, et il vient de la loi plutôt que d’Apple : « Le droit texan considère un changement de classification d’âge d’une app comme un changement significatif, et les développeurs doivent tenir à jour leurs sélections de classification d’âge dans App Store Connect ».4
Ce qu’Apple définit, en revanche, c’est la chaîne que vous écrivez. SignificantAppUpdateTopic présente côte à côte un bon et un mauvais exemple, franchise inhabituelle pour une page de symbole :
// Specific
let topic = SignificantAppUpdateTopic(
description: "This update adds video calling and location sharing features."
)
// Vague
let topic = SignificantAppUpdateTopic(
description: "We've made improvements to the app."
)
La consigne d’Apple autour de ce listing est sans détour : « Employez un langage concis et compréhensible qui explique clairement ce qui a changé dans votre app. Les parents et les tuteurs voient cette description au moment de décider s’ils accordent leur permission ».12 Le texte passe-partout des notes de version passe désormais sous les yeux d’un parent qui décide si son enfant garde l’accès, ce qui en fait la chaîne de journal des modifications la plus lourde de conséquences que la plupart des apps livreront jamais.
L’obligation attachée à la réponse, elle, n’a rien de vague, même si Apple reste évasive sur son déclencheur. Apple vous rend « responsable d’empêcher l’accès à votre app ou à certaines fonctionnalités lorsque cela est requis », puis affirme sans ambages que « tant que le parent n’a pas donné son consentement, l’enfant doit être empêché d’accéder à la mise à jour significative, ce qui peut inclure l’ensemble des données de l’app et du compte, ou des fonctionnalités précises ».8 Les mots « l’ensemble des données de l’app et du compte » pèsent lourd. Apple vous laisse le rayon d’impact et désigne le compte entier comme borne supérieure.
La machine à états, et l’endroit où l’exemple d’Apple fuit
Apple a publié un projet d’exemple, « Implementing age assurance and permissions », seule description de bout en bout du flux.6 Le lire comme une machine à états fait apparaître quatre branches et un listing qui mérite d’être réécrit.
La première branche est la moins coûteuse. AgeRangeService.requiredRegulatoryFeatures renvoie un Set<AgeRangeService.RegulatoryFeature> à trois membres : declaredAgeRangeRequired, significantAppChangeRequiresParentalConsent et significantAppChangeRequiresAdultNotification.7 L’exemple d’Apple le vérifie en premier, et « lorsque aucune des deux fonctionnalités n’est présente, l’app saute entièrement le flux ».6 Apple présente la propriété comme reflétant « la région et les réglages de compte » d’une personne, ce qui se lit à mes yeux comme la promesse que les utilisateurs situés hors des juridictions nommées reviennent les mains vides, sans qu’Apple ne garantisse rien de tel.7
Les branches restantes se séparent selon l’âge et selon la manière dont cet âge a été établi :
| Personne | Fonctionnalité réglementaire requise | Ce que fait l’exemple d’Apple |
|---|---|---|
| Mineur | significantAppChangeRequiresParentalConsent |
Envoie une PermissionQuestion au tuteur6 |
| Adulte, moyen de paiement confirmé | significantAppChangeRequiresAdultNotification |
Présente la feuille système d’accusé de réception6 |
| Adulte, sans moyen confirmé | l’une ou l’autre | Bloque l’accès « jusqu’à ce que la personne vérifie son compte dans Réglages »6 |
| Partage refusé | l’une ou l’autre | Analyse le cas, puis n’expose aucune branche pour lui6 |
C’est la troisième ligne qui mérite qu’on s’y arrête. Un adulte qui n’a jamais associé de moyen de paiement à son compte Apple se retrouve bloqué par l’implémentation de référence d’Apple, dans un flux conçu pour protéger les enfants. Apple ne propose ni branche alternative, ni dérogation.
Composé à partir des déclarations publiées, l’aiguillage ressemble à ceci. La séparation adulte/mineur suit l’exemple d’Apple ainsi que la matrice de tests en sandbox, où un compte de 18 ans et plus renvoie un lowerBound de 18 sans borne supérieure et un ageRangeDeclaration valant confirmed ou selfDeclared. Notez le plancher de disponibilité : lire .confirmed vous coûte iOS 26.5, soit une version de plus que tout le reste du flux.61136
import DeclaredAgeRange
import PermissionKit
import SwiftUI
@available(iOS 26.5, *)
struct SignificantChangeGate: View {
enum Phase {
case checking
case clear
case awaitingGuardian(PermissionQuestion<SignificantAppUpdateTopic>)
case blocked
}
let changeDescription: String
@Environment(\.requestAgeRange) private var requestAgeRange
@Environment(\.showSignificantUpdateAcknowledgment) private var acknowledge
@State private var phase: Phase = .checking
var body: some View {
switch phase {
case .checking:
ProgressView().task { await resolve() }
case .clear:
ChangedFeatureView()
case .awaitingGuardian(let question):
PermissionButton(question: question) { Text("Ask a parent to approve") }
case .blocked:
AccountVerificationPrompt()
}
}
private func resolve() async {
let features = (try? await AgeRangeService.shared.requiredRegulatoryFeatures) ?? []
guard !features.isEmpty else {
phase = .clear // nothing applies to this person
return
}
guard let response = try? await requestAgeRange(ageGates: 18),
case let .sharing(range) = response else {
phase = .blocked // declined or unavailable: Apple documents no branch
return
}
let isAdult = range.lowerBound == 18
let isConfirmed = range.ageRangeDeclaration == .confirmed
switch (isAdult, isConfirmed) {
case (true, true) where features.contains(.significantAppChangeRequiresAdultNotification):
try? await acknowledge(updateDescription: changeDescription)
phase = .clear
case (true, false):
phase = .blocked // adult with no confirmed method
case (true, true):
phase = .clear
case (false, _):
guard features.contains(.significantAppChangeRequiresParentalConsent) else {
phase = .clear
return
}
let topic = SignificantAppUpdateTopic(description: changeDescription)
phase = .awaitingGuardian(PermissionQuestion(significantAppUpdateTopic: topic))
}
}
}
Envoyer la question est la moitié facile. C’est à la réception de la réponse que l’exemple d’Apple fuit, et le listing est assez court pour être lu de près :6
for await response in AskCenter.shared.responses(for: SignificantAppUpdateTopic.self) {
guard response.choice.answer == .approval else {
return
}
versionManager.handleAllChanges()
}
Un return à l’intérieur d’un for await abandonne la séquence. Un seul refus, et l’app cesse d’observer ce sujet jusqu’au lancement suivant : un tuteur qui touche Refuser puis se ravise une minute plus tard envoie une approbation dans un flux que personne ne lit. Écrivez plutôt continue et consignez le refus. Lire ce listing comme un défaut plutôt que comme une intention est mon interprétation ; le correctif coûte un mot-clé dans les deux cas. Concevez l’écouteur comme si le lancement en arrière-plan n’existait pas : seule la séquence dépréciée qu’il remplace l’avait jamais promis.2021
L’attente est un état, et son délai d’expiration vous échappe
C’est au milieu du cycle de vie que se loge le vrai travail de conception, et cinq comportements documentés le façonnent. Commencez par celui auquel Apple ne répond qu’à moitié : PermissionQuestion.expirationDate est un Optional<Date> passé lequel « la personne qui reçoit la question ne peut plus répondre », et Apple n’en publie aucune valeur par défaut.16
Un enfant peut annuler avant même que le tuteur ne voie la question : « À tout moment pendant le flux d’envoi de la demande, l’enfant a la possibilité d’annuler la demande […]. Dans ce cas, le système ne délivre aucune réponse à l’app appelante pour cette question précise ».17 Aucune réponse, aucune erreur, aucun rappel. Votre état d’attente reste en attente indéfiniment, sauf si vous fixez vous-même son expiration.
Interroger un adulte lève une erreur. L’article consacré à la sandbox documente un cas que la page du symbole laisse vide : « Appeler AskCenter.ask(_:) pour un utilisateur adulte lève cette erreur, car il ne remplit pas les conditions des demandes de permission parentale ».11 AskError.notAvailable est l’un des deux cas de l’énumération dépourvus de résumé sur leur propre page, et c’est celui qu’un adulte déclenche.18 Aiguillez selon l’âge avant de demander, plutôt qu’en interceptant l’erreur levée.
L’état d’approbation a sa place dans iCloud plutôt que dans le conteneur de l’app. L’exemple d’Apple écrit chaque changement accepté dans NSUbiquitousKeyValueStore et observe didChangeExternallyNotification « afin que les autres appareils ne présentent pas de nouveau le même flux ».6 Un parent qui approuve sur l’iPhone ne devrait pas produire une seconde demande sur l’iPad, et PermissionKit ne fait rien de tout cela pour vous.
Le meilleur détail de l’exemple résout un problème que la plupart des équipes livreraient de travers. Une nouvelle installation ne devrait pas consentir à un changement qu’elle n’a jamais connu : Apple lit donc AppTransaction.originalAppVersion et « marque automatiquement comme traités tous les changements introduits par l’app à cette version ou avant ».619 Le suivi du consentement se fait donc par changement et par personne : des identifiants de changement assortis de leur version d’introduction, pas un booléen.
Votre classification peut changer sans aucune compilation
AppStore.ageRatingCode est une static var qui renvoie un Int? de façon asynchrone, disponible à partir de 26.2 sur iOS, iPadOS, macOS, tvOS, visionOS et watchOS.10 L’usage qu’Apple en décrit relève de la comparaison, pas de l’interprétation : « Utilisez cette propriété pour récupérer la classification d’âge de votre app et la comparer à la dernière classification connue afin de vérifier si elle a changé ».10 La comparaison est tout ce que vous obtenez, car Apple ne publie nulle part dans sa documentation de correspondance entre l’entier et un palier de classification. Vous ne pouvez pas demander si vous êtes en 13+, seulement si vous êtes ce que vous étiez la dernière fois : le vrai contrat, c’est que vous conserviez la valeur précédente et que vous assumiez la toute première exécution.
Pourquoi une app surveillerait-elle sa propre classification ? La raison saute aux yeux dès qu’on remarque que les classifications bougent sans version. Le 21 mai 2026, Apple a annoncé qu’à partir du 18 juin, « la classification d’âge 15+ ne sera plus disponible sur l’App Store en Australie », et a doté le Vietnam d’un barème régional à quatre niveaux dérivé des réponses déjà fournies au questionnaire.9 Les deux ont bien atterri : le tableau australien de l’aide App Store Connect publie désormais 16+ et R 18+ sans ligne 15+, et le Vietnam dispose de son propre tableau.37 Aucun de ces changements n’a réclamé de soumission, de compilation ou d’action du développeur, et Apple écrit que le droit texan compte un changement de classification comme un changement significatif.4
Comparez avec un changement de classification que vous initiez : « Lorsqu’un développeur met à jour la classification d’âge de son app, celle-ci est mise à jour sur tous les appareils des utilisateurs dès que la version est en ligne ».4 Vos propres changements voyagent avec une version que vous pouvez instrumenter. Ceux d’Apple, non, et c’est le manque que comble cette propriété.
Le problème de test est pire qu’un simple manque. Une réponse signée Apple Staff sur les Developer Forums le dit sans détour : « Une valeur de 0 est attendue pendant le développement de votre app, lorsqu’elle est compilée et exécutée depuis Xcode et depuis les environnements Sandbox (TestFlight compris) ».22 Zéro n’est pas nil : l’exemple documenté par Apple lui-même franchit donc son guard let et restitue 0 comme classification valide, ce qu’a précisément observé sur un appareil physique le développeur à l’origine de ce fil.1022
Poussez le raisonnement. Conservez 0 comme référence et le premier lancement depuis l’App Store lira un code réel, y verra un changement, et demandera à un parent de reconsentir à rien du tout. Traiter 0 comme une sentinelle que l’on n’écrit jamais dans la référence est mon interprétation de ces deux déclarations d’Apple, et c’est la seule ligne de code défensif sans laquelle je ne livrerais pas.
Les métadonnées de disponibilité d’Apple recèlent un autre constat qu’aucune page rédigée n’énonce. Les lignes de plateformes de ces quatre capacités percent des trous à des endroits différents :
| Capacité | iOS / iPadOS | macOS | Mac Catalyst | visionOS | tvOS / watchOS |
|---|---|---|---|---|---|
AppStore.ageRatingCode10 |
26.2 | 26.2 | aucune ligne | 26.2 | 26.2 |
requiredRegulatoryFeatures7 |
26.4 | 26.4 | 26.4 | aucune ligne | aucune ligne |
SignificantAppUpdateTopic, PermissionButton1226 |
26.2 | 26.2 | 26.2 | 26.2 | aucune ligne |
showSignificantUpdateAcknowledgment27 |
26.4 | aucune ligne | 26.4 | aucune ligne | aucune ligne |
Deux asymétries en découlent. Une app macOS native peut apprendre que significantAppChangeRequiresAdultNotification s’applique à une personne sans disposer d’aucune API pour y satisfaire : showSignificantUpdateAcknowledgment(in:updateDescription:) prend un UIWindowScene et ne publie aucune ligne macOS, et la variante SwiftUI SignificantUpdateAction s’arrête aux trois mêmes plateformes.27 Apple a bien fourni une variante NSWindow pour l’appel voisin requestAgeRange : l’omission se lit donc comme un manque plutôt que comme une politique délibérée, et c’est là mon interprétation.28
Deuxièmement, la détection va plus loin que la remédiation. ageRatingCode publie des lignes tvOS et watchOS ; rien de ce qui demande un consentement n’en publie.10 Ces cibles peuvent observer un changement de classification sans aucun moyen documenté d’y répondre, et Return livre les deux : sa version tvOS 1.0.1 est en READY_FOR_DISTRIBUTION dans App Store Connect, et l’app iOS distribuée embarque une app pour la montre.30 Ces deux lectures supposent que les métadonnées d’Apple sont complètes, ce que ces mêmes métadonnées contredisent : ageRatingCode ne publie aucune ligne Mac Catalyst alors que tous les symboles voisins le font.10
À quoi sert un serveur une fois qu’Apple bloque le lancement
Apple énonce le comportement de révocation en une phrase : « Lorsqu’un parent ou un tuteur révoque le consentement donné à son enfant pour accéder à une app, Apple empêche le lancement de cette app. Pour gérer les révocations de consentement, utilisez la valeur RESCIND_CONSENT de notificationType ».8
Lisez l’ordre de ces propositions. Le système bloque déjà l’accès : RESCIND_CONSENT n’est donc pas un point d’accroche d’application de la règle, et le construire comme tel le gaspille. C’est le seul signal que vous recevez au sujet d’un utilisateur sur l’appareil duquel votre code ne peut plus s’exécuter. Apple ne dit rien de la façon de classer cet événement ; l’analogie la plus proche que j’aie trouvée est une demande de suppression de compte : des abonnements à réconcilier, un état à geler, et un foyer qui peut réapparaître.
La charge utile est d’une maigreur inhabituelle. appData transporte quatre champs : appAppleId, bundleId, environment et signedAppTransactionInfo.3 Aucune transaction, aucun abonnement, aucune information de renouvellement, et aucun identifiant de compte qui vous appartienne. Le lien vers une personne passe par la transaction d’app signée, dont l’App Store génère l’appTransactionID « pour chaque compte Apple qui télécharge votre app, et pour chaque membre du groupe familial dans le cas des apps prenant en charge le partage familial ».19 Une app gratuite dispose donc bel et bien d’une clé durable par compte sur laquelle faire la correspondance, et une app qui n’en a jamais stocké reçoit une notification qu’elle ne peut attribuer à personne.
Le transport ne réserve aucune surprise : une URL de version 2 par environnement dans App Store Connect, en TLS 1.2 ou ultérieur, 17.0.0.0/8 en liste d’autorisation, 200 à 206 pour le succès, et 40x ou 50x pour s’offrir cinq nouvelles tentatives à 1, 12, 24, 48 et 72 heures, en production uniquement.2324
RESCIND_CONSENT ne porte pas non plus de sous-type. Chacune des 19 valeurs de sous-type publiées se rattache à un type de notification nommé, et la chaîne RESCIND_CONSENT n’apparaît nulle part sur cette page.25 Plus révélateur encore : la page notificationType d’Apple répartit 40 événements sur huit tableaux sous « Handle use cases for In-App Purchase life-cycle events », et RESCIND_CONSENT ne figure dans aucun d’eux ; la valeur existe dans la liste des valeurs possibles et nulle part ailleurs sur la page.2 Apple a documenté la notification dans le matériel consacré à la vérification de l’âge, sans jamais la raccorder à la référence que consulte réellement une équipe serveur.
Ce que mes propres projets font déjà de travers
J’ai fouillé mon propre code avant d’écrire sur celui des autres. La règle de sélection était mécanique, et c’est dans la règle que se cachait le bug : chaque ligne du tableau Active Projects de mon propre CLAUDE.md derrière laquelle se trouve un projet Xcode, soit sept projets et 508 fichiers couvrant tous les fichiers Swift, fichiers d’entitlements, listes de propriétés et project.pbxproj.31 Zéro correspondance pour PermissionKit, AskCenter, SignificantAppUpdateTopic, FamilyControls, ManagedSettings, DeviceActivity, CKShare, sharedCloudDatabase ou ageRatingCode. Cinq des sept possèdent une fiche App Store Connect. Water et Yawara n’en ont aucune et n’ont jamais été livrés : lisez ces deux-là comme du code, pas comme des apps entre les mains de qui que ce soit.31 Ace Citizenship semblait le foyer le plus probable pour des utilisateurs mineurs ; c’est le moins probable : son site compagnon fixe l’éligibilité au N-400 à « 18 ans ou plus », et l’app ne demande de date de naissance nulle part.31
J’ai ensuite passé les mêmes motifs sur tous les dépôts de ~/Projects, ce que j’aurais dû faire d’emblée. Ces motifs correspondent à 21 fichiers, dont sept de code, et six de ces sept se trouvent dans un seul projet iOS auquel le registre n’a jamais consacré de ligne.32
Randori est un carnet d’entraînement de jiu-jitsu, et il abrite exactement la surface de consentement que la liste de motifs existe pour attraper : 20 correspondances pour CKShare et sharedCloudDatabase réparties sur ConnectionStore.swift, RandoriApp.swift, CKPostTransport.swift, ProfileCardView.swift, CloudShareSheet.swift et SocialContracts.swift, où l’acceptation passe par userDidAcceptCloudKitShareWith quand l’app tourne déjà, et par connectionOptions.cloudKitShareMetadata au démarrage à froid.32 Randori livre aussi sa propre primitive de consentement pour le port du nom d’autrui : TagConsentStore échoue en position fermée, si bien qu’un athlète dont le client n’a pas publié de politique d’identification ne peut tout simplement pas être identifié.32
Ainsi, le seul projet doté d’une véritable surface de consentement a écrit son propre registre et n’a rien adopté de celui d’Apple. Il me doit trois choses que je n’ai pas construites. Activer les surfaces de personne à personne que ses contrats gardent en sommeil est le cas d’école de SignificantAppUpdateTopic. ageRatingCode n’a aucune référence stockée, et une version 1.0 jamais publiée n’a jamais tourné que depuis Xcode, la sandbox ou TestFlight, c’est-à-dire précisément là où Apple dit que la propriété renvoie 0.22 La révocation n’a nulle part où atterrir : Randori ne vend rien et n’exploite aucun serveur.32
Le constat le plus intéressant, c’est que le consentement du tuteur est déjà livré dans trois des sept projets d’origine, sous un nom plus ancien.
Ask to Buy est le même mécanisme, de la même forme, et Apple le décrit presque dans les mêmes termes : « Avec Ask to Buy, lorsqu’un enfant souhaite effectuer un achat ou un téléchargement éligible, le système envoie la demande d’achat au parent ou au tuteur ».34 Il se manifeste sous la forme de Product.PurchaseResult.pending, et une approbation arrive par Transaction.updates plutôt qu’au site d’appel, parce que cette séquence transporte « les transactions qui se produisent en dehors de l’app, comme les transactions Ask to Buy ».35 Un refus, lui, ne délivre rien : « Votre app ne reçoit aucune transaction, car vous avez refusé Ask to Buy ».34
Trois des sept vendent quelque chose, et chacun traite l’état d’attente différemment :33
| App | Produit | Traitement de .pending |
|---|---|---|
| ResumeGeni | Abonnement mensuel | Un cas .pending nommé, avec un chemin de réconciliation documenté |
| Reps | Abonnement, deux paliers | case .userCancelled, .pending: fondus en une seule branche |
| Ace Citizenship | Non consommable | Un case .pending: distinct qui renvoie false, identique à une annulation |
Deux sur trois rapportent la délibération d’un tuteur comme un refus de l’utilisateur, et ce que voit l’utilisateur est pire qu’une étiquette erronée. Reps ne referme son paywall que lorsque purchase renvoie true ; Ace ne franchit le sien que lorsque son propre appel renvoie un succès. Sur .pending, aucun des deux appels ne renvoie quoi que ce soit d’exploitable : les deux paywalls restent donc ouverts, sans erreur, sans toast, sans indicateur d’activité. Un appui mort.33 Le parent approuve quelques minutes plus tard, et la transaction atterrit dans un écouteur dont l’interface n’a jamais reconnu qu’une demande avait eu lieu.
Cette branche fondue est exactement la défaillance à laquelle PermissionKit invite, avec des enjeux plus lourds, et je l’ai livrée deux fois avant qu’Apple ne donne à ce motif une seconde API. L’attente n’est pas une branche exotique. L’attente, c’est à quoi ressemble un flux de consentement vu de l’intérieur d’une app, et le bon comportement par défaut est un état que l’on affiche, pas une valeur que l’on renvoie.
Une app se trouve déjà en aval de la moitié serveur, ce qui rend la branche manquante très concrète. Le point de terminaison de version 2 de ResumeGeni a enregistré au moins 56 notifications depuis le 26 juin 2026, dont 55 issues de la sandbox et une de la production, ce qui me permet de savoir que les deux URL d’environnement sont enregistrées, et non une seule. Son gestionnaire nomme 13 types de notification et en aiguille huit ; les cinq restants n’apparaissent que dans des commentaires. RESCIND_CONSENT ne figure dans aucun des deux groupes, ni nulle part ailleurs dans le dépôt.33
Questions fréquentes
Quelle API demande son consentement à un tuteur, et laquelle s’adresse à un adulte ?
Deux frameworks distincts, et il est facile de les inverser. Le consentement parental passe par PermissionKit : un SignificantAppUpdateTopic enveloppé dans PermissionQuestion(significantAppUpdateTopic:), envoyé avec PermissionButton, dont la réponse arrive par AskCenter.shared.responses(for:).12162126 L’accusé de réception d’un adulte passe en revanche par Declared Age Range, sous la forme showSignificantUpdateAcknowledgment(in:updateDescription:).27 Vérifiez requiredRegulatoryFeatures en premier, car interroger un adulte via PermissionKit lève AskError.notAvailable.711
Comment tester la révocation du consentement sans véritable compte familial ?
Activez le mode développeur, puis ouvrez Réglages, Développeur, Compte Apple Sandbox, connectez-vous, sélectionnez le compte, touchez Gérer et choisissez Revoke App Consent. Saisissez votre identifiant de bundle et touchez Revoke Consent ; le système affiche « Notification Triggered ».11 Avec une URL de version 2 configurée, votre serveur reçoit RESCIND_CONSENT accompagné d’un objet appData dont les champs bundleId et environment confirment que la notification correspond à la bonne app.311 La sandbox envoie chaque notification une seule fois, sans nouvelle tentative : un point de terminaison qui renvoie un 50x en plein test n’a pas de seconde chance.24
Pourquoi une app surveillerait-elle sa propre classification d’âge à l’exécution ?
Parce qu’Apple modifie les classifications sur la boutique sans aucune soumission de votre part, et qu’Apple écrit que le droit texan compte un changement de classification comme un changement significatif, avant de vous renvoyer vers l’API Significant Change pour demander le consentement parental.4 Le 18 juin 2026 en est l’exemple travaillé : l’Australie a perdu son palier 15+ et le Vietnam a gagné un barème à quatre niveaux, tous deux appliqués aux apps existantes à partir des réponses déjà données au questionnaire.937 Apple ne publie aucune correspondance entre l’entier de la propriété et un palier de classification : la comparaison avec une valeur que vous avez stockée est donc la seule opération qu’elle permette.10
Est-ce que RESCIND_CONSENT bloque l’utilisateur à ma place ?
Le système le fait déjà, et c’est le point que la plupart des implémentations manquent. Apple précise que lorsqu’un tuteur révoque son consentement, « Apple empêche le lancement de l’app », puis vous oriente vers la notification pour gérer l’événement, non pour l’appliquer.8 Le travail qui reste est donc de nature serveur : geler l’état du compte, réconcilier un éventuel abonnement, et cesser d’envoyer des notifications push vers un appareil qui ne peut pas ouvrir votre app. Traitez-le comme une suppression de compte, pas comme un contrôle d’autorisation en échec.
À retenir
Pour les développeurs iOS :
- Auditez dès aujourd’hui votre switch sur Product.PurchaseResult. Ask to Buy est un consentement de tuteur déjà livré, .pending est la façon dont il se manifeste, et fondre ce cas dans .userCancelled est le défaut auquel PermissionKit invitera, avec des enjeux plus lourds.3435
- Prévoyez iOS 26.5, et non 26.4, si votre flux distingue un adulte confirmé d’un adulte auto-déclaré.36
- Corrigez le return de l’écouteur d’exemple d’Apple avant de le copier, et ne laissez jamais 0 atteindre la référence ageRatingCode que vous stockez.622
Pour les équipes qui livrent sur plusieurs plateformes Apple :
- Vérifiez les lignes de disponibilité avant de planifier le travail. Une app macOS native peut détecter qu’un accusé de réception adulte s’applique sans disposer d’aucune API pour le présenter ; tvOS et watchOS peuvent lire ageRatingCode sans aucune API de consentement derrière.71027
- Stockez l’accusé de réception dans NSUbiquitousKeyValueStore, avec une clé par changement, et servez-vous de AppTransaction.originalAppVersion pour exempter les nouvelles installations des changements qui leur sont antérieurs.619
Pour les responsables backend et livraison :
- Enregistrez l’appTransactionID par compte avant d’en avoir besoin, puis montez le point de terminaison de version 2, même pour une app gratuite. Un point de terminaison construit après coup reçoit des événements de révocation qu’il ne peut attribuer à personne.319
- Faites relire la description du changement significatif par la personne qui a la charge de vos textes produit. C’est la seule chaîne qu’un tuteur lit au moment de décider si votre app garde un utilisateur.12
Trois des quatre points d’application de ce cycle se déclenchent sur quelque chose que vous contrôlez : la clé d’écran de lancement sur un SDK, la macro @State sur une chaîne d’outils, et la déclaration sur les réseaux sociaux sur une soumission. Le consentement du tuteur, lui, se déclenche sur un rôle d’audience et sur un tableau de classification de boutique. Le sommaire complet se trouve sur la page Série Écosystème Apple.
Références
-
Apple, App Store Server Notifications changelog. Sous l’intitulé du 4 novembre 2025, rubrique New features : « Mise à jour de
responseBodyV2DecodedPayloadpour inclure le nouvel objet de charge utile,appData» et « Ajout du type de notificationRESCIND_CONSENTànotificationType». Les deux entrées suivantes du journal des modifications sont datées du 10 décembre 2025 et du 27 avril 2026, et aucune ne touche au consentement. Lu depuis le JSON de documentation d’Apple le 26 juillet 2026, la page HTML étant rendue par JavaScript. ↩ -
Apple, notificationType, App Store Server Notifications. Source de la définition de
RESCIND_CONSENT(« un type de notification indiquant que le parent ou le tuteur a retiré son consentement à l’utilisation de l’app par l’enfant ») et du décompte retenu ici : la page publie 23 valeurs possibles (CONSUMPTION_REQUEST, DID_CHANGE_RENEWAL_PREF, DID_CHANGE_RENEWAL_STATUS, DID_FAIL_TO_RENEW, DID_RENEW, EXPIRED, EXTERNAL_PURCHASE_TOKEN, GRACE_PERIOD_EXPIRED, METADATA_UPDATE, MIGRATION, OFFER_REDEEMED, ONE_TIME_CHARGE, PRICE_CHANGE, PRICE_INCREASE, REFUND, REFUND_DECLINED, REFUND_REVERSED, RENEWAL_EXTENDED, RENEWAL_EXTENSION, RESCIND_CONSENT, REVOKE, SUBSCRIBED, TEST). La chaîneRESCIND_CONSENTapparaît exactement une fois dans le contenu de la page, à l’intérieur de la liste des valeurs possibles ; les huit tableaux regroupés sous « Handle use cases for In-App Purchase life-cycle events » totalisent 40 lignes d’événements (4, 6, 7, 7, 8, 6, 6 et 4 lignes en comptant chaque en-tête) et aucun ne la nomme. À noter queREVOKEcorrespond à une perte de droit liée au partage familial, et non à un retrait de consentement : « un achat intégré auquel le client avait droit par le partage familial n’est plus disponible par ce biais ». Lu depuis le JSON de documentation d’Apple le 26 juillet 2026. ↩↩↩↩ -
Apple, appData, App Store Server Notifications, introduit à la version 2.19. Source de la phrase « L’objet
appDatafait partie deresponseBodyV2DecodedPayload. Cet objet est présent dans la charge utile lorsquenotificationTypevautRESCIND_CONSENT» et des quatre propriétés :appAppleId(« disponible pour les apps que les utilisateurs téléchargent depuis l’App Store ; absent dans l’environnement sandbox »),bundleId,environmentetsignedAppTransactionInfo(unJWSAppTransaction). La page responseBodyV2DecodedPayload énonce l’exclusivité de façon indépendante, en décrivantappDatacomme un champ qui « apparaît lorsquenotificationTypevautRESCIND_CONSENT», et en ajoutant que « les champsdata,appData,summaryetexternalPurchaseTokensont mutuellement exclusifs. La charge utile ne contient qu’un seul de ces champs ». Ces deux pages fondent l’affirmation selon laquelleRESCIND_CONSENTest le seul type de notification dont la charge utile transporteappData; la chaîneappDatan’apparaît nulle part sur la pagenotificationType. ↩↩↩↩ -
Apple, Next steps for apps distributed in Texas, Apple Developer News, 4 novembre 2025. Source des catégories d’âge texanes (« moins de 13 ans, 13-15, 16-17 ou plus de 18 »), du nom de framework employé par Apple (« l’API Significant Change du framework PermissionKit »), de la phrase sur la responsabilité du développeur (« il revient au développeur de déterminer quand son app connaît un changement significatif »), de l’exemple de classification d’âge (« Le droit texan considère un changement de classification d’âge d’une app comme un changement significatif, et les développeurs doivent tenir à jour leurs sélections de classification d’âge dans App Store Connect. Lorsqu’un développeur met à jour la classification d’âge de son app, celle-ci est mise à jour sur tous les appareils des utilisateurs dès que la version est en ligne »), du cadrage StoreKit (« Les développeurs peuvent utiliser un nouveau type de propriété dans StoreKit pour vérifier automatiquement si la classification d’âge de leur app a changé sur l’appareil d’un utilisateur, puis employer l’API Significant Change pour demander le consentement parental »), du comportement de révocation (« Un parent ou tuteur au Texas peut retirer son consentement pour n’importe quelle app, ce qui bloquera le lancement de cette app sur l’appareil de l’enfant ou de l’adolescent ») et de la liste de mise en œuvre en quatre points intitulée « Next steps ». Également source pour dater la disponibilité en bêta à iOS 26.2 et iPadOS 26.2. Récupéré le 26 juillet 2026. ↩↩↩↩↩↩↩↩↩
-
Apple, Update for Apps Distributed in Texas, Apple Developer News, 3 juin 2026. Source de la levée de l’injonction (« À la suite d’une décision de justice levant l’injonction visant la loi texane SB 2420, les nouveaux comptes Apple au Texas sont désormais soumis à la loi »), du périmètre (« vérification de l’âge et consentement du parent ou du tuteur au nom des mineurs de moins de 18 ans pour les téléchargements, les achats intégrés Apple et les changements significatifs associés à une app. Les parents ou tuteurs pourront également révoquer leur consentement pour toute app qu’ils avaient précédemment approuvée pour leur enfant »), de la date d’entrée en vigueur (« Ces changements entreront en vigueur à partir du 4 juin 2026 »), du rappel répété sur la responsabilité du développeur, et de la même liste de mise en œuvre en quatre points. Récupéré le 26 juillet 2026. ↩↩↩↩↩↩
-
Apple, Implementing age assurance and permissions, code d’exemple Declared Age Range. Disponibilité : iOS 26.5, iPadOS 26.5, Mac Catalyst 26.5, Xcode 27.0 bêta ; l’article demande de se connecter à iCloud « sur un appareil sous iOS 26.4 ou ultérieur » avant l’exécution. Source du comportement en cas d’ensemble vide (« lorsque aucune des deux fonctionnalités n’est présente, l’app saute entièrement le flux »), du seuil d’âge fixé à 18, des quatre catégories analysées (
.minor,.verifiedAdultdécrit comme « un adulte disposant d’un moyen de paiement confirmé »,.unverifiedAdultcomme « un adulte sans compte vérifié »,.declinedSharing), de l’issue réservée à l’adulte non vérifié (« l’app passe la phase à.blockedet empêche l’accès jusqu’à ce que la personne vérifie son compte dans Réglages »), du chemin mineur construisant unSignificantAppUpdateTopicet unePermissionQuestion, de l’envoi parPermissionButton, du listing de l’écouteurAskCenter.shared.responses(for:)cité mot pour mot ici, du comportement en cas de refus (« lorsque le parent refuse la demande, l’app empêche le mineur de l’utiliser »), du suivi des accusés de réception dansNSUbiquitousKeyValueStoreavecdidChangeExternallyNotification(« afin que les autres appareils ne présentent pas de nouveau le même flux »), et de l’exemption liée àoriginalAppVersion(« les personnes qui installent l’app alors qu’un changement significatif est déjà présent n’ont pas à en accuser réception »). Le projet exige également la capacité Declared Age Range et le service de stockage clé-valeur iCloud. Lu depuis le JSON de documentation d’Apple le 26 juillet 2026. ↩↩↩↩↩↩↩↩↩↩↩↩↩ -
Apple, AgeRangeService.requiredRegulatoryFeatures et AgeRangeService.RegulatoryFeature, Declared Age Range. Tous deux disponibles à partir d’iOS 26.4, iPadOS 26.4, Mac Catalyst 26.4 et macOS 26.4, sans aucune ligne visionOS, tvOS ou watchOS. Déclaration :
var requiredRegulatoryFeatures: Set<AgeRangeService.RegulatoryFeature> { get async throws }, qui lèvenotAvailable« si le service de la fonctionnalité réglementaire est indisponible ». L’énumération publie exactement trois cas :declaredAgeRangeRequired(« indique que la personne est tenue de partager sa tranche d’âge avec votre app »),significantAppChangeRequiresAdultNotification(« indique que les utilisateurs adultes doivent accuser réception du changement significatif de votre app ») etsignificantAppChangeRequiresParentalConsent(« indique qu’un parent ou tuteur doit prendre connaissance d’un changement significatif de l’app et y consentir »). ↩↩↩↩↩↩ -
Apple, Age assurance frameworks Q&A, Apple Developer Support. Source du plancher de SDK (« vous devez compiler votre app avec les SDK iOS 26.2 et iPadOS 26.2, ou ultérieurs, avec Xcode 26.2 (17C52) ou ultérieur »), de l’exception pour les comptes anciens (« les comptes Apple existants sous iOS 18 et iPadOS 18, ou antérieurs […] ne seront pas concernés », phrase dont la partie élidée précise « y compris les comptes adultes et les comptes enfants pour les enfants et les adolescents »), de la réponse sur la responsabilité (« Oui, les développeurs sont responsables de leurs propres restrictions d’âge » et « pour toute question sur vos obligations de conformité, consultez votre conseil juridique »), de la délimitation régionale citée deux fois dans cet article (« Dans certaines régions, lorsque la loi l’exige, Apple utilise des méthodes de vérification de l’âge pour confirmer l’âge du titulaire d’un compte Apple et vous communique des catégories d’âge via l’API Declared Age Range. Dans ces régions, vous devez vérifier l’âge des personnes qui utilisent votre app », reformulée plus loin en « Dans les régions où la loi l’exige, vous devez vérifier l’âge des personnes qui utilisent votre app à l’aide de l’API Declared Age Range »), de l’obligation d’accès (« Pour les mises à jour significatives d’app, vous êtes responsable d’empêcher l’accès à votre app ou à certaines fonctionnalités lorsque cela est requis, et de traiter la réponse du parent ou du tuteur. Tant que le parent n’a pas donné son consentement, l’enfant doit être empêché d’accéder à la mise à jour significative, ce qui peut inclure l’ensemble des données de l’app et du compte, ou des fonctionnalités précises »), du comportement de révocation (« Lorsqu’un parent ou un tuteur révoque le consentement donné à son enfant pour accéder à une app, Apple empêche le lancement de cette app. Pour gérer les révocations de consentement, utilisez la valeur
RESCIND_CONSENTdenotificationType»), de la réponse sur App Review (« Non, le processus d’App Review reste inchangé ») et de la réponse sur les conditions et la confidentialité (« Cela dépend. C’est à vous de déterminer ce qui constitue une mise à jour significative de l’app en fonction des lois applicables »). Consulté le 26 juillet 2026. ↩↩↩↩↩↩↩↩↩ -
Apple, Upcoming changes to age ratings in Australia and Vietnam, Apple Developer News, 21 mai 2026. Source de « À partir du 18 juin 2026, les classifications d’âge sur l’App Store seront mises à jour en Australie et au Vietnam », du changement australien (« La classification d’âge 15+ ne sera plus disponible sur l’App Store en Australie. Les apps actuellement classées 15+ portant les descripteurs de contenu suivants passeront en 16+ ») et de ses trois descripteurs (accès web non restreint ; informations médicales ou de traitement fréquentes ; coffres à butin), ainsi que du changement vietnamien (« Pour se conformer à l’article 38 du décret 147 du Vietnam, les apps disponibles sur l’App Store au Vietnam devront porter une classification d’âge spécifique à la région. En fonction de vos réponses au questionnaire de classification d’âge dans App Store Connect, votre app recevra l’une des quatre classifications suivantes : 00+ (tous publics), 12+, 16+ ou 18+ »). Aucun de ces changements ne demande au développeur de soumettre une compilation. Récupéré le 26 juillet 2026. ↩↩↩
-
Apple, AppStore.ageRatingCode, StoreKit. Déclaration
static var ageRatingCode: Int? { get async }, disponible à partir d’iOS 26.2, iPadOS 26.2, macOS 26.2, tvOS 26.2, visionOS 26.2 et watchOS 26.2, sans ligne Mac Catalyst. Valeur de retour : « un entier représentant le code de classification d’âge actuel, ounilsi la classification d’âge est indisponible ». Source du cadrage par comparaison (« Utilisez cette propriété pour récupérer la classification d’âge de votre app et la comparer à la dernière classification connue afin de vérifier si elle a changé ») et du renvoi vers PermissionKit (« Si la classification d’âge de votre app a changé, envisagez d’en informer les parents ou tuteurs à l’aide de l’API Significant Change »), phrase dans laquelle « Significant Change API » est le texte du lien et la pageSignificantAppUpdateTopicd’Apple en est la cible. L’exemple d’Apple sur cette page est unguard letautour de la propriété, qui imprime un message lorsque la valeur est indisponible. Recherche effectuée dans la documentation d’Apple le 26 juillet 2026 pour une correspondance entre ces entiers et les paliers de classification (4+, 9+, 13+, 16+, 18+) : rien sur cette page, ni sur la page du typeAppStore, ni dans la référence des classifications d’âge de l’aide App Store Connect. ↩↩↩↩↩↩↩↩↩ -
Apple, Testing age assurance in sandbox, StoreKit. Source du chemin sur l’appareil (Réglages, Développeur, Compte Apple Sandbox, Gérer, puis « Age Assurance ou Revoke App Consent »), de la matrice de tests à six lignes dont les lignes 18 ans et plus renvoient une borne inférieure de 18 sans borne supérieure et une déclaration d’âge
selfDeclaredouconfirmed, du comportement en cas de demande à un adulte (« Pour les cas de test 18+, PermissionKit lèveAskError.notAvailableau lieu de renvoyer unPermissionChoice. AppelerAskCenter.ask(_:)pour un utilisateur adulte lève cette erreur, car il ne remplit pas les conditions des demandes de permission parentale »), des étapes de révocation se concluant par la confirmation « Notification Triggered » avec « Une notification sera bientôt envoyée au serveur du développeur », et de la note sur la charge utile (« votre serveur reçoit unnotificationTypeRESCIND_CONSENT. La charge utile de la notification comprend un objetappDatacontenant des métadonnées de l’app, dont les champsbundleIdetenvironment»). Les trois lignes « mineur » de la matrice sont : moins de 13 ans approuvé, 13 à 15 approuvé et 16 à 17 refusé, toutes avec une déclaration d’âgeguardianDeclared. ↩↩↩↩↩ -
Apple, SignificantAppUpdateTopic, PermissionKit. Disponible à partir d’iOS 26.2, iPadOS 26.2, Mac Catalyst 26.2, macOS 26.2 et visionOS 26.2 ; déclaré
struct SignificantAppUpdateTopic, conforme àQuestionTopic, avecinit(description: String). Source du renvoi définitionnel (« C’est à vous de déterminer ce qui constitue une mise à jour significative en fonction de la réglementation applicable »), de la consigne de rédaction (« Employez un langage concis et compréhensible qui explique clairement ce qui a changé dans votre app. Les parents et les tuteurs voient cette description au moment de décider s’ils accordent leur permission ») et des deux commentaires de code du listing Specific/Vague reproduits mot pour mot dans cet article. ↩↩↩↩↩↩ -
Apple, New requirements for apps available in Texas, Apple Developer News, 8 octobre 2025. Source de l’annonce initiale (« À partir du 1er janvier 2026, une nouvelle loi de l’État du Texas […] introduit des exigences de vérification de l’âge pour les plateformes de distribution d’apps et les développeurs », le passage élidé nommant SB2420), de l’exigence de partage familial (« Tous les nouveaux comptes Apple des utilisateurs de moins de 18 ans devront rejoindre un groupe de partage familial, et les parents ou tuteurs devront donner leur consentement pour tous les téléchargements sur l’App Store, tous les achats d’apps et toutes les transactions effectuées par le mineur via le système d’achats intégrés d’Apple ») et de l’annonce anticipée pour l’Utah et la Louisiane (« Des exigences similaires entreront en vigueur plus tard l’an prochain »). Récupéré le 26 juillet 2026. ↩
-
Apple, Update on age requirements for apps distributed in Texas, Apple Developer News, 23 décembre 2025. Source de l’injonction (« Une injonction récemment prononcée par un tribunal de district a suspendu l’application de la loi texane SB2420 […]. Au vu de cette décision, Apple met en pause les plans de mise en œuvre annoncés précédemment et suit la procédure judiciaire en cours »), du maintien de la disponibilité des quatre outils en sandbox et de leur extension à l’Utah et à la Louisiane. Récupéré le 26 juillet 2026. ↩↩
-
Apple, Age requirements for apps distributed in Brazil, Australia, Singapore, Utah, and Louisiana, Apple Developer News, 24 février 2026. Source de la restriction de téléchargement 18+ (« À partir du 24 février 2026, Apple empêchera les utilisateurs en Australie, au Brésil et à Singapour de télécharger des apps classées 18+ à moins qu’ils n’aient été confirmés comme adultes par des méthodes raisonnables. L’App Store effectuera cette confirmation automatiquement. Les développeurs peuvent toutefois avoir des obligations distinctes de confirmer eux-mêmes que leurs utilisateurs sont adultes »), des dates pour l’Utah et la Louisiane (« Pour les utilisateurs disposant d’un nouveau compte Apple en Utah à partir du 6 mai 2026, et en Louisiane à partir du 1er juillet 2026, les catégories d’âge seront communiquées à l’app du développeur lorsqu’elles sont demandées via l’API Declared Age Range »), de la phrase d’extension citée dans cet article et des quatre liens qui la suivent (« Les outils que nous avons annoncés précédemment ont été étendus pour aider les développeurs à satisfaire leurs obligations de conformité en Louisiane et en Utah, à savoir : » API Declared Age Range, API Significant Change de PermissionKit, nouveau type de propriété de classification d’âge dans StoreKit, App Store Server Notifications), de la conséquence brésilienne sur les coffres à butin, et de la première mention publique de la Significant Update Action (« Les développeurs peuvent utiliser l’API Declared Age Range pour présenter des notifications de mise à jour significative aux adultes dans ces États via la Significant Update Action, désormais en bêta »). Récupéré le 26 juillet 2026. ↩↩↩↩↩
-
Apple, PermissionQuestion et expirationDate, PermissionKit.
final class PermissionQuestion<Topic> where Topic : QuestionTopic, disponible à partir d’iOS 26.0 avec quatre initialiseurs :init(handle:),init(handles:),init(communicationTopic:)etinit(significantAppUpdateTopic:), ce dernier introduit à iOS 26.2 et décrit comme créant « une question de permission qui demande au parent ou au tuteur l’autorisation de continuer à utiliser votre app après une mise à jour significative ».expirationDateest déclaréfinal var expirationDate: Date?avec la discussion « Une fois la date passée, la personne qui reçoit la question ne peut plus répondre ». Apple ne publie aucune valeur par défaut pour cette propriété, ni aucune consigne de réglage pour le sujet de mise à jour significative. ↩↩ -
Apple, Creating a communication experience, PermissionKit. Source du comportement d’annulation cité ici : « À tout moment pendant le flux d’envoi de la demande, l’enfant a la possibilité d’annuler la demande et de décider de ne pas envoyer la question à son parent ou tuteur. Dans ce cas, le système ne délivre aucune réponse à l’app appelante pour cette question précise ». Également source de la contrainte iMessage du framework, que la page d’accueil PermissionKit énonce dans un encadré Important : « Les expériences de communication utilisant le framework
PermissionKitne sont disponibles qu’avec iMessage ». À noter que l’article ne documente que le fluxCommunicationTopic; Apple ne publie aucun article équivalent pour le flux de mise à jour significative, et les exemples de code de l’article appellentCommunicationLimits.current.permissionResponses, un symbole qui renvoie une 404 dans la documentation d’Apple au 26 juillet 2026. ↩ -
Apple, AskError, PermissionKit.
enum AskError, conforme àLocalizedError, à six cas. Quatre portent un résumé et arrivent à iOS 26.1 :unknown,communicationLimitsNotEnabled(« indique que les limites de communication ne sont pas activées pour envoyer des demandes de permission »),contactSyncNotSetupetinvalidQuestion. Deux ne publient aucun résumé sur leur propre page :systemError(underlyingError:)à iOS 26.1, et notAvailable à iOS 26.2, en même temps queSignificantAppUpdateTopic. Le sens denotAvailablen’apparaît que dans l’article de test en sandbox cité à la note 11 ;systemError, lui, nomme au moins sa cause dans sa signature. Savoir sicommunicationLimitsNotEnabledoucontactSyncNotSetuppeuvent également survenir lors d’une demande de mise à jour significative n’est pas précisé. ↩ -
Apple, appTransactionID et originalAppVersion sur
AppTransaction, StoreKit. Source de la sémantique de l’identifiant (« L’App Store génère unappTransactionIDunique au monde pour chaque compte Apple qui télécharge votre app, et pour chaque membre du groupe familial dans le cas des apps prenant en charge le partage familial »), de sa stabilité à travers un nouveau téléchargement, un remboursement, un rachat et un changement de boutique, de sa présence dans les charges utiles des App Store Server Notifications version 2, et de la ligne décisive pour les apps gratuites : « L’appTransactionIDest disponible même si un client ne réalise aucun achat intégré ».originalAppVersionest « la version de l’app que le client a initialement achetée sur l’App Store », portantCFBundleShortVersionStringsur macOS etCFBundleVersionailleurs, et toujours1.0dans l’environnement sandbox. Le type correspondant côté serveur est appTransactionId dans l’API App Store Server, introduit à la version 1.15. ↩↩↩↩↩ -
Apple, CommunicationLimits et updates, PermissionKit.
updatesest déclaréfinal var updates: some AsyncSequence<PermissionResponse<CommunicationTopic>, Never> { get }et résumé ainsi : « Enregistre le sujet de communication auprès du système, afin que votre app puisse être lancée à la demande en arrière-plan pour recevoir les mises à jour de permission ». La documentation d’Apple regroupeupdateset les deux surcharges deCommunicationLimits.ask(_:in:)sous un intitulé API dépréciées ; la classe elle-même reste d’actualité pourisKnownHandle(_:)etknownHandles(in:). La séquence de remplacement abandonne la promesse : ni le résumé ni la discussion deAskCenter.responses(for:)ne mentionnent le lancement en arrière-plan, ce qui constitue exactement le manque documentaire signalé dans le corps de l’article. ↩ -
Apple, AskCenter et responses(for:), PermissionKit, tous deux disponibles à partir d’iOS 26.2, iPadOS 26.2, Mac Catalyst 26.2, macOS 26.2 et visionOS 26.2.
AskCenterest unefinal classaccessible parstatic let shared, décrite comme acheminant « vos questions par les canaux de partage familial appropriés » et délivrant « les réponses à votre app lorsque les parents prennent leur décision ».responses(for:)est déclaréfinal func responses<Topic>(for topicType: Topic.Type) -> some AsyncSequence<PermissionResponse<Topic>, Never> where Topic : QuestionTopicet résumé par « Enregistre le type de sujet auprès du système et renvoie une séquence asynchrone de réponses », sans mention du lancement en arrière-plan. Il existe quatre surcharges deask(_:in:): deux prenant unUIViewControllersur iOS, iPadOS et visionOS, et deux prenant unNSWindowsur macOS, soit une de chaque par type de sujet.PermissionResponseexposechoiceetquestion;PermissionChoice.Answerpublie exactement deux cas,approvaletdenial. ↩↩ -
Réponse Apple Staff sur AppStore.ageRatingCode always returns 0 on real device, Apple Developer Forums. Message d’origine d’avril 2026 signalant un
0sur un appareil physique sous iOS 26.4, avec un compte sandbox connecté et la classification d’âge configurée dans App Store Connect. La réponse, d’un auteur identifié comme Apple Staff, indique : « L’APIageRatingCodedoit servir à observer les changements de classification d’âge de votre app dans le temps, en comparant avec sa dernière valeur connue. Si le code de classification d’âge de votre app a changé, envisagez d’en informer les parents ou tuteurs à l’aide de l’API Significant Change », et « Une valeur de0est attendue pendant le développement de votre app, lorsqu’elle est compilée et exécutée depuis Xcode et depuis les environnements Sandbox (TestFlight compris) ». Récupérée deux fois le 26 juillet 2026 avec une formulation identique ; le forum est rendu par JavaScript et affiche l’ancienneté de la réponse sous la forme de l’horodatage relatif « 1w » plutôt qu’en date, aucune date de publication n’est donc rapportée ici. Une réponse de forum développeur constitue une preuve plus faible qu’une page de documentation, et la documentation d’Apple pourageRatingCodene mentionne pas du tout la valeur0. La conséquence tirée dans cet article, à savoir que conserver0comme référence fabrique un faux changement lors de la première compilation distribuée par l’App Store, est mon interprétation de cette réponse et du motif de comparaison documenté par Apple. ↩↩↩↩ -
Apple, Enabling App Store Server Notifications. Source du plancher TLS (« votre serveur doit prendre en charge le protocole Transport Layer Security (TLS) 1.2 ou ultérieur »), de la configuration d’une URL par environnement dans App Store Connect, de la contrainte de port (443, ou 1024 et au-delà) et du sous-réseau à autoriser (« ajoutez le sous-réseau d’adresses IP
17.0.0.0/8», lequel « s’applique aux environnements sandbox et production »). L’article associé Receiving App Store Server Notifications décrit lesignedPayloadsigné en JWS et, au 26 juillet 2026, ne mentionne que l’objetdatasans faire référence àappData. ↩ -
Apple, Responding to App Store Server Notifications. Source des codes de succès (« Renvoyez un HTTP
200, ou tout code HTTP compris entre200et206»), du déclencheur de nouvelle tentative (« Renvoyez un HTTP50xou40xpour que l’App Store retente la notification »), du calendrier de reprise en version 2 (« il retente cinq fois, à 1, 12, 24, 48 et 72 heures après la tentative précédente »), de la limitation en sandbox (« Les notifications de reprise ne sont disponibles que dans l’environnement de production. Dans l’environnement sandbox, le serveur de l’App Store tente d’envoyer la notification une seule fois ») et du chemin de récupération viaGet-Notification-History. ↩↩ -
Apple, subtype, App Store Server Notifications. La page publie 19 valeurs possibles (ACCEPTED, ACTIVE_TOKEN_REMINDER, AUTO_RENEW_DISABLED, AUTO_RENEW_ENABLED, BILLING_RECOVERY, BILLING_RETRY, CREATED, DOWNGRADE, FAILURE, GRACE_PERIOD, INITIAL_BUY, PENDING, PRICE_INCREASE, PRODUCT_NOT_FOR_SALE, RESUBSCRIBE, SUMMARY, UPGRADE, UNREPORTED, VOLUNTARY), chacune rattachée à un type de notification nommé. La chaîne
RESCIND_CONSENTn’apparaît nulle part dans le contenu de la page, lue le 26 juillet 2026. ↩ -
Apple, PermissionButton, PermissionKit.
@MainActor @preconcurrency struct PermissionButton<Topic, Label> where Topic : QuestionTopic, Label : View, disponible à partir d’iOS 26.2, iPadOS 26.2, Mac Catalyst 26.2, macOS 26.2 et visionOS 26.2, avec deux surcharges deinit(question:label:)contraintes respectivement àCommunicationTopicet àSignificantAppUpdateTopic. Il remplaceCommunicationLimitsButton, qu’Apple liste sous les API dépréciées sur la page du framework. ↩↩↩ -
Apple, AgeRangeService.showSignificantUpdateAcknowledgment(in:updateDescription:), SignificantUpdateAction et la valeur d’environnement SwiftUI showSignificantUpdateAcknowledgment. La méthode est déclarée
@MainActor func showSignificantUpdateAcknowledgment(in windowScene: UIWindowScene, updateDescription: String) async throwset publie iOS 26.4, iPadOS 26.4 et Mac Catalyst 26.4, sans ligne macOS ;AgeRangeServicene liste que cette seule surcharge sous « Displaying update acknowledgments ».SignificantUpdateActionet la valeur d’environnement publient les trois mêmes plateformes. L’encadré Important d’Apple sur la méthode : « Avant d’appeler cette fonction, vérifiezRegulatoryFeaturepour déterminer si une personne doit accuser réception de votre changement significatif d’app ». La discussion de la valeur d’environnement ajoute qu’il faut « appeler cette action depuis unButtonou depuisonAppear(perform:)». ↩↩↩↩ -
Apple, requestAgeRange(ageGates:::in:), Declared Age Range. Apple publie deux surcharges : l’une prenant
in viewController: UIViewControllersur iOS 26.0, iPadOS 26.0 et Mac Catalyst 26.0, l’autre prenantin window: NSWindowsur macOS 26.0. L’existence d’une varianteNSWindowpour la demande de tranche d’âge, et son absence pour la feuille d’accusé de réception citée à la note 27, fonde la lecture de l’omission macOS comme un manque plutôt que comme une décision de politique ; Apple n’a rien publié dans un sens ni dans l’autre. ↩ -
Apple, Requesting people’s age range information in your app, Declared Age Range. Source de l’arithmétique des seuils citée dans le corps de l’article (« Vous pouvez spécifier jusqu’à trois seuils d’âge, qui créent jusqu’à quatre tranches d’âge possibles »), de la contrainte d’écart (« Chaque tranche doit couvrir au moins deux années ») et de la sémantique des bornes (« Lorsque la valeur
lowerBoundestnil, la personne se situe en dessous de votre seuil d’âge le plus bas » et « LorsqueupperBoundestnil, la personne atteint ou dépasse votre seuil d’âge le plus élevé »). Des seuils à 13, 16 et 18 renvoient exactement les quatre tranches qu’Apple publie pour le Texas, et les deux tranches bornées respectent le minimum de deux ans : 13 à 15 couvre trois années et 16 à 17 en couvre deux. L’article avertit également que les régions auxquelles appartient le compte d’une personne « déterminent les seuils d’âge que le système utilise pour renvoyer les tranches d’âge, lesquels peuvent différer de ceux que vous spécifiez dans votre demande ». Lu depuis le JSON de documentation d’Apple le 26 juillet 2026. ↩ -
Enquête de l’auteur, 26 juillet 2026 : comment les affirmations relatives aux plateformes ont été établies. Les valeurs de plateforme sont lues dans le réglage de compilation
SUPPORTED_PLATFORMSde chaque projet plutôt que dans les clés de cible de déploiement, qu’Xcode écrit quelle que soit la destination : Reps déclareappletvos appletvsimulator iphoneos iphonesimulator macosx xros xrsimulatorplus une ciblewatchos watchsimulatordistincte, et Ace Citizenship déclare uniquementiphoneos iphonesimulator. Cette méthode sous-estime les plateformes, et Return en est la preuve : la seule valeurSUPPORTED_PLATFORMSde Return estiphoneos iphonesimulator macosx xros xrsimulator, tandis que ses cibles TV et montre portentSDKROOT = appletvosetSDKROOT = watchos, si bien qu’un balayage deSUPPORTED_PLATFORMSn’en voit aucune. Toute affirmation « livre sur la plateforme X » dans cet article provient donc d’App Store Connect et non d’un réglage de compilation. Return : TV_OS 1.0 et 1.0.1 toutes deux en READY_FOR_DISTRIBUTION, IOS et MAC_OS 1.0.1 également, et la cible iOS embarqueReturnWatch Watch App(com.941apps.Return.watchkitapp) via une phase Embed Watch Content, c’est ainsi qu’une app pour la montre atteint un poignet. Reps : IOS 1.1 et MAC_OS 1.1 en READY_FOR_DISTRIBUTION, aucune version TV_OS n’a jamais atteint cet état, et TV_OS 1.2 est en WAITING_FOR_REVIEW depuis le 2 juin 2026 ; Reps livre donc sur iOS et macOS. ↩ -
Enquête de l’auteur, 26 juillet 2026, sur macOS 26.5.2 (build 25F84) avec Xcode 26.6 (build 17F113) et Swift 6.3.3. Règle de sélection, énoncée pour que le périmètre puisse être vérifié et reproduit plutôt que cru sur parole : chaque ligne du tableau Active Projects de ma propre configuration d’agent (
~/.claude/CLAUDE.md) derrière laquelle se trouve un projet Xcode, ce qui en donne exactement sept :Reps,Return,Banana List(distribué sous le nom Get Bananas),Ace-Citizenship,Water,ResumeGeniAppetYawara. Cette règle est aussi le défaut de l’enquête, carRandorin’a pas de ligne dans ce tableau etRandoris’est révélé être le projet qui comptait. Cinq des sept ont une fiche App Store Connect (Reps 6776044339, Return 6756242021, Get Bananas 6756241534, Ace Citizenship 6532592671, ResumeGeni 6771154645) ;WateretYawarane figurent nulle part parmi les 18 apps du compte, aucun des deux n’a donc jamais été soumis, et les qualifier d’apps serait exagéré. Nombre de fichiers Swift par projet : 77, 57, 55, 26, 34, 71 et 143. Le balayage de consentement a couvert*.swift,*.entitlements,*.plistetproject.pbxproj: 85, 65, 63, 34, 38, 74 et 149, soit 508 au total. Reproduire ces décomptes exige d’exclure huit noms de répertoires plutôt que six :build,DerivedData,.build,Pods,.git,worktrees, et, pour Reps seulement,.venv(142 listes de propriétés réparties entreReps/.venvetReps/server/.venv) et.xcode-state-backups(18). N’excluez que les six premiers et Reps affiche 245 tandis que le total affiche 668 : la liste d’exclusion est donc structurante, et chacun des noms dont elle dépend est imprimé ici. Les 16 motifs, tous sensibles à la casse :PermissionKit,CommunicationLimits,AskPermission,AskCenter,SignificantAppUpdateTopic,SignificantUpdateAction,PermissionTopic,com.apple.developer.family-controls,FamilyControls,ManagedSettings,DeviceActivity,AuthorizationCenter,CKShare,sharedCloudDatabase,publicCloudDatabaseetageRatingCode. Zéro fichier correspondant dans les sept projets,AskCentercompris. Deux motifs témoins passés dans la même chaîne de traitement ont renvoyé des résultats non nuls (StoreKita trouvé trois fichiers dans Reps,CKContainer|NSPersistentCloudKitContainer|SwiftDataen a trouvé 17 dans Banana List), ce qui m’assure que la chaîne lit bien les fichiers et n’échoue pas silencieusement. L’accueil d’Ace Citizenship (IntroCarouselView,WelcomeView,AddStateView,AddRepresentativeView) ne comporte aucun champ d’âge ou de date de naissance, et sonPrivacyInfo.xcprivacydéclare unNSPrivacyCollectedDataTypesvide ; la mention d’éligibilité « 18 ans ou plus » provient de~/Projects/acecitizenship.app/content/blog/n400-application-guide.md. ↩↩↩ -
Enquête de l’auteur, 26 juillet 2026 : le balayage élargi et Randori. Le balayage élargi a passé les mêmes 16 motifs sur tous les dépôts de
~/Projects, en excluant les mêmes huit noms plusnode_moduleset les chemins ignorés par git, et a trouvé 21 fichiers. Sept sont du code : six fichiers Swift sousRandori/Randori/et_archive/Oishii-AI/Oishii AI/Services/CloudKitManager.swift(publicCloudDatabase, dans un projet archivé). Les 14 autres relèvent du texte ou de l’état machine : neuf plans et documents de conception Randori, trois articles dans lecontent/blog/de ce site (dont le brouillon du présent article), une note de transfert Obsidian, etobsidian-signals/40-Projects/blakecrosley-com/wwdc-2026/.pulse_state.json, écrit par un script et non par une personne. Randori estcom.wayofyawara.randori, app App Store Connect 6789693294, version 1.0 en PREPARE_FOR_SUBMISSION.CKShareetsharedCloudDatabasecorrespondent à 20 lignes réparties sur six fichiers :ConnectionStore.swift(10),RandoriApp.swift(5),ProfileCardView.swift(2), et une ligne chacun dansCKPostTransport.swift,CloudShareSheet.swiftetSocialContracts.swift.ConnectionStoredécrit sa propre forme dans son commentaire d’en-tête comme « une zone (ProfileCardZone), un type d’enregistrement (ConnectionCard), un partage », détientprivateCloudDatabaseetsharedCloudDatabasecôte à côte, et implémenteaccept(_ metadata: CKShare.Metadata)à la ligne 355,ensureOutboundShare()à la ligne 547, ainsi que le blocage et le déblocage par compte.RandoriApp.swiftimplémenteuserDidAcceptCloudKitShareWithà la fois sur le délégué d’app (ligne 73) et sur le délégué de scène de fenêtre (ligne 97, commenté « Warm: the app is running when the link is opened »), tandis queRandoriSceneDelegate.scene(_:willConnectTo:options:)litconnectionOptions.cloudKitShareMetadataà la ligne 88 sous le commentaire « Cold start: the invitation rides the connection options », d’où l’attribution, dans le corps de l’article, du démarrage à froid aux options de connexion plutôt qu’au rappel d’acceptation.TagConsentStoreest le registre de consentement propre à l’app, cloisonné par compte iCloud, et son comportement par défaut documenté est l’échec en position fermée : un partenaire dont l’enregistrement de politique n’est pas arrivé ne peut pas être identifié.Randori/Randori.entitlementsdemande CloudKit, HealthKit etaps-environment. Tous les motifs de consentement, hormis les deux relatifs au partage CloudKit, renvoient zéro dans ce dépôt, tout commeStoreKit,signedPayloadetnotificationType: l’app ne vend donc rien et n’exploite aucun serveur ;docs/asc-metadata.mdprépare un prix gratuit et une classification 4+. ↩↩↩↩ -
Enquête de l’auteur, 26 juillet 2026 : sites d’appel StoreKit et preuves côté serveur. StoreKit apparaît dans trois projets :
Reps/Reps/Services/RepsProStore.swift(renouvellement automatique, deux paliers),Ace Citizenship/StoreKitManager.swift(un non consommable) etResumeGeni/Subscription/(un abonnement mensuel, contrôlé côté serveur). Le traitement de.pendingcité dans le tableau se trouve àRepsProStore.swift:134(case .userCancelled, .pending:),StoreKitManager.swift:69-73(uncase .pending:distinct dont lereturn falseoccupe la ligne 73) etSubscriptionStore.swift:209-212(un cas d’attente nommé). Ce sont les sites d’appel qui laissent l’utilisateur en plan :RepsProPaywallView.swift:359-361ne referme la vue qu’à l’intérieur deif await store.purchase(plan), etContentView.swift:484-485ne déverrouille qu’à l’intérieur deif success; unfalserenvoyé pour un achat en attente ne change donc rien à l’écran dans l’une comme dans l’autre app. ResumeGeni atteint au contrairePaywallView.swift:526, une branche.pendingqui affiche le toast « Waiting for approval. You’ll get access once it’s approved. » Le point de terminaison App Store Server Notifications de ResumeGeni estPOST /api/appstore/notificationsdans~/Projects/resumegeni/app/routers/appstore.py; unPOSTde{"signedPayload":"probe"}renvoie un HTTP 400{"status":"invalid"}, réponse atteignable uniquement au-delà du drapeau d’activation et à l’intérieur du vérificateur de chaîne de certificats d’Apple, et unGETrenvoie 405. Les deux URL d’environnement sont enregistrées, et la preuve en est la livraison plutôt qu’un manuel d’exploitation : la base D1941-analytics, tablefunnel_events, contient 56 lignes oùplatform='ios'et dont les métadonnées portentnotification_type, réparties du 2026-06-26T15:48:51Z au 2026-07-25T16:40:11Z, dont 55 avec unenvironmentvalantsandboxet une valantproduction(2026-07-21T18:08:21Z). Considérez 56 comme une borne inférieure, car le service ne fait correspondre que cinq noms d’événements à des lignes de tunnel ; les quatre types effectivement observés sont DID_RENEW (42), SUBSCRIBED (6), EXPIRED (5) et DID_CHANGE_RENEWAL_STATUS (3).app/services/app_store_notification_service.pynomme 13 types de notification, dont huit atteignent une branche d’aiguillage exécutable dans_funnel_event_name(lignes 75 à 89) : SUBSCRIBED, OFFER_REDEEMED, DID_RENEW, REFUND, REVOKE, EXPIRED, DID_CHANGE_RENEWAL_STATUS et DID_FAIL_TO_RENEW. Les cinq autres n’apparaissent que dans des commentaires : PRICE_INCREASE, RENEWAL_EXTENDED et METADATA_UPDATE à la ligne 73, EXTERNAL_PURCHASE_TOKEN et TEST à la ligne 187.RESCIND_CONSENT,CONSUMPTION_REQUEST,REFUND_DECLINEDetREFUND_REVERSEDne renvoient aucune correspondance dans l’ensemble de ce dépôt. Par souci de complétude sur ce qui ne constitue pas une preuve :docs/SUBSCRIPTION_GO_LIVE.md:83dit bien « set BOTH the Production and Sandbox URL », mais cette ligne est une instruction de manuel d’exploitation, sous un intitulé précisant que l’étape « nécessite votre ré-authentification » ; elle consigne donc une intention et non un enregistrement effectué, et l’affirmation d’enregistrement ci-dessus repose sur les notifications livrées. ↩↩↩ -
Apple, Testing Ask to Buy in Xcode, StoreKit. Source de la description du mécanisme par Apple (« Avec Ask to Buy, lorsqu’un enfant souhaite effectuer un achat ou un téléchargement éligible, le système envoie la demande d’achat au parent ou au tuteur ») et du comportement en cas de refus (« Votre app ne reçoit aucune transaction, car vous avez refusé Ask to Buy »). L’article documente également le commutateur Ask to Buy sous Purchase Options dans l’éditeur de configuration StoreKit, ainsi que les états Pending Ask to Buy, Ask to Buy Approved et Ask to Buy Declined affichés par le gestionnaire de transactions. ↩↩↩
-
Apple, Product.PurchaseResult.pending et Transaction.updates, StoreKit, tous deux disponibles à partir d’iOS 15.0. Source du résumé du cas (« L’achat est en attente et nécessite une action du client »), du chemin de résolution (« Si un achat en attente aboutit, StoreKit délivre la
Transactionrésultante dans lesupdatesde transactions ») et de la finalité de la séquence (« Cette séquence reçoit les transactions qui se produisent en dehors de l’app, comme les transactions Ask to Buy, les échanges de codes promotionnels et les achats effectués par les clients dans l’App Store »). L’énumération Product.PurchaseResult publie trois cas :success(_:),pendingetuserCancelled. L’exemple d’Apple sur la page de l’énumération commente la branche d’attente ainsi : « L’achat nécessite une action du client. Si la transaction aboutit, elle est disponible viaTransaction.updates». ↩↩ -
Tous les symboles du listing d’aiguillage ci-dessus ont été vérifiés dans le JSON de documentation d’Apple le 26 juillet 2026.
AgeRangeService.sharedeststatic let shared: AgeRangeService(iOS 26.0). Les valeurs d’environnement SwiftUI sontrequestAgeRange,var requestAgeRange: DeclaredAgeRangeAction { get }(iOS 26.0), etshowSignificantUpdateAcknowledgment,var showSignificantUpdateAcknowledgment: SignificantUpdateAction { get }(iOS 26.4). Le plancher@available(iOS 26.5, *)du listing ne vient d’aucune des deux :AgeRangeService.AgeRangeDeclaration.confirmedpublie iOS 26.5, iPadOS 26.5, Mac Catalyst 26.5 et macOS 26.5, soit une version après l’action d’accusé de réception ; tout code qui distingue un adulte confirmé hérite donc du plancher le plus élevé. Le projet d’exemple d’Apple publie la même disponibilité 26.5.6AgeRangeService.AgeRangeexposelowerBound,upperBound,ageRangeDeclarationdéclarévar ageRangeDeclaration: AgeRangeService.AgeRangeDeclaration?, etactiveParentalControls. La comparaison== .confirmedsur cette valeur optionnelle est valide carAgeRangeDeclarationest conforme àEquatableetHashabled’après sa section relationships. L’initialiseur dePermissionButtonestinit(question: PermissionQuestion<Topic>, @ViewBuilder label: @escaping () -> Label), contraint àSignificantAppUpdateTopicdans la surcharge employée ici.26ChangedFeatureViewetAccountVerificationPromptsont des espaces réservés pour vos propres vues, et non des symboles Apple. ↩↩↩ -
Apple, Age ratings values and definitions, aide App Store Connect, consultée le 26 juillet 2026, comme source confirmant que les changements du 18 juin 2026 annoncés à la note 9 sont entrés en vigueur. Sous « Australia age rating values », le tableau publie désormais deux classifications, 16+ et R 18+, sans ligne 15+. Une section distincte « Vietnam age rating values » existe maintenant, introduite par « Comme l’exige l’article 38 du décret 147 du Vietnam », et sa ligne 00+ est définie comme désignant les apps qui « ne contiennent aucun contenu répréhensible mais peuvent comporter des occurrences des contenus suivants », avec l’énumération suivante : contrôles parentaux, vérification de l’âge, contenu généré par les utilisateurs, messagerie et chat, publicité, et concours peu fréquents. La liste de descripteurs publiée par Apple le 21 mai pour la migration australienne ne correspond pas à la liste de déclencheurs 16+ actuellement affichée sur cette page, une divergence que je n’ai pas résolue et sur laquelle je ne m’appuie pas, l’affirmation retenue ici se limitant à la disparition du palier 15+ et à l’existence du tableau vietnamien. ↩↩↩