Guideline 2.3.1: κρυφές λειτουργίες και ενημερώσεις OTA
Από Mehdi Azizi
Δημοσιεύτηκε στις , 9 λ.
Guideline 2.3.1: η Apple απορρίπτει κάθε κρυφή, αδρανή ή μη δηλωμένη λειτουργία. Τι αλλάζεις εξ αποστάσεως και τι θέλει νέο build.
Η guideline 2.3.1 απαγορεύει τις κρυφές, αδρανείς ή μη τεκμηριωμένες λειτουργίες: ό,τι κάνει η εφαρμογή σου πρέπει να είναι ορατό στον χρήστη και στον reviewer. Κάθε νέα λειτουργία περιγράφεται με ακρίβεια στις σημειώσεις για το review. Μια ενημέρωση εξ αποστάσεως μπορεί να διορθώσει ό,τι είδε η Apple, ποτέ να προσθέσει κάτι που η Apple δεν είδε.
Δημοσιεύω τις iOS εφαρμογές μου με Expo, και καθεμία έχει ενσωματωμένο το EAS Update από το πρώτο της build για να στέλνω διορθώσεις. Ο κανόνας μου: καμία ενημέρωση εξ αποστάσεως όσο μια έκδοση βρίσκεται σε review. Αυτός ο οδηγός βασίζεται στο κείμενο της Apple, στην άδεια χρήσης της, στην τεκμηρίωση του Expo και σε απορρίψεις που έχουν δημοσιευτεί.
Τι λέει η guideline 2.3.1;
Η 2.3.1 ανοίγει το τμήμα 2.3 «Accurate Metadata» των App Review Guidelines, στην ενότητα 2 «Performance». Το κείμενο ξεκινά έτσι: «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.» Με απλά λόγια: τίποτα κρυφό, αδρανές ή μη τεκμηριωμένο.
Η συνέχεια αφορά κάθε υποβολή: «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.» Κάθε νέα λειτουργία πρέπει να περιγράφεται με ακρίβεια στις σημειώσεις για το review και να είναι προσβάσιμη στον reviewer. Άρα το «Διορθώσεις σφαλμάτων και βελτιώσεις» δεν αρκεί.
Η ίδια παράγραφος στοχεύει και το παραπλανητικό μάρκετινγκ: να υπόσχεσαι περιεχόμενο ή υπηρεσία που δεν υπάρχει, ή να δείχνεις ψεύτικη τιμή, στο App Store ή οπουδήποτε αλλού.
Αυτό το σκέλος μού χρησίμευσε στη Nolera. Ξαναδιαβάζοντας τη σελίδα της πριν την κυκλοφορία, βρήκα δύο φράσεις που δεν μπορούσαν να επαληθευτούν: «περιβάλλον μόνο στα γαλλικά», που είχε πάψει να ισχύει, και «το πιο δημοφιλές πακέτο», για μια εφαρμογή που δεν είχε κυκλοφορήσει ποτέ. Οι σημειώσεις προς τον reviewer περιέγραφαν επίσης μια ξεπερασμένη κατάσταση του προϊόντος. Αυτό ακριβώς στοχεύει αυτό το σκέλος. Η 2.3.10, από την άλλη, αφορά κυρίως τη σελίδα του App Store.
Τι σημαίνει το μήνυμα απόρριψης 2.3.1;
Η απόρριψη έχει τίτλο «Guideline 2.3.1 - Performance», μερικές φορές ακολουθούμενο από «Hidden Features». Δύο μηνύματα που δημοσίευσαν developers React Native:
- Μάρτιος 2024, σε ένα issue του CodePush στο GitHub: «The app contains hidden features.», χωρίς άλλη εξήγηση. Ο developer ζητά διευκρινίσεις και παίρνει συμπλήρωμα με βάση την 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.» Το issue δεν λέει αν η εφαρμογή πέρασε μετά.
- Φεβρουάριος 2023, σε ένα άλλο issue, η Apple στοχεύει το «CodePush-related remote code downloading/updating support» του binary.
Η δική μου ανάγνωση: ο reviewer δεν βλέπει τις ενημερώσεις σου, βλέπει τον κώδικα που μπορεί να τις δεχτεί. Και οι δύο περιπτώσεις αφορούν το CodePush. Δεν βρήκα πρόσφατη δημόσια απόρριψη για το EAS Update, πράγμα που δεν αποδεικνύει τίποτα.
Ενημερώσεις εξ αποστάσεως: τι επιτρέπουν η 2.5.2 και η άδεια χρήσης
Ο κανόνας 2.5.2, που αναφέρω και στον οδηγό για τα background modes, απαγορεύει στην εφαρμογή να «download, install, or execute code which introduces or changes features or functionality of the app».
Το άρθρο 3.3.1(B) του Apple Developer Program License Agreement απαγορεύει τον εκτελέσιμο κώδικα που κατεβαίνει, αλλά διευκρινίζει: «Interpreted code may be downloaded to an Application». Με τρεις προϋποθέσεις: ο κώδικας αυτός δεν αλλάζει τον βασικό σκοπό της εφαρμογής, δεν παρακάμπτει ούτε την υπογραφή, ούτε το sandbox, ούτε τις ασφαλιστικές δικλίδες του συστήματος, και δεν δημιουργεί κατάστημα για άλλες εφαρμογές.
Η JavaScript μιας εφαρμογής Expo είναι interpreted κώδικας: κατά τη γνώμη μου, το EAS Update χωράει σε αυτό το πλαίσιο. Η τεκμηρίωσή του θυμίζει ότι οι ενημερώσεις ακολουθούν τους κανόνες του App Store και ότι «changes to your app's behavior need to be reviewed». Για ένα κρίσιμο bug, το Expo προτείνει τη διόρθωση εξ αποστάσεως και μετά μια νέα υποβολή που την ενσωματώνει.
Η γραμμή μου: διορθώνω και ρυθμίζω ό,τι υπάρχει, ναι. Αλλάζω το τι είναι η εφαρμογή ή τι πουλάει, όχι.
Τι αλλάζεις εξ αποστάσεως και τι θέλει νέο build
Ο πίνακας που εφαρμόζω, βγαλμένος από τις σελίδες της Apple και του Expo που αναφέρω εδώ. Η γραμμή «δωρεάν που γίνεται επί πληρωμή» και οι δύο τελευταίες είναι δική μου ερμηνεία.
| Αλλαγή | Δρόμος | Γιατί |
|---|---|---|
| Bug στη JavaScript | Ενημέρωση εξ αποστάσεως | Χρήση που προβλέπει το Expo |
| Κείμενο, μετάφραση, στυλ | Ενημέρωση εξ αποστάσεως | Χρήση που προβλέπει το Expo |
| Τιμή συνδρομής | App Store Connect | Καμία αλλαγή στον κώδικα |
| Promotional text | App Store Connect | Χωρίς νέα υποβολή |
| Αγορά ήδη εγκεκριμένου τύπου | Review μόνο της αγοράς | Κανόνας του App Store Connect |
| Πρώτη αγορά ενός τύπου | Νέα έκδοση | Κανόνας του App Store Connect |
| Νέα λειτουργία | Νέα έκδοση | 2.3.1 και 2.5.2 |
| Δωρεάν που γίνεται επί πληρωμή | Νέα έκδοση | Σελίδα και σημειώσεις ξανά |
| Άδεια, Info.plist | Νέο build | Αρχείο του binary |
| Native module, Expo SDK | Νέο build | Αλλαγή στον native κώδικα |
| Διακόπτης που απενεργοποιεί | Απομακρυσμένη ρύθμιση | Ανεκτό αν είναι προσωρινό |
| Διακόπτης που αποκαλύπτει | Απαγορεύεται | Λειτουργία που δεν ελέγχθηκε |
Το promotional text αλλάζει «without requiring an updated submission», σύμφωνα με τη σελίδα της Apple για τις πληροφορίες έκδοσης. Η πρώτη αγορά κάθε τύπου υποβάλλεται μαζί με νέα έκδοση, οι επόμενες μόνες τους, σύμφωνα με τη βοήθεια για την υποβολή των αγορών. Το Expo κατατάσσει τον native κώδικα, τις άδειες και την έκδοση του SDK σε όσα απαιτούν νέο binary, όπως και για το ITMS-90683. Ένας διακόπτης που απενεργοποιεί μια χαλασμένη λειτουργία δεν προσθέτει τίποτα καινούργιο, αρκεί να μην αφαιρεί κάτι που πλήρωσαν οι συνδρομητές.
Διακόπτες εξ αποστάσεως χωρίς απόρριψη
Ένας διακόπτης εξ αποστάσεως, ή feature flag, δεν απαγορεύεται από μόνος του: όλα εξαρτώνται από το τι κρύβει. Οι κανόνες μου:
- Ό,τι μπορεί να εμφανίσει ένας διακόπτης υπάρχει και είναι ενεργό για τον reviewer, και αναφέρεται στις σημειώσεις για το review.
- Ένα A/B test συγκρίνει δύο παραλλαγές που ο reviewer μπορεί να δει, ποτέ μια ελεγμένη με μια ανέλεγκτη.
- Κανένας κώδικας δεν ανιχνεύει το review για να συμπεριφερθεί διαφορετικά. Αυτός είναι ο ίδιος ο ορισμός της κρυφής λειτουργίας.
- Ένα paywall που ελέγχεται εξ αποστάσεως δείχνει μόνο εγκεκριμένα προϊόντα, και κάθε παραλλαγή τηρεί τον κανόνα 3.1.2.
Demo mode και test λογαριασμοί
Η guideline 2.1 ζητά demo λογαριασμό αν η εφαρμογή σου έχει σύνδεση, με τον server σε λειτουργία. Αν νομικές υποχρεώσεις ή λόγοι ασφαλείας σε εμποδίζουν να δώσεις λογαριασμό, ένα ενσωματωμένο demo mode μπορεί να τον αντικαταστήσει, αλλά μόνο «with prior approval by Apple», και πρέπει να δείχνει όλες τις λειτουργίες. Η σελίδα της Apple για τις πληροφορίες έκδοσης προσθέτει: ο λογαριασμός δεν λήγει, και οι επιπλέον λογαριασμοί μπαίνουν στις σημειώσεις, που έχουν όριο 4.000 bytes.
Η σύνδεση με την 2.3.1: ένα demo mode ή ένας test λογαριασμός που ανοίγει οθόνες αλλιώς αόρατες πρέπει να περιγράφεται. Μια μυστική κίνηση, ένας κωδικός ή ένας ειδικός λογαριασμός που δεν δηλώνεται είναι κρυφή λειτουργία. Ανέφερε κάθε ειδική πρόσβαση στις σημειώσεις, μαζί με το τι ανοίγει.
Ο κανόνας μου: καμία ενημέρωση εξ αποστάσεως κατά το review
Γιατί το EAS Update υπάρχει σε όλες μου τις εφαρμογές: στη Solys, το ξυπνητήρι μου, ένα bug στη JavaScript έκανε την εφαρμογή βουβή μόλις ένα ηχητικό εφέ αποτύγχανε μία φορά. Η διόρθωση ήταν τρεις γραμμές, αλλά χρειαζόταν πλήρες build και review για να φτάσει στους χρήστες. Από τότε, όλες μου οι εφαρμογές ενσωματώνουν το expo-updates από το πρώτο τους build: είναι native module, ένα binary που μεταγλωττίστηκε χωρίς αυτό δεν θα λάβει ποτέ τίποτα. Η ρύθμισή μου, στο app.json:
{
"expo": {
"runtimeVersion": { "policy": "fingerprint" },
"updates": { "fallbackToCacheTimeout": 0, "checkAutomatically": "ON_LOAD" }
}
}
Με το fingerprint, μια ενημέρωση φτάνει μόνο στα binaries με αντίστοιχο native κώδικα (runtime versions). Όμως όσο μια έκδοση περιμένει ή περνά review, δεν δημοσιεύω τίποτα. Για δύο λόγους.
- Ο reviewer δεν θα έβλεπε τη διόρθωση. Με το
fallbackToCacheTimeoutστο 0, μια καινούργια εγκατάσταση ξεκινά με τον ενσωματωμένο κώδικα και εφαρμόζει την ενημέρωση στο επόμενο άνοιγμα (τεκμηρίωση του expo-updates). Ο reviewer εγκαθιστά, ανοίγει μία φορά και δοκιμάζει το binary. Η Nolera απορρίφθηκε ενώ η διόρθωση είχε ήδη σταλεί εξ αποστάσεως: το διηγούμαι στον οδηγό για την 2.1. - Αυτό που δοκιμάζει ο reviewer πρέπει να παραμένει αυτό που λαμβάνουν οι χρήστες. Αν αλλάξεις τη συμπεριφορά κατά το review, δημιουργείς απόκλιση ανάμεσα στην έκδοση που ελέγχθηκε και σε αυτή που παραδόθηκε.
Ακριβείς σημειώσεις για το review
Η 2.3.1 απορρίπτει τις γενικόλογες περιγραφές. Το πρότυπο που θα χρησιμοποιούσα, στα αγγλικά:
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.
Πώς απαντάς σε μια απόρριψη 2.3.1;
Απάντησε μέσα από το App Store Connect, όπου απαντάς στο μήνυμα του App Review.
- Το μήνυμα μένει αόριστο: ρώτα ποια λειτουργία εννοούν, όπως έκανε ο developer στο issue του 2024.
- Μια πραγματική λειτουργία είναι κακώς περιγεγραμμένη: δώσε την ακριβή διαδρομή και ένα σύντομο βίντεο, και μετά συμπλήρωσε τις σημειώσεις.
- Ένας διακόπτης αποκαλύπτει κάτι ανέλεγκτο: αφαίρεσέ τον, ή ενεργοποίησέ τον και περιέγραψέ τον, και μετά κάνε νέο build.
- Το θέμα είναι ο κώδικας ενημερώσεων: εξήγησε ότι χρησιμεύει για διορθώσεις, στο πλαίσιο του άρθρου 3.3.1(B). Στο issue του 2023, η Apple ζητούσε να αφαιρεθεί.
Η checklist 2.3.1 πριν την υποβολή
- Κάθε νέα λειτουργία της έκδοσης περιγράφεται στις σημειώσεις, με τη διαδρομή της.
- Κάθε διακόπτης εξ αποστάσεως είναι ενεργός για το review και αναφέρεται στις σημειώσεις.
- Κανένας κώδικας δεν αλλάζει συμπεριφορά για τον reviewer.
- Οι ειδικές προσβάσεις περιγράφονται: demo mode, test λογαριασμός, κίνηση.
- Η σελίδα και το paywall δεν υπόσχονται τίποτα που δεν κάνει το build.
- Καμία ενημέρωση εξ αποστάσεως δεν δημοσιεύεται κατά το review.
Έλεγξε τον φάκελό σου πριν την αποστολή
Το Review Kit συγκεντρώνει τη checklist μου πριν την αποστολή, πρότυπα σημειώσεων για τον tester και skills του Claude Code που ελέγχουν το project σου. Αν θέλεις ανθρώπινο μάτι, κλείσε μια ώρα μαζί μου: ξαναδιαβάζουμε μαζί τις σημειώσεις για το review και τη ρύθμιση των ενημερώσεων, μέσω βιντεοκλήσης. Όλη η διαδικασία βρίσκεται στη σελίδα εφαρμογή που απέρριψε η Apple.
Ετοιμάζεις την πρώτη σου κυκλοφορία; Κάνε τη δωρεάν διάγνωση: βάζει τα βήματα σε σειρά και σου στέλνει τη checklist μου κατά των απορρίψεων της Apple.
Συχνές ερωτήσεις
Τι σημαίνει «Guideline 2.3.1 - Performance - Hidden Features»;
Η Apple κρίνει ότι η εφαρμογή σου περιέχει μια λειτουργία κρυφή, αδρανή ή που λείπει από τις σημειώσεις για το review. Το μήνυμα μπορεί επίσης να αφορά κώδικα ικανό να αλλάξει την εφαρμογή μετά το review, μερικές φορές μαζί με απόρριψη 2.5.2.
Επιτρέπει η Apple το EAS Update;
Η άδεια χρήσης επιτρέπει interpreted κώδικα που κατεβαίνει, εφόσον δεν αλλάζει τον βασικό σκοπό της εφαρμογής. Διόρθωση, ναι. Προσθήκη λειτουργίας που δεν ελέγχθηκε, όχι.
Μπορώ να χρησιμοποιήσω feature flags σε μια iOS εφαρμογή;
Ναι, αν ό,τι μπορούν να εμφανίσουν έχει ελεγχθεί. Ενεργοποίησε κάθε διακόπτη για το review και περιέγραψέ τον στις σημειώσεις.
Πρέπει να δηλώσω ένα demo mode στον reviewer;
Ναι. Ένα ενσωματωμένο demo mode χρειάζεται προηγούμενη έγκριση της Apple και πρέπει να δείχνει όλες τις λειτουργίες.
Μπορώ να δημοσιεύσω ενημέρωση εξ αποστάσεως κατά το review;
Τίποτα δεν το απαγορεύει ρητά. Όμως ο reviewer δεν θα τη δει στο πρώτο άνοιγμα, και δημιουργεί απόκλιση από την έκδοση που ελέγχθηκε. Εγώ πάντα την αποφεύγω.

