Guideline 2.3.1 : fonctions cachées et mises à jour OTA
Par Mehdi Azizi
Publié le , 9 min
Guideline 2.3.1 : Apple refuse toute fonction cachée, dormante ou absente des notes de revue. Ce que tu peux changer à distance et ce qui exige un build.
La guideline 2.3.1 interdit les fonctions cachées, dormantes ou non documentées : tout ce que fait ton app doit être visible pour l'utilisateur et pour le reviewer. Chaque nouveauté se décrit précisément dans les notes de revue. Une mise à jour à distance peut corriger ce qu'Apple a vu, jamais ajouter ce qu'Apple n'a pas vu.
Je publie mes apps iOS avec Expo, et chacune embarque EAS Update dès son premier build pour livrer des correctifs. Ma règle : aucune mise à jour à distance pendant qu'une version est en revue. Ce guide s'appuie sur le texte d'Apple, sa licence, la documentation d'Expo et des refus publiés.
Que dit la guideline 2.3.1 ?
La 2.3.1 ouvre la partie 2.3 « Accurate Metadata » des App Review Guidelines, dans la section 2 « Performance ». Le texte commence ainsi : « Don't include any hidden, dormant, or undocumented features in your app; your app's functionality should be clear to end users and App Review. » En clair : rien de caché, d'endormi ou de non documenté.
La suite vise chaque soumission : « All new features, functionality, and product changes must be described with specificity in the Notes for Review section of App Store Connect (generic descriptions will be rejected) and accessible for review. » Chaque nouveauté doit être décrite précisément dans les notes de revue, et accessible au reviewer. « Corrections de bugs et améliorations » ne suffit donc pas.
Le même alinéa vise le marketing trompeur : promettre un contenu ou un service absent, ou afficher un faux prix, dans l'App Store ou ailleurs.
Ce volet m'a servi sur Nolera. En relisant sa fiche avant la sortie, j'ai trouvé deux phrases invérifiables : « interface en français uniquement », devenue fausse, et « le pack le plus choisi », pour une app jamais publiée. Les notes au reviewer décrivaient aussi un état dépassé du produit. C'est exactement ce que vise ce volet. La 2.3.10, elle, vise surtout la fiche.
Que veut dire le message de refus 2.3.1 ?
Le refus s'intitule « Guideline 2.3.1 - Performance », parfois suivi de « Hidden Features ». Deux messages publiés par des développeurs React Native :
- Mars 2024, dans un ticket GitHub de CodePush : « The app contains hidden features. », sans autre précision. Le développeur demande des précisions et reçoit un complément au titre de la 2.5.2 : « Your app, extension, or linked framework appears to contain code designed explicitly with the capability to change the app's behavior or functionality after App Review approval. » Le ticket ne dit pas si l'app est passée ensuite.
- Février 2023, dans un autre ticket, Apple vise le « CodePush-related remote code downloading/updating support » du binaire.
Ma lecture : le reviewer ne voit pas tes mises à jour, il voit le code capable d'en recevoir. Ces deux cas visent CodePush. Je n'ai pas trouvé de refus public récent visant EAS Update, ce qui ne prouve rien.
Mises à jour à distance : ce que permettent la 2.5.2 et la licence
La règle 2.5.2, que j'aborde aussi dans le guide des modes d'arrière-plan, interdit à l'app de « download, install, or execute code which introduces or changes features or functionality of the app ».
L'article 3.3.1(B) de l'Apple Developer Program License Agreement interdit le code exécutable téléchargé, mais précise : « Interpreted code may be downloaded to an Application ». À trois conditions : ce code ne change pas l'objet principal de l'app, il ne contourne ni la signature, ni le bac à sable, ni les sécurités du système, et il ne crée pas de boutique d'autres apps.
Le JavaScript d'une app Expo est du code interprété : à mon sens, EAS Update entre dans ce cadre. Sa documentation rappelle que les mises à jour suivent les règles de l'App Store, et que « changes to your app's behavior need to be reviewed ». Pour un bug critique, Expo propose le correctif à distance, puis une nouvelle soumission qui l'embarque.
Ma ligne : corriger et ajuster ce qui existe, oui. Changer ce que l'app est ou ce qu'elle vend, non.
Ce que tu peux changer à distance, ce qui exige un nouveau build
La grille que j'applique, tirée des pages d'Apple et d'Expo citées ici. La ligne « gratuit qui devient payant » et les deux dernières sont ma lecture.
| Changement | Voie | Pourquoi |
|---|---|---|
| Bug JavaScript | Mise à jour à distance | Usage prévu par Expo |
| Texte, traduction, style | Mise à jour à distance | Usage prévu par Expo |
| Prix d'un abonnement | App Store Connect | Aucun code modifié |
| Texte promotionnel | App Store Connect | Sans nouvelle soumission |
| Achat d'un type déjà validé | Revue de l'achat seul | Règle d'App Store Connect |
| Premier achat d'un type | Nouvelle version | Règle d'App Store Connect |
| Nouvelle fonction | Nouvelle version | 2.3.1 et 2.5.2 |
| Gratuit qui devient payant | Nouvelle version | Fiche et notes à revoir |
| Permission, Info.plist | Nouveau build | Fichier du binaire |
| Module natif, SDK Expo | Nouveau build | Code natif changé |
| Interrupteur qui coupe | Configuration distante | Toléré si temporaire |
| Interrupteur qui révèle | Interdit | Fonction jamais revue |
Le texte promotionnel se change « without requiring an updated submission », selon la page d'Apple sur les informations de version. Le premier achat de chaque type part avec une nouvelle version, les suivants seuls, selon l'aide sur la soumission des achats. Expo range code natif, permissions et version du SDK parmi ce qui demande un nouveau binaire, comme pour ITMS-90683. Un interrupteur qui coupe une fonction en panne n'ajoute rien d'inédit, tant qu'il ne retire pas ce que les abonnés ont payé.
Les interrupteurs à distance sans refus
Un interrupteur à distance, ou feature flag, n'est pas interdit en soi : tout dépend de ce qu'il cache. Mes règles :
- Tout ce qu'un interrupteur peut afficher est présent et allumé pour le reviewer, et nommé dans les notes de revue.
- Un test A/B compare deux variantes que le reviewer peut voir, jamais une variante revue et une inédite.
- Aucun code ne détecte la revue pour se comporter autrement. C'est la définition même d'une fonction cachée.
- Un paywall piloté à distance ne montre que des produits validés, et chaque variante respecte la règle 3.1.2.
Mode démo et comptes de test
La guideline 2.1 demande un compte de démonstration si ton app a une connexion, avec le serveur allumé. Si des obligations légales ou de sécurité t'empêchent d'en fournir un, un mode démo intégré peut le remplacer, mais seulement « with prior approval by Apple », et il doit montrer toutes les fonctions. La page d'Apple sur les informations de version ajoute : le compte n'expire pas, et les comptes supplémentaires vont dans les notes, limitées à 4 000 octets.
Le lien avec la 2.3.1 : un mode démo ou un compte de test qui ouvre des écrans invisibles autrement doit être décrit. Un geste secret, un code ou un compte spécial non signalé, c'est une fonction cachée. Nomme chaque accès spécial dans les notes, avec ce qu'il ouvre.
Ma règle : aucune mise à jour à distance pendant une revue
Pourquoi EAS Update est partout chez moi : sur Solys, mon réveil, un bug JavaScript rendait l'app muette dès qu'un bruitage ratait une fois. Le correctif tenait en trois lignes, mais il fallait un build complet et une revue pour le livrer. Depuis, toutes mes apps embarquent expo-updates dès leur premier build : c'est un module natif, un binaire compilé sans lui ne recevra jamais rien. Ma configuration, dans app.json :
{
"expo": {
"runtimeVersion": { "policy": "fingerprint" },
"updates": { "fallbackToCacheTimeout": 0, "checkAutomatically": "ON_LOAD" }
}
}
Avec fingerprint, une mise à jour n'atteint que les binaires dont le code natif correspond (versions d'exécution). Mais pendant qu'une version attend ou passe la revue, je ne publie rien. Deux raisons.
- Le reviewer ne verrait pas le correctif. Avec
fallbackToCacheTimeoutà 0, une installation neuve démarre sur le code embarqué et applique la mise à jour au lancement suivant (documentation d'expo-updates). Le reviewer installe, lance une fois et teste le binaire. Nolera a été refusée avec un correctif déjà livré à distance : je le raconte dans le guide de la 2.1. - Ce que le reviewer teste doit rester ce que les utilisateurs reçoivent. Changer le comportement pendant la revue crée un écart entre la version vue et la version livrée.
Des notes de revue précises
La 2.3.1 refuse les descriptions génériques. Le modèle que j'utiliserais, en anglais :
What's new in this version:
- [Feature]: [where to find it, how to test it].
Remote configuration:
- "[flag]" controls [feature]. It is ON for review.
Over-the-air updates:
- The app uses expo-updates for bug fixes only.
No update will be published during this review.
Demo account: see Sign-in information. It never expires.
Comment répondre à un refus 2.3.1 ?
Réponds dans App Store Connect, où tu réponds au message d'App Review.
- Le message reste vague : demande quelle fonction est visée, comme le développeur du ticket de 2024.
- Une vraie fonction est mal décrite : donne le chemin exact et une courte vidéo, puis complète les notes.
- Un interrupteur révèle de l'inédit : retire-le, ou allume-le et décris-le, puis refais un build.
- Le code de mise à jour est en cause : explique qu'il sert aux correctifs, dans le cadre de l'article 3.3.1(B). Dans le ticket de 2023, Apple demandait de le retirer.
La checklist 2.3.1 avant soumission
- Chaque nouveauté de la version est décrite dans les notes, avec son chemin.
- Chaque interrupteur à distance est allumé pour la revue et nommé dans les notes.
- Aucun code ne change de comportement pour le reviewer.
- Les accès spéciaux sont décrits : mode démo, compte de test, geste.
- La fiche et le paywall ne promettent rien que le build ne fait pas.
- Aucune mise à jour à distance n'est publiée pendant la revue.
Vérifier ton dossier avant l'envoi
Le Kit Review Apple réunit ma checklist avant l'envoi, des modèles de notes pour le testeur et des skills Claude Code qui relisent ton projet. Pour un regard humain, réserve une heure avec moi : on relit tes notes de revue et ta configuration de mises à jour ensemble, en visio. La démarche complète est sur la page application refusée par Apple.
Tu prépares ta première sortie ? Fais ton diagnostic gratuit : il remet les étapes dans l'ordre et t'envoie ma checklist anti-refus Apple.
Questions fréquentes
Que veut dire « Guideline 2.3.1 - Performance - Hidden Features » ?
Apple estime que ton app contient une fonction cachée, dormante ou absente des notes de revue. Le message peut aussi viser un code capable de changer l'app après la revue, parfois avec un refus 2.5.2.
EAS Update est-il autorisé par Apple ?
La licence autorise le code interprété téléchargé tant qu'il ne change pas l'objet principal de l'app. Corriger, oui. Ajouter une fonction non revue, non.
Peut-on utiliser des feature flags dans une app iOS ?
Oui, si tout ce qu'ils peuvent afficher a été revu. Allume chaque interrupteur pour la revue et décris-le dans les notes.
Faut-il signaler un mode démo au reviewer ?
Oui. Un mode démo intégré demande l'accord préalable d'Apple et doit montrer toutes les fonctions.
Peut-on publier une mise à jour à distance pendant la revue ?
Rien ne l'interdit noir sur blanc. Mais le reviewer ne la verrait pas au premier lancement, et elle crée un écart avec la version revue. Je m'en abstiens toujours.

