Cykl życia zgody: co trafia do użytkownika po weryfikacji wieku
4 listopada 2025 roku Apple dodało do App Store Server Notifications typ powiadomienia uruchamiany decyzją rodzica, a nie płatnością: RESCIND_CONSENT, oznaczający, że „rodzic lub opiekun wycofał zgodę na korzystanie z aplikacji przez dziecko”.12
Spośród 23 typów obsługiwanych przez tę usługę tylko ten jeden przenosi w ładunku obiekt appData zamiast informacji o transakcji, a appData zawiera podpisaną transakcję aplikacji, która istnieje „nawet jeśli klient nie dokonuje żadnych zakupów w aplikacji”.319 Aplikacja, która nigdy niczego nie sprzedała, zyskuje więc pierwszy powód, by uruchomić endpoint odbierający powiadomienia.
Kwestię odczytu przedziału wiekowego danej osoby — uprawnienie, bramki i znaczenie zwracanych granic — omawia deklaracja mediów społecznościowych. Przyjmijmy to za ustalone i zacznijmy o krok dalej. Weryfikacja wieku odpowiada na pytanie, ile ktoś ma lat. Trzy API opisane tutaj odpowiadają na pytania: kto wyraża zgodę, czy zgoda już udzielona przetrwa zmianę w aplikacji i co się dzieje, gdy opiekun ją cofnie.21012
W skrócie
- Trzy API leżą za weryfikacją wieku, a Apple dostarcza je jako komplet: Significant Change API w ramach PermissionKit,
AppStore.ageRatingCodew StoreKit oraz powiadomienie serweroweRESCIND_CONSENT.45 - Apple wiąże swoją listę czterech narzędzi nie tylko z Teksasem, lecz także z Utah i Luizjaną; wszystkie podane dla tych trzech stanów daty już minęły, a Apple zakreśla obowiązek słowami „w określonych regionach, tam gdzie wymaga tego prawo”.5815
- Bramką decydującą o tym, czy cokolwiek z tego się uruchomi, jest
AgeRangeService.requiredRegulatoryFeatures— nowość w 26.4 — a pełne napisanie kontrolowanego przez nią przepływu kosztuje iOS 26.5.736 - Apple odmawia zdefiniowania istotnej zmiany i mówi to czterokrotnie, odsyłając do prawnika. Jedyny konkretny przykład, jaki publikuje, przypisuje ustawie: Apple pisze, że prawo Teksasu uznaje zmianę klasyfikacji wiekowej aplikacji za istotną zmianę.48
- Klasyfikacja może się zmienić bez żadnej kompilacji z Państwa strony. 18 czerwca 2026 roku Apple wycofało australijski poziom 15+ i nadało Wietnamowi nowy, czterostopniowy schemat — w obu przypadkach bez proszenia kogokolwiek o przesłanie czegokolwiek.937
- Cofnięcie zgody to gałąź, której nikt nie buduje, a dokumentacja Apple buduje ją niewiele lepiej:
RESCIND_CONSENTnie pojawia się w żadnej z tabel cyklu życia na stronie, którą czyta zespół backendowy.2
Udzielenie, ponowne udzielenie, cofnięcie. Pierwsze dwie ścieżki Apple dokumentuje projektem przykładowym i macierzą testów w sandboksie. Trzecia dostaje jedną wartość enum i cztery pola — co, jak się okazało, mniej więcej odpowiada uwadze, jaką poświęca jej również mój własny kod.
Kalendarz, który Apple publikowało czterokrotnie
Każda z poniższych dat pochodzi z własnych aktualności deweloperskich Apple, a kolejność zdarzeń znaczy więcej niż którykolwiek pojedynczy wpis.
8 października 2025 roku Apple ogłosiło teksańską ustawę SB2420 z datą „od 1 stycznia 2026 roku”, opisując przy tym własną reakcję, a nie treść przepisu: nowe konta Apple dla użytkowników poniżej 18 lat miały dołączać do grupy Chmury Rodzinnej, a „rodzice lub opiekunowie będą musieli wyrazić zgodę na wszystkie pobrania z App Store, zakupy aplikacji i transakcje dokonywane przez małoletniego w systemie zakupów w aplikacji Apple”.13 4 listopada Apple nazwało narzędzia i udostępniło je w betach 26.2.4 23 grudnia plan się zatrzymał: „Niedawne postanowienie zabezpieczające wydane przez sąd okręgowy zawiesiło egzekwowanie teksańskiej ustawy stanowej SB2420 … Apple wstrzyma wcześniej zapowiedziane plany wdrożeniowe”.14 3 czerwca 2026 roku ruszyło ponownie: „W związku z niedawnym orzeczeniem sądu uchylającym zabezpieczenie dotyczące teksańskiej ustawy SB 2420 … Zmiany te wejdą w życie 4 czerwca 2026 roku”.5
Osiem miesięcy szarpaniny wokół jednej ustawy — a API nie drgnęły ani razu. Trafiły do 26.2, przez cały czas obowiązywania zabezpieczenia pozostawały dostępne do testów w sandboksie i czekały gotowe, gdy je uchylono.14 Kto odczytał grudniową pauzę jako przyzwolenie na odłożenie prac, stracił pięć miesięcy zapasu.
Reszta mapy pojawiła się 24 lutego 2026 roku i sięga daleko poza Teksas.
| Jurysdykcja | Data podana przez Apple | Co, według Apple, obowiązuje |
|---|---|---|
| Australia, Brazylia, Singapur | 24 lutego 2026 | Apple blokuje pobieranie aplikacji z klasyfikacją 18+, „chyba że potwierdzono rozsądnymi metodami, że użytkownicy są osobami dorosłymi”15 |
| Utah | 6 maja 2026 | Kategorie wiekowe udostępniane na żądanie dla nowych kont Apple15 |
| Teksas | 4 czerwca 2026 | Nowe konta Apple „podlegają od teraz tej ustawie”: zgoda na pobrania, zakupy w aplikacji i istotne zmiany w imieniu małoletnich poniżej 18 lat, z możliwością jej cofnięcia przez opiekuna5 |
| Luizjana | 1 lipca 2026 | Kategorie wiekowe udostępniane na żądanie dla nowych kont Apple15 |
Teksas odstaje progiem, nie narzędziami. Apple opisuje Teksas jako zgodę „w imieniu małoletnich poniżej 18. roku życia” i publikuje kategorie „poniżej 13, 13-15, 16-17 lub powyżej 18”, czyli dokładnie to, co produkuje API: „Można określić maksymalnie trzy bramki wiekowe, które tworzą maksymalnie cztery możliwe przedziały wiekowe”.4529 Dla Utah i Luizjany Apple wymienia udostępnianie kategorii wiekowych na żądanie, po czym stwierdza, że wszystkie cztery narzędzia „zostały rozszerzone, aby pomóc deweloperom spełnić obowiązki zgodności dla Luizjany i Utah”, wymieniając wśród nich Significant Change API.15 Narzędzia nie są zatem wyłącznie teksańskie. Czy obowiązki są — tego Apple nie chce powiedzieć: zakreśla je jako dotyczące „określonych regionów, tam gdzie wymaga tego prawo”, a pytanie odsyła do prawnika.8
Powyższej tabeli nie należy czytać ani jako wykładni prawa, ani jako oceny zgodności. Rejestruje ona to, co Apple ogłosiło, oraz datę, którą do tego dołączyło. Jestem inżynierem, nie prawnikiem, i wszystko poniżej zatrzymuje się na granicy API.
Sekcja pytań i odpowiedzi, która odsyła kwestie zgodności do prawników, niesie lepsze wieści dla planowania wydań: zapytane, czy cokolwiek z tego zmienia proces recenzji, Apple odpowiada „Nie, proces App Review pozostaje bez zmian”.8 Obowiązki spadają na czas działania aplikacji w wymienionych jurysdykcjach, a nie na moment zgłoszenia.
Podział jest więc czysty. To, czy Państwa aplikacja jest zobowiązana do uzyskania zgody w Teksasie, Utah czy Luizjanie, to pytanie do prawnika z uprawnieniami w danym stanie. Wybór SDK, w oparciu o które się kompiluje, już nie: Apple stwierdza, że „aplikację należy zbudować w oparciu o SDK iOS 26.2 i iPadOS 26.2 lub nowsze, przy użyciu Xcode 26.2 (17C52) lub nowszego”, by w ogóle dosięgnąć tych frameworków, oraz że istniejące konta na iOS 18 lub wcześniejszym „nie zostaną objęte zmianami”.8
Apple nie powie, co znaczy „istotna”
Definicyjna dziura leży w samym centrum tej funkcji, a cztery miejsca w materiałach Apple oddają ją z powrotem, zamiast ją wypełnić: strona symbolu („To Państwo określają, co stanowi istotną aktualizację, w oparciu o obowiązujące przepisy”),12 oba wpisy w aktualnościach („to na deweloperze spoczywa odpowiedzialność za ustalenie, kiedy w jego aplikacji zachodzi istotna zmiana”),45 oraz sekcja Q&A, zapytana wprost, czy zmiana regulaminu lub polityki prywatności się liczy („To zależy. To Państwo określają, co stanowi istotną aktualizację aplikacji, w oparciu o obowiązujące prawo”).8
Apple podaje dokładnie jeden rozpisany przykład, a i ten pochodzi z ustawy, nie od Apple: „Prawo stanu Teksas uznaje zmianę klasyfikacji wiekowej aplikacji za istotną zmianę, a deweloperzy powinni utrzymywać swoje wybory klasyfikacji w App Store Connect w stanie aktualnym”.4
To, co Apple rzeczywiście definiuje, to tekst, który Państwo napiszą. SignificantAppUpdateTopic zestawia obok siebie przykład dobry i zły, co jak na stronę symbolu jest niecodzienną szczerością:
// 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."
)
Instrukcja Apple wokół tego listingu jest dosadna: „Należy używać zwięzłego, zrozumiałego języka, który jasno wyjaśnia, co zmieniło się w aplikacji. Rodzice i opiekunowie widzą ten opis, decydując o udzieleniu zgody”.12 Szablonowe „poprawki i usprawnienia” trafiają teraz przed oczy rodzica decydującego, czy jego dziecko zachowa dostęp — co czyni z tego pola najbardziej brzemienny w skutki wpis w historii zmian, jaki większość aplikacji kiedykolwiek wyda.
Obowiązek dołączony do tej odpowiedzi nie jest już wcale nieostry, choć Apple asekuruje się przy jego wyzwalaczu. Apple czyni Państwa „odpowiedzialnymi za uniemożliwienie dostępu do aplikacji lub jej funkcji, gdy jest to wymagane”, po czym stwierdza wprost: „Dopóki rodzic nie wyrazi zgody, dziecku należy uniemożliwić dostęp do istotnej aktualizacji, co może obejmować wszystkie dane aplikacji i konta lub konkretne funkcje”.8 Słowa „wszystkie dane aplikacji i konta” niosą tu bardzo dużo ciężaru. Apple zostawia promień rażenia Państwu, a jako górną granicę wskazuje całe konto.
Maszyna stanów i miejsce, w którym przecieka własny przykład Apple
Apple opublikowało projekt przykładowy „Implementing age assurance and permissions”, będący jedynym kompletnym opisem tego przepływu.6 Przeczytanie go jako maszyny stanów ujawnia cztery gałęzie i jeden listing wart przepisania.
Pierwsza gałąź jest tania. AgeRangeService.requiredRegulatoryFeatures zwraca Set<AgeRangeService.RegulatoryFeature> z trzema elementami: declaredAgeRangeRequired, significantAppChangeRequiresParentalConsent i significantAppChangeRequiresAdultNotification.7 Przykład Apple sprawdza to w pierwszej kolejności, a „gdy nie występuje żadna z tych funkcji, aplikacja pomija cały przepływ”.6 Apple przedstawia tę właściwość jako odzwierciedlenie „regionu i ustawień konta” danej osoby, co odczytuję jako obietnicę, że użytkownicy spoza wymienionych jurysdykcji wracają z pustym zbiorem — choć niczego takiego Apple nie gwarantuje.7
Pozostałe gałęzie rozchodzą się według wieku i sposobu jego ustalenia:
| Osoba | Wymagana funkcja regulacyjna | Co robi przykład Apple |
|---|---|---|
| Małoletni | significantAppChangeRequiresParentalConsent |
Wysyła PermissionQuestion do opiekuna6 |
| Dorosły, metoda potwierdzona | significantAppChangeRequiresAdultNotification |
Prezentuje systemowy arkusz potwierdzenia6 |
| Dorosły, brak potwierdzonej metody | dowolna | Blokuje dostęp „do czasu zweryfikowania konta w Ustawieniach”6 |
| Odmowa udostępnienia | dowolna | Rozpoznaje przypadek, po czym nie pokazuje dla niego żadnej gałęzi6 |
Przy trzecim wierszu warto się zatrzymać. Osoba dorosła, która nigdy nie podpięła metody płatności do swojego konta Apple, zostaje zablokowana przez referencyjną implementację Apple — w przepływie zbudowanym po to, by chronić dzieci. Apple nie oferuje ani alternatywnej gałęzi, ani obejścia.
Zestawione z opublikowanymi deklaracjami, kierowanie ścieżek wygląda tak. Podział na dorosłych i małoletnich wynika z przykładu Apple oraz macierzy testów w sandboksie, gdzie konto 18+ zwraca lowerBound równy 18 bez górnej granicy oraz ageRangeDeclaration o wartości confirmed lub selfDeclared. Uwaga na próg dostępności: odczyt .confirmed kosztuje iOS 26.5, czyli wydanie późniejsze niż wszystko pozostałe w tym przepływie.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))
}
}
}
Wysłanie pytania to łatwiejsza połowa. Odebranie odpowiedzi to miejsce, w którym przykład Apple przecieka, a listing jest na tyle krótki, że da się go przeczytać uważnie:6
for await response in AskCenter.shared.responses(for: SignificantAppUpdateTopic.self) {
guard response.choice.answer == .approval else {
return
}
versionManager.handleAllChanges()
}
return wewnątrz for await porzuca sekwencję. Jedna odmowa i aplikacja przestaje obserwować dany temat aż do kolejnego uruchomienia, więc opiekun, który stuknie „Odmów”, a minutę później zmieni zdanie, wysyła zgodę do strumienia, którego nikt nie czyta. Zamiast tego należy napisać continue i odnotować odmowę. Odczytanie tego listingu jako defektu, a nie jako zamierzonego zachowania, jest moim wnioskiem; poprawka i tak kosztuje jedno słowo kluczowe. Nasłuch trzeba projektować tak, jakby uruchomienie w tle nie istniało: obiecywała je wyłącznie wycofana sekwencja, którą zastąpiono.2021
Oczekiwanie to stan, a jego limitu czasu Państwo nie kontrolują
Środek cyklu życia to miejsce, w którym mieszka właściwa praca projektowa, kształtowana przez pięć udokumentowanych zachowań. Zacznijmy od tego, na które Apple odpowiada połowicznie: PermissionQuestion.expirationDate jest typu Optional<Date>, po którym „osoba otrzymująca pytanie nie może już odpowiedzieć”, a wartości domyślnej Apple nie publikuje.16
Dziecko może anulować, zanim opiekun w ogóle zobaczy pytanie: „W dowolnym momencie procesu wysyłania żądania dziecko ma możliwość anulowania go … W takim przypadku system nie dostarcza aplikacji wywołującej odpowiedzi na to konkretne pytanie”.17 Brak odpowiedzi, brak błędu, brak wywołania zwrotnego. Stan oczekiwania trwa w nieskończoność, o ile sami nie wprowadzą Państwo limitu czasu.
Zapytanie osoby dorosłej rzuca wyjątek. Artykuł o sandboksie dokumentuje przypadek, który strona symbolu zostawia pusty: „Wywołanie AskCenter.ask(_:) dla użytkownika dorosłego rzuca ten błąd, ponieważ taki użytkownik nie spełnia wymogów żądań zgody rodzicielskiej”.11 AskError.notAvailable to jeden z dwóch przypadków w enumie pozbawionych opisu na własnej stronie — i akurat ten wyzwala osoba dorosła.18 Lepiej rozdzielać ścieżki według wieku przed zapytaniem, niż przechwytywać rzucony wyjątek.
Stan zgody należy do iCloud, nie do kontenera aplikacji. Przykład Apple zapisuje każdą potwierdzoną zmianę do NSUbiquitousKeyValueStore i obserwuje didChangeExternallyNotification, „żeby inne urządzenia nie prezentowały tego samego przepływu ponownie”.6 Rodzic zatwierdzający coś na iPhonie nie powinien wywołać drugiego żądania na iPadzie, a PermissionKit nie robi tego za Państwa.
Najlepszy szczegół w przykładzie rozwiązuje problem, który większość zespołów wdrożyłaby błędnie. Nowe instalacje nie powinny wyrażać zgody na zmianę, której nigdy nie doświadczyły, więc Apple odczytuje AppTransaction.originalAppVersion i „automatycznie oznacza jako obsłużone wszystkie zmiany wprowadzone w tej wersji lub wcześniej”.619 Śledzenie zgód jest zatem prowadzone osobno dla każdej zmiany i każdej osoby: identyfikatory zmian wraz z wersjami wprowadzenia, a nie wartość logiczna.
Klasyfikacja może się zmienić bez kompilacji
AppStore.ageRatingCode to static var zwracająca asynchronicznie Int?, dostępna od 26.2 na iOS, iPadOS, macOS, tvOS, visionOS i watchOS.10 Deklarowane przez Apple zastosowanie to porównanie, nie interpretacja: „Tej właściwości należy używać do pobrania klasyfikacji wiekowej aplikacji i porównania jej z ostatnią znaną klasyfikacją, aby sprawdzić, czy uległa zmianie”.10 Porównanie to wszystko, co Państwo dostają, ponieważ Apple nigdzie w dokumentacji nie publikuje odwzorowania liczby całkowitej na poziom klasyfikacji. Nie da się zapytać, czy jest się 13+ — tylko czy jest się tym, czym się było poprzednio. Realny kontrakt brzmi więc: trzeba trwale zapisywać poprzednią wartość i samodzielnie obsłużyć pierwsze uruchomienie.
Dlaczego aplikacja miałaby obserwować własną klasyfikację, staje się oczywiste, gdy zauważą Państwo, że klasyfikacje zmieniają się bez wydań. 21 maja 2026 roku Apple ogłosiło, że od 18 czerwca „Klasyfikacja wiekowa 15+ nie będzie już dostępna w App Store w Australii”, a Wietnamowi nadało specyficzny dla regionu czterostopniowy schemat wyprowadzony z istniejących odpowiedzi kwestionariusza.9 Obie zmiany weszły w życie: australijska tabela w pomocy App Store Connect publikuje teraz 16+ i R 18+ bez wiersza 15+, a Wietnam ma własną tabelę.37 Żadna z nich nie wymagała zgłoszenia, kompilacji ani jakiegokolwiek działania dewelopera — a Apple pisze, że prawo Teksasu liczy zmianę klasyfikacji jako istotną zmianę.4
Dla kontrastu zmiana klasyfikacji zainicjowana przez Państwa: „Gdy deweloper aktualizuje klasyfikację wiekową swojej aplikacji, klasyfikacja jest aktualizowana na wszystkich urządzeniach użytkowników z chwilą udostępnienia wersji”.4 Państwa własne zmiany towarzyszą wydaniu, które można oprzyrządować. Zmiany Apple nie — i to jest luka, którą wypełnia ta właściwość.
Problem z testowaniem jest gorszy niż zwykła luka. Odpowiedź pracownika Apple na forach deweloperskich mówi to wprost: „Wartość 0 jest oczekiwana w trakcie rozwoju aplikacji, gdy jest ona budowana i uruchamiana z Xcode oraz w środowiskach sandboksowych (w tym TestFlight)”.22 Zero to nie nil, więc udokumentowany przez samo Apple przykład przechodzi przez guard let i zwraca 0 jako prawidłową klasyfikację — dokładnie to zobaczył na fizycznym urządzeniu deweloper, który założył ten wątek.1022
Prześledźmy konsekwencje. Zapisanie 0 jako punktu odniesienia sprawia, że pierwsze uruchomienie z App Store odczytuje prawdziwy kod, widzi zmianę i prosi rodzica o ponowną zgodę na nic. Traktowanie 0 jako wartownika, którego nigdy nie zapisuje się do punktu odniesienia, to mój wniosek z tych dwóch wypowiedzi Apple — i jedyna linijka kodu obronnego, bez której nie wypuściłbym tej funkcji.
Metadane dostępności Apple niosą jeszcze jedno ustalenie, którego nie podaje żadna strona opisowa. Wiersze platform w tych czterech możliwościach mają dziury w różnych miejscach:
| Możliwość | iOS / iPadOS | macOS | Mac Catalyst | visionOS | tvOS / watchOS |
|---|---|---|---|---|---|
AppStore.ageRatingCode10 |
26.2 | 26.2 | brak wiersza | 26.2 | 26.2 |
requiredRegulatoryFeatures7 |
26.4 | 26.4 | 26.4 | brak wiersza | brak wiersza |
SignificantAppUpdateTopic, PermissionButton1226 |
26.2 | 26.2 | 26.2 | 26.2 | brak wiersza |
showSignificantUpdateAcknowledgment27 |
26.4 | brak wiersza | 26.4 | brak wiersza | brak wiersza |
Wypadają z tego dwie asymetrie. Natywna aplikacja macOS może dowiedzieć się, że wobec danej osoby stosuje się significantAppChangeRequiresAdultNotification, i nie mieć żadnego API, którym mogłaby to spełnić: showSignificantUpdateAcknowledgment(in:updateDescription:) przyjmuje UIWindowScene i nie publikuje wiersza macOS, a SignificantUpdateAction w SwiftUI zatrzymuje się na tych samych trzech platformach.27 Apple dostarczyło natomiast wariant z NSWindow dla sąsiedniego wywołania requestAgeRange, więc to pominięcie czytam raczej jako lukę niż jako decyzję kierunkową — i jest to mój wniosek.28
Po drugie, wykrywanie sięga dalej niż reagowanie. ageRatingCode publikuje wiersze tvOS i watchOS; nic, co prosi o zgodę, tego nie robi.10 Te platformy mogą zaobserwować zmianę klasyfikacji, nie mając udokumentowanego sposobu, by zareagować — a Return wydaje na obie: jego wersja tvOS 1.0.1 ma w App Store Connect stan READY_FOR_DISTRIBUTION, a dystrybuowana aplikacja iOS osadza aplikację na zegarek.30 Oba odczyty zakładają kompletność metadanych Apple, co te same metadane podważają: ageRatingCode nie publikuje wiersza Mac Catalyst, podczas gdy każdy sąsiedni symbol go publikuje.10
Do czego służy serwer, skoro Apple i tak blokuje uruchomienie
Zachowanie przy cofnięciu zgody Apple opisuje jednym zdaniem: „Gdy rodzic lub opiekun cofnie zgodę na dostęp dziecka do aplikacji, Apple uniemożliwi jej uruchomienie. Do obsługi cofnięć zgody należy używać wartości RESCIND_CONSENT z notificationType”.8
Warto zwrócić uwagę na kolejność tych zdań. System już blokuje dostęp, więc RESCIND_CONSENT nie jest punktem zaczepienia do egzekwowania i budowanie go w tej roli to marnotrawstwo. To jedyny sygnał, jaki Państwo dostają o użytkowniku, na którego urządzeniu Państwa kod nie może już działać. O tym, jak zaksięgować to zdarzenie, Apple nie mówi nic, więc najbliższą analogią, jaką znalazłem, jest żądanie usunięcia konta: subskrypcje do rozliczenia, stan do zamrożenia i gospodarstwo domowe, które może się jeszcze pojawić ponownie.
Ładunek jest wyjątkowo ubogi. appData niesie cztery pola: appAppleId, bundleId, environment i signedAppTransactionInfo.3 Żadnej transakcji, żadnej subskrypcji, żadnych informacji o odnowieniu i żadnego Państwa identyfikatora konta. Powiązanie z osobą prowadzi przez podpisaną transakcję aplikacji, której appTransactionID App Store generuje „dla każdego konta Apple, które pobiera aplikację, oraz dla każdego członka grupy rodzinnej w aplikacjach obsługujących Chmurę Rodzinną”.19 Darmowa aplikacja ma więc trwały klucz przypisany do konta, do którego może dopasować powiadomienie — a aplikacja, która nigdy takiego klucza nie zapisała, dostaje powiadomienie, którego nie potrafi nikomu przypisać.
Transport nie niesie żadnych niespodzianek: adres URL w wersji 2 osobno dla każdego środowiska w App Store Connect, po TLS 1.2 lub nowszym, 17.0.0.0/8 na liście dozwolonych, kody od 200 do 206 jako sukces, a 40x lub 50x po to, by wykupić pięć ponowień po 1, 12, 24, 48 i 72 godzinach — wyłącznie w środowisku produkcyjnym.2324
RESCIND_CONSENT nie niesie też żadnego podtypu. Każda z 19 opublikowanych wartości podtypu jest przypisana do konkretnego typu powiadomienia, a ciąg RESCIND_CONSENT nie występuje na tej stronie ani razu.25 Bardziej wymowne jest co innego: strona notificationType w Apple mapuje 40 zdarzeń w ośmiu tabelach pod nagłówkiem „Handle use cases for In-App Purchase life-cycle events”, a RESCIND_CONSENT nie pojawia się w żadnej z nich — wartość istnieje na liście możliwych wartości i nigdzie indziej na tej stronie.2 Apple udokumentowało to powiadomienie w materiałach o weryfikacji wieku i nigdy nie wpięło go do dokumentacji, którą realnie czyta zespół backendowy.
Co moje własne projekty już robią źle
Zanim zacząłem pisać o cudzym kodzie, przeszukałem własny. Reguła doboru była mechaniczna — i to w niej siedział błąd: każdy wiersz tabeli aktywnych projektów w moim własnym CLAUDE.md, za którym stoi projekt Xcode, co daje siedem projektów i 508 plików obejmujących wszystkie pliki Swift, pliki uprawnień, listy właściwości i project.pbxproj.31 Zero trafień dla PermissionKit, AskCenter, SignificantAppUpdateTopic, FamilyControls, ManagedSettings, DeviceActivity, CKShare, sharedCloudDatabase i ageRatingCode. Pięć z siedmiu ma rekord w App Store Connect. Water i Yawara nie mają żadnego i nigdy nie zostały wydane, więc te dwa należy czytać jako kod, a nie jako aplikacje w czyichkolwiek rękach.31 Ace Citizenship wyglądało na najbardziej prawdopodobne miejsce dla małoletnich użytkowników, a jest najmniej prawdopodobnym: towarzysząca mu witryna podaje uprawnienie do N-400 jako „ukończone 18 lat”, a aplikacja nigdzie nie pyta o datę urodzenia.31
Następnie uruchomiłem identyczne wzorce na wszystkich repozytoriach w ~/Projects, czyli skan, który powinienem był przeprowadzić najpierw. Te same wzorce trafiają w 21 plików, siedem z nich to kod, a sześć z tych siedmiu leży w jednym projekcie iOS, dla którego rejestr nigdy nie doczekał się wiersza.32
Randori to dziennik treningowy jiu-jitsu i mieści dokładnie tę powierzchnię zgody, do wyłapywania której powstała lista wzorców: 20 trafień dla CKShare i sharedCloudDatabase w plikach ConnectionStore.swift, RandoriApp.swift, CKPostTransport.swift, ProfileCardView.swift, CloudShareSheet.swift i SocialContracts.swift, gdzie akceptacja przebiega przez userDidAcceptCloudKitShareWith, gdy aplikacja już działa, oraz przez connectionOptions.cloudKitShareMetadata przy zimnym starcie.32 Randori dostarcza też własny prymityw zgody na noszenie cudzego nazwiska: TagConsentStore domyślnie blokuje, więc zawodnika, którego klient nie opublikował polityki oznaczania, w ogóle nie da się oznaczyć.32
Jedyny projekt z prawdziwą powierzchnią zgody napisał więc własny rejestr i nie przyjął żadnego mechanizmu Apple. Winien jest trzy rzeczy, których nie zbudowałem. Włączenie powierzchni komunikacji między osobami, które jego kontrakty trzymają w uśpieniu, to podręcznikowy przypadek SignificantAppUpdateTopic. ageRatingCode nie ma zapisanego punktu odniesienia, a niewydana wersja 1.0 działała dotąd wyłącznie z Xcode, sandboksa albo TestFlight — czyli dokładnie tam, gdzie według Apple właściwość zwraca 0.22 Cofnięcie zgody nie ma gdzie wylądować: Randori nic nie sprzedaje i nie prowadzi własnego serwera.32
Ciekawsze jest jednak ustalenie, że zgoda opiekuna już działa w trzech z pierwotnych siedmiu projektów — pod starszą nazwą.
Ask to Buy to ten sam mechanizm o tym samym kształcie, a Apple opisuje go niemal tymi samymi słowami: „Przy Ask to Buy, gdy dziecko chce dokonać kwalifikującego się zakupu lub pobrania, system wysyła żądanie zakupu do rodzica lub opiekuna”.34 Ujawnia się jako Product.PurchaseResult.pending, a zgoda przychodzi przez Transaction.updates, a nie w miejscu wywołania, ponieważ ta sekwencja niesie „transakcje zachodzące poza aplikacją, takie jak transakcje Ask to Buy”.35 Odmowa nie dostarcza nic: „Aplikacja nie otrzymuje transakcji, ponieważ odrzucili Państwo Ask to Buy”.34
Trzy z siedmiu projektów coś sprzedają i każdy obsługuje stan oczekiwania inaczej:33
| Aplikacja | Produkt | Obsługa .pending |
|---|---|---|
| ResumeGeni | Subskrypcja miesięczna | Nazwany wynik .pending z udokumentowaną ścieżką rozliczenia |
| Reps | Subskrypcja, dwa poziomy | case .userCancelled, .pending: zwinięte w jedną gałąź |
| Ace Citizenship | Produkt niekonsumowalny | Osobny case .pending: zwracający false, identycznie jak przy anulowaniu |
Dwie z trzech raportują namyślającego się opiekuna jako użytkownika, który odmówił — a to, co widzi użytkownik, jest gorsze niż zła etykieta. Reps zamyka swój paywall wyłącznie wtedy, gdy purchase zwróci true; Ace przepuszcza poza paywall wyłącznie wtedy, gdy jego własne wywołanie zwróci sukces. Przy .pending żadne z tych wywołań nie zwraca niczego, na czym da się działać, więc oba paywalle zostają otwarte bez błędu, bez powiadomienia, bez wskaźnika: martwe stuknięcie.33 Rodzic zatwierdza kilka minut później, a transakcja ląduje w nasłuchu, którego interfejs nigdy nie przyznał, że jakiekolwiek żądanie w ogóle zaszło.
Zwinięta gałąź to dokładnie ta awaria, do której PermissionKit zaprasza przy wyższej stawce — a ja wypuściłem ją dwukrotnie, zanim Apple nadało temu wzorcowi drugie API. Oczekiwanie nie jest gałęzią egzotyczną. Oczekiwanie to właśnie to, jak przepływ zgody wygląda od wewnątrz aplikacji, a właściwym domyślnym rozwiązaniem jest stan, który się renderuje, a nie wartość, którą się zwraca.
Jedna aplikacja jest już po serwerowej stronie tej historii, co czyni brakującą gałąź całkiem konkretną. Endpoint w wersji 2 w ResumeGeni odnotował od 26 czerwca 2026 roku co najmniej 56 powiadomień, 55 z sandboksa i jedno z produkcji, i stąd wiem, że zarejestrowane są oba adresy środowiskowe, a nie tylko jeden. Jego procedura obsługi wymienia 13 typów powiadomień i rozdziela osiem; pozostałych pięć pojawia się wyłącznie w komentarzach. RESCIND_CONSENT nie należy do żadnej z tych grup — ani nie występuje nigdzie indziej w repozytorium.33
Najczęstsze pytania
Które API prosi o zgodę opiekuna, a które zwraca się do osoby dorosłej?
To różne frameworki i łatwo je pomylić. Zgoda rodzicielska przebiega przez PermissionKit: SignificantAppUpdateTopic opakowany w PermissionQuestion(significantAppUpdateTopic:), wysłany przez PermissionButton, z odpowiedzią odbieraną przez AskCenter.shared.responses(for:).12162126 Potwierdzenie przez osobę dorosłą przebiega natomiast przez Declared Age Range, jako showSignificantUpdateAcknowledgment(in:updateDescription:).27 Najpierw należy sprawdzić requiredRegulatoryFeatures, ponieważ zapytanie osoby dorosłej przez PermissionKit rzuca AskError.notAvailable.711
Jak przetestować cofnięcie zgody bez prawdziwego konta rodzinnego?
Należy włączyć tryb dewelopera, następnie otworzyć Ustawienia, Developer, sandboksowe konto Apple, zalogować się, wybrać konto, stuknąć Manage i wybrać Revoke App Consent. Po wpisaniu identyfikatora pakietu i stuknięciu Revoke Consent system wyświetla „Notification Triggered”.11 Przy skonfigurowanym adresie URL w wersji 2 serwer otrzymuje RESCIND_CONSENT z obiektem appData, którego pola bundleId i environment potwierdzają, że powiadomienie dotyczy właściwej aplikacji.311 W sandboksie każde powiadomienie wychodzi jednorazowo, bez ponowień, więc endpoint zwracający w trakcie testu 50x nie dostaje drugiej szansy.24
Po co aplikacja miałaby obserwować własną klasyfikację wiekową w czasie działania?
Ponieważ Apple zmienia klasyfikacje w sklepie bez jakiegokolwiek zgłoszenia z Państwa strony, a jednocześnie pisze, że prawo Teksasu liczy zmianę klasyfikacji jako istotną zmianę, po czym kieruje do Significant Change API w celu uzyskania zgody rodzicielskiej.4 18 czerwca 2026 roku jest tego rozpisanym przykładem: Australia straciła poziom 15+, a Wietnam zyskał schemat czterostopniowy, oba zastosowane do istniejących aplikacji na podstawie istniejących odpowiedzi kwestionariusza.937 Apple nie publikuje odwzorowania liczby całkowitej z tej właściwości na poziom klasyfikacji, więc porównanie z zapisaną wcześniej wartością to jedyna operacja, jaką ta właściwość wspiera.10
Czy RESCIND_CONSENT blokuje użytkownika za mnie?
Robi to już system — i właśnie ten punkt umyka większości wdrożeń. Apple stwierdza, że gdy opiekun cofa zgodę, „Apple uniemożliwi uruchomienie aplikacji”, a następnie kieruje do powiadomienia po to, by obsłużyć zdarzenie, a nie by je egzekwować.8 Do zrobienia zostaje więc praca po stronie serwera: zamrozić stan konta, rozliczyć ewentualną subskrypcję i przestać wysyłać powiadomienia push na urządzenie, które nie może otworzyć Państwa aplikacji. Należy potraktować to jak usunięcie konta, a nie jak nieudaną kontrolę uprawnień.
Najważniejsze wnioski
Dla deweloperów iOS:
- Warto już dziś przejrzeć istniejącą instrukcję switch na Product.PurchaseResult. Ask to Buy to zgoda opiekuna, która już działa, .pending to sposób, w jaki się ujawnia, a zwinięcie tego przypadku w .userCancelled to dokładnie ten defekt, do którego PermissionKit zaprosi przy wyższej stawce.3435
- Jeśli Państwa przepływ odróżnia dorosłego potwierdzonego od zadeklarowanego samodzielnie, należy planować iOS 26.5, a nie 26.4.36
- Zanim skopiują Państwo nasłuch z przykładu Apple, trzeba poprawić w nim return — i nigdy nie pozwolić, by 0 trafiło do zapisanego punktu odniesienia ageRatingCode.622
Dla zespołów wydających na wiele platform Apple:
- Przed zaplanowaniem prac warto sprawdzić wiersze dostępności. Natywna aplikacja macOS może wykryć, że wymagane jest potwierdzenie przez osobę dorosłą, i nie mieć żadnego API, by je zaprezentować; tvOS i watchOS mogą odczytać ageRatingCode, nie mając za nim żadnego API zgody.71027
- Potwierdzenia należy przechowywać w NSUbiquitousKeyValueStore z kluczem osobnym dla każdej zmiany, a AppTransaction.originalAppVersion wykorzystać do zwolnienia nowych instalacji ze zmian, które je poprzedzają.619
Dla odpowiedzialnych za backend i wydania:
- Warto zapisywać appTransactionID dla każdego konta, zanim okaże się potrzebny, a następnie postawić endpoint w wersji 2 nawet dla darmowej aplikacji. Endpoint zbudowany po fakcie otrzymuje zdarzenia cofnięcia zgody, których nie potrafi nikomu przypisać.319
- Opis istotnej zmiany warto przepuścić przez osobę odpowiedzialną za teksty produktowe. To jedyny ciąg znaków, jaki czyta opiekun, decydując, czy Państwa aplikacja zachowa użytkownika.12
Trzy z czterech punktów egzekwowania w tym cyklu uruchamiają się na czymś, co Państwo kontrolują: klucz ekranu startowego na SDK, makro @State na łańcuchu narzędzi i deklaracja mediów społecznościowych przy zgłoszeniu. Zgoda opiekuna uruchamia się na wokandzie sądowej i na tabeli klasyfikacji w sklepie. Centrum całej serii to seria o ekosystemie Apple.
Bibliografia
-
Apple, App Store Server Notifications changelog. Pod nagłówkiem z 4 listopada 2025 roku, w sekcji nowych funkcji: „Updated the
responseBodyV2DecodedPayloadto include the new payload object,appData” oraz „Added the notification typeRESCIND_CONSENTtonotificationType”. Kolejne dwa wpisy w dzienniku zmian to 10 grudnia 2025 i 27 kwietnia 2026, żaden nie dotyczy zgody. Odczytane z JSON dokumentacji Apple 26 lipca 2026 roku, ponieważ strona HTML renderuje się przez JavaScript. ↩ -
Apple, notificationType, App Store Server Notifications. Źródło definicji
RESCIND_CONSENT(„A notification type that indicates the parent or guardian has withdrawn consent for a child’s app usage”) oraz użytej tu liczby: strona publikuje 23 możliwe wartości (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). CiągRESCIND_CONSENTwystępuje w ładunku strony dokładnie raz, wewnątrz listy możliwych wartości; osiem tabel pod nagłówkiem „Handle use cases for In-App Purchase life-cycle events” zawiera łącznie 40 wierszy zdarzeń (4, 6, 7, 7, 8, 6, 6 i 4 wiersze wliczając każdy nagłówek) i żadna z nich go nie wymienia. Warto zauważyć, żeREVOKEoznacza utratę uprawnienia z Chmury Rodzinnej, a nie wycofanie zgody: „an In-App Purchase the customer was entitled to through Family Sharing is no longer available through sharing”. Odczytane z JSON dokumentacji Apple 26 lipca 2026 roku. ↩↩↩↩ -
Apple, appData, App Store Server Notifications, wprowadzone w wersji 2.19. Źródło zdania „The
appDataobject is part of theresponseBodyV2DecodedPayload. This object is present in the payload when thenotificationTypeisRESCIND_CONSENT” oraz czterech właściwości:appAppleId(„available for apps that users download from the App Store. It isn’t present in the sandbox environment”),bundleId,environmentisignedAppTransactionInfo(typuJWSAppTransaction). Strona responseBodyV2DecodedPayload stwierdza tę wyłączność niezależnie, opisującappDatajako pole, które „appears when thenotificationTypeisRESCIND_CONSENT”, i dodając, że „Thedata,appData,summary, andexternalPurchaseTokenfields are mutually exclusive. The payload contains only one of these fields”. Te dwie strony są podstawą dla nazwaniaRESCIND_CONSENTjedynym typem powiadomienia, którego ładunek niesieappData; ciągappDatanie występuje nigdzie na stronienotificationType. ↩↩↩↩ -
Apple, Next steps for apps distributed in Texas, Apple Developer News, 4 listopada 2025. Źródło teksańskich kategorii wiekowych („under 13, 13-15, 16-17, or over 18”), nazwy frameworka używanej przez Apple („the Significant Change API under the PermissionKit framework”), zdania o odpowiedzialności dewelopera („It’s the developer’s responsibility to determine when there’s a significant change to their app”), przykładu z klasyfikacją wiekową („Texas state law considers a change in the age rating of an app to be a significant change, and developers should keep their age rating selections current in App Store Connect. When a developer updates their app’s age rating, the rating is updated on all user devices once the version is live”), ujęcia po stronie StoreKit („Developers can use a new property type in StoreKit to automatically check when their app’s age rating has changed on a user’s device and then use the Significant Change API to request parental consent”), zachowania przy cofnięciu zgody („A parent or guardian in Texas can withdraw consent for any app, which will block launching of the app on the child or teen’s device”) oraz czteropunktowej listy wdrożeniowej „Next steps”. Także źródło datowania dostępności bet na iOS 26.2 i iPadOS 26.2. Pobrane 26 lipca 2026 roku. ↩↩↩↩↩↩↩↩↩
-
Apple, Update for Apps Distributed in Texas, Apple Developer News, 3 czerwca 2026. Źródło informacji o uchyleniu zabezpieczenia („Due to a recent court ruling lifting an injunction on Texas law SB 2420, new Apple Accounts in Texas are now subject to the law”), zakresu („age assurance and parent or guardian consent on behalf of minors under the age of 18 for downloads, Apple In-App Purchases, and significant changes associated with an app. Parents or guardians will also be able to revoke their consent for any app they previously approved for their child”), daty wejścia w życie („These changes will go into effect starting June 4, 2026”), powtórzonego przypomnienia o odpowiedzialności dewelopera oraz tej samej czteropunktowej listy wdrożeniowej. Pobrane 26 lipca 2026 roku. ↩↩↩↩↩↩
-
Apple, Implementing age assurance and permissions, kod przykładowy Declared Age Range. Dostępność: iOS 26.5, iPadOS 26.5, Mac Catalyst 26.5, Xcode 27.0 beta; artykuł zaleca zalogowanie się do iCloud „na urządzeniu z iOS 26.4 lub nowszym” przed uruchomieniem. Źródło zachowania przy pustym zbiorze („When neither feature is present, the app skips the flow entirely”), bramki wiekowej 18, czterech rozpoznawanych kategorii (
.minor,.verifiedAdultopisane jako „an adult with a confirmed payment method”,.unverifiedAdultjako „an adult without account verification”,.declinedSharing), wyniku dla niezweryfikowanego dorosłego („the app sets the phase to.blockedand prevents access until the person verifies their account in Settings”), ścieżki małoletniego budującejSignificantAppUpdateTopiciPermissionQuestion, wysyłki przezPermissionButton, listingu nasłuchuAskCenter.shared.responses(for:)przytoczonego tu dosłownie, zachowania przy odmowie („When the parent denies the request, the app prevents the minor from using it”), śledzenia potwierdzeń wNSUbiquitousKeyValueStorewraz zdidChangeExternallyNotification(„so other devices don’t present the same flow again”) oraz zwolnienia opartego naoriginalAppVersion(„People who install the app when a significant change is already present don’t need to acknowledge it”). Projekt wymaga też uprawnienia Declared Age Range oraz usługi magazynu klucz-wartość iCloud. Odczytane z JSON dokumentacji Apple 26 lipca 2026 roku. ↩↩↩↩↩↩↩↩↩↩↩↩↩ -
Apple, AgeRangeService.requiredRegulatoryFeatures i AgeRangeService.RegulatoryFeature, Declared Age Range. Oba dostępne od iOS 26.4, iPadOS 26.4, Mac Catalyst 26.4 i macOS 26.4, bez wiersza visionOS, tvOS i watchOS. Deklaracja:
var requiredRegulatoryFeatures: Set<AgeRangeService.RegulatoryFeature> { get async throws }, rzucającanotAvailable„if the regulatory feature’s service is unavailable”. Enum publikuje dokładnie trzy przypadki:declaredAgeRangeRequired(„Indicates the person is required to share their age range with your app”),significantAppChangeRequiresAdultNotification(„Indicates that adult users must acknowledge your app’s significant change”) orazsignificantAppChangeRequiresParentalConsent(„Indicates a parent or guardian is required to acknowledge and consent to a significant app change”). ↩↩↩↩↩↩ -
Apple, Age assurance frameworks Q&A, Apple Developer Support. Źródło progu SDK („you must build your app against the iOS 26.2 and iPadOS 26.2 SDKs, or later, with Xcode 26.2 (17C52) or later”), wyłączenia dla istniejących kont („Existing Apple Accounts running iOS 18 and iPadOS 18, or earlier … won’t be affected” — zdania, którego pominięty fragment precyzuje „including adult and child accounts for kids and teens”), odpowiedzi o odpowiedzialności („Yes, developers are responsible for their own age restrictions” oraz „For questions about your compliance obligations, consult your legal counsel”), zakreślenia regionalnego cytowanego w tym artykule dwukrotnie („In certain regions, where legally required, Apple uses age assurance methods to confirm an Apple Account holder’s age and shares age categories with you through the Declared Age Range API. In those regions, you must check the age of the people using your app”, powtórzonego dalej jako „In regions where legally required, you need to check the age of the people using your app with the Declared Age Range API”), obowiązku ograniczenia dostępu („For significant app updates, you’re responsible for preventing access to your app or features when required, and for handling the response from the parent or guardian. Until the parent provides consent, the child must be prevented from accessing the significant update, which may include all app and account data or specific features”), zachowania przy cofnięciu zgody („When a parent or guardian revokes consent for their child to access an app, Apple will prevent the app from launching. To handle consent revocations, use the
RESCIND_CONSENTvalue fromnotificationType”), odpowiedzi o App Review („No, there are no changes to the App Review process”) oraz odpowiedzi o regulaminie i prywatności („It depends. You determine what constitutes a significant app update based on applicable laws”). Odczytane 26 lipca 2026 roku. ↩↩↩↩↩↩↩↩↩ -
Apple, Upcoming changes to age ratings in Australia and Vietnam, Apple Developer News, 21 maja 2026. Źródło zdania „Starting June 18, 2026, age ratings on the App Store will be updated in Australia and Vietnam”, zmiany australijskiej („The 15+ age rating will no longer be available on the App Store in Australia. Apps currently rated 15+ with the following content descriptors will be updated to 16+”) i jej trzech deskryptorów (nieograniczony dostęp do sieci; częste informacje medyczne lub o leczeniu; skrzynki z łupami), a także zmiany wietnamskiej („To align with Article 38 of Vietnam Decree 147, apps available on the App Store in Vietnam will require a region-specific age rating. Based on your age rating questionnaire responses in App Store Connect, your app will receive one of four ratings (00+(all ages), 12+, 16+, or 18+)”). Żadna z tych zmian nie wymaga od dewelopera przesłania kompilacji. Pobrane 26 lipca 2026 roku. ↩↩↩
-
Apple, AppStore.ageRatingCode, StoreKit. Deklaracja
static var ageRatingCode: Int? { get async }, dostępna od iOS 26.2, iPadOS 26.2, macOS 26.2, tvOS 26.2, visionOS 26.2 i watchOS 26.2, bez wiersza Mac Catalyst. Wartość zwracana: „An integer representing the current age rating code, ornilif the age rating is unavailable”. Źródło ujęcia porównawczego („Use this property to fetch the age rating for your app and compare it with the last known age rating to check if it has changed”) oraz powiązania z PermissionKit („If your app’s age rating has changed, consider informing parents or guardians by using the Significant Change API”) — zdania, w którym „Significant Change API” jest tekstem odnośnika, a stronaSignificantAppUpdateTopicjego celem. Własny przykład Apple na tej stronie toguard letwokół tej właściwości, wypisujący komunikat, gdy wartość jest niedostępna. Przeszukałem dokumentację Apple w poszukiwaniu odwzorowania tych liczb na poziomy klasyfikacji (4+, 9+, 13+, 16+, 18+) 26 lipca 2026 roku i nie znalazłem go ani na tej stronie, ani na stronie typuAppStore, ani w materiałach pomocy App Store Connect dotyczących klasyfikacji wiekowych. ↩↩↩↩↩↩↩↩↩ -
Apple, Testing age assurance in sandbox, StoreKit. Źródło ścieżki na urządzeniu (Ustawienia, Developer, Sandbox Apple Account, Manage, następnie „Age Assurance or Revoke App Consent”), sześciowierszowej macierzy testów, której wiersze 18+ zwracają dolną granicę 18 bez górnej oraz deklarację wieku
selfDeclaredlubconfirmed, zachowania przy pytaniu osoby dorosłej („For 18+ test cases, PermissionKit throwsAskError.notAvailablerather than returning aPermissionChoice. CallingAskCenter.ask(_:)for an adult user throws this error because they don’t meet the requirements for parental permission requests”), kroków cofnięcia zgody kończących się potwierdzeniem „Notification Triggered” wraz z „A notification will be sent to the developer server soon”, a także uwagi o ładunku („your server receives aRESCIND_CONSENTnotificationType. The notification payload includes anappDataobject with app metadata, including thebundleIdandenvironmentfields”). Trzy wiersze dla małoletnich w macierzy to: poniżej 13 zatwierdzone, 13-15 zatwierdzone i 16-17 odrzucone, wszystkie z deklaracją wiekuguardianDeclared. ↩↩↩↩↩ -
Apple, SignificantAppUpdateTopic, PermissionKit. Dostępne od iOS 26.2, iPadOS 26.2, Mac Catalyst 26.2, macOS 26.2 i visionOS 26.2; zadeklarowane jako
struct SignificantAppUpdateTopic, zgodne zQuestionTopic, zinit(description: String). Źródło odesłania definicyjnego („You determine what constitutes a significant update based on applicable regulations”), wskazówek do opisu („Use concise, understandable language that clearly explains what changed in your app. Parents and guardians see this description when deciding whether to grant permission”) oraz obu komentarzy w listingu Specific/Vague odtworzonym w tym artykule dosłownie. ↩↩↩↩↩↩ -
Apple, New requirements for apps available in Texas, Apple Developer News, 8 października 2025. Źródło pierwotnego ogłoszenia („Beginning January 1, 2026, a new state law in Texas … introduces age assurance requirements for app marketplaces and developers”, gdzie pominięty fragment wymienia SB2420), wymogu Chmury Rodzinnej („All new Apple Accounts for users under the age of 18 will be required to join a Family Sharing group, and parents or guardians will need to provide consent for all App Store downloads, app purchases, and transactions using Apple’s In-App Purchase system by the minor”) oraz wcześniejszej zapowiedzi dotyczącej Utah i Luizjany („Similar requirements will come into effect later next year”). Pobrane 26 lipca 2026 roku. ↩
-
Apple, Update on age requirements for apps distributed in Texas, Apple Developer News, 23 grudnia 2025. Źródło informacji o zabezpieczeniu („A recent injunction issued by a district court suspended enforcement of Texas state law SB2420 … In light of this ruling, Apple will pause previously announced implementation plans and monitor the ongoing legal process”), dalszej dostępności wszystkich czterech narzędzi w sandboksie oraz ich rozszerzenia na Utah i Luizjanę. Pobrane 26 lipca 2026 roku. ↩↩
-
Apple, Age requirements for apps distributed in Brazil, Australia, Singapore, Utah, and Louisiana, Apple Developer News, 24 lutego 2026. Źródło ograniczenia pobrań 18+ („Starting February 24, 2026, Apple will block users in Australia, Brazil, and Singapore from downloading apps rated 18+ unless they have been confirmed to be adults through reasonable methods. The App Store will perform this confirmation automatically. However, developers may have separate obligations to independently confirm that their users are adults”), dat dla Utah i Luizjany („For users with new Apple Accounts in Utah as of May 6, 2026, and in Louisiana as of July 1, 2026, age categories will be shared with the developer’s app when requested through the Declared Age Range API”), zdania o rozszerzeniu cytowanego w tym artykule i czterech następujących po nim odnośników („The tools we previously announced have been expanded to help developers meet compliance obligations for Louisiana and Utah, including:” Declared Age Range API, Significant Change API w PermissionKit, nowy typ właściwości klasyfikacji wiekowej w StoreKit, App Store Server Notifications), konsekwencji dotyczącej skrzynek z łupami w Brazylii oraz pierwszego publicznego nazwania Significant Update Action („Developers can use the Declared Age Range API to present significant update notifications to adults in these states through the Significant Update Action, now in beta”). Pobrane 26 lipca 2026 roku. ↩↩↩↩↩
-
Apple, PermissionQuestion i expirationDate, PermissionKit.
final class PermissionQuestion<Topic> where Topic : QuestionTopic, dostępna od iOS 26.0 z czterema inicjalizatorami:init(handle:),init(handles:),init(communicationTopic:)iinit(significantAppUpdateTopic:), przy czym ostatni wprowadzono w iOS 26.2 i opisano jako tworzący „a permission question that asks parents or guardians for permission to continue using your app after a significant update”.expirationDatejest zadeklarowana jakofinal var expirationDate: Date?z omówieniem „Once the date passes, the person that receives the question can no longer respond”. Apple nie publikuje ani wartości domyślnej tej właściwości, ani wskazówek dotyczących jej ustawiania dla tematu istotnej aktualizacji. ↩↩ -
Apple, Creating a communication experience, PermissionKit. Źródło cytowanego tu zachowania przy anulowaniu: „At any point during the send request flow, the child has the option to cancel the request, and decide not to send the question to their parent or guardian. In this scenario, the system doesn’t deliver a response to the calling app for that specific question”. Także źródło ograniczenia frameworka do iMessage, które strona główna PermissionKit podaje jako uwagę Important: „Communication experiences using the
PermissionKitframework are only available using iMessage”. Warto zauważyć, że artykuł dokumentuje wyłącznie przepływCommunicationTopic; Apple nie publikuje odpowiednika dla przepływu istotnej aktualizacji, a przykłady kodu w artykule wywołująCommunicationLimits.current.permissionResponses— symbol, który na 26 lipca 2026 roku zwraca w dokumentacji Apple błąd 404. ↩ -
Apple, AskError, PermissionKit.
enum AskError, zgodny zLocalizedError, z sześcioma przypadkami. Cztery mają opisy i pojawiają się w iOS 26.1:unknown,communicationLimitsNotEnabled(„Indicates communication limits isn’t enabled to send permission requests”),contactSyncNotSetupiinvalidQuestion. Dwa nie publikują opisu na własnych stronach:systemError(underlyingError:)w iOS 26.1 oraz notAvailable w iOS 26.2, pojawiający się wraz zSignificantAppUpdateTopic. ZnaczenienotAvailablewystępuje wyłącznie w artykule o testowaniu w sandboksie cytowanym w przypisie 11;systemErrorprzynajmniej nazywa swoją przyczynę w sygnaturze. CzycommunicationLimitsNotEnabledlubcontactSyncNotSetupmogą wystąpić także przy pytaniu o istotną aktualizację, pozostaje niedopowiedziane. ↩ -
Apple, appTransactionID i originalAppVersion w
AppTransaction, StoreKit. Źródło semantyki identyfikatora („The App Store generates a single, globally uniqueappTransactionIDfor each Apple Account that downloads your app and for each family group member for apps that support Family Sharing”), jego trwałości przy ponownym pobraniu, zwrocie, ponownym zakupie i zmianie sklepu, jego obecności w ładunkach App Store Server Notifications w wersji 2 oraz kluczowego zdania dla aplikacji darmowych: „TheappTransactionIDis available even if a customer makes no in-app purchases”.originalAppVersionto „The app version that the customer originally purchased from the App Store”, niosącaCFBundleShortVersionStringna macOS iCFBundleVersiongdzie indziej, a w środowisku sandbox zawsze1.0. Odpowiadający jej typ po stronie serwera to appTransactionId w App Store Server API, wprowadzony w wersji 1.15. ↩↩↩↩↩ -
Apple, CommunicationLimits i updates, PermissionKit.
updatesjest zadeklarowane jakofinal var updates: some AsyncSequence<PermissionResponse<CommunicationTopic>, Never> { get }i opisane jako „Registers the communication topic with the system, so your app can be launched on-demand in the background to receive permission updates”. Dokumentacja Apple grupujeupdatesoraz obie wersjeCommunicationLimits.ask(_:in:)pod nagłówkiem wycofanych API; sama klasa pozostaje aktualna dlaisKnownHandle(_:)iknownHandles(in:). Sekwencja zastępcza porzuca tę obietnicę: ani opis, ani omówienieAskCenter.responses(for:)nie wspominają o uruchamianiu w tle, i na tym polega cała luka dokumentacyjna sygnalizowana w tekście. ↩ -
Apple, AskCenter i responses(for:), PermissionKit, oba dostępne od iOS 26.2, iPadOS 26.2, Mac Catalyst 26.2, macOS 26.2 i visionOS 26.2.
AskCentertofinal classosiągana przezstatic let shared, opisana jako kierująca „your questions through the appropriate family sharing channels” i dostarczająca „responses back to your app when parents make their decisions”.responses(for:)jest zadeklarowane jakofinal func responses<Topic>(for topicType: Topic.Type) -> some AsyncSequence<PermissionResponse<Topic>, Never> where Topic : QuestionTopici opisane jako „Registers the topic type with the system and returns an asynchronous sequence of responses”, bez wzmianki o uruchamianiu w tle. Istnieją cztery wariantyask(_:in:): dwa przyjmująceUIViewControllerna iOS, iPadOS i visionOS oraz dwa przyjmująceNSWindowna macOS, po jednym na każdy typ tematu.PermissionResponseudostępniachoiceiquestion;PermissionChoice.Answerpublikuje dokładnie dwa przypadki,approvalidenial. ↩↩ -
Odpowiedź pracownika Apple w wątku AppStore.ageRatingCode always returns 0 on real device, Apple Developer Forums. Wpis otwierający z kwietnia 2026 roku zgłasza wartość
0na fizycznym urządzeniu z iOS 26.4, z zalogowanym kontem sandbox i klasyfikacją wiekową skonfigurowaną w App Store Connect. Odpowiedź, od autora oznaczonego jako Apple Staff, brzmi: „TheageRatingCodeAPI should be used to observe changes to your app’s age rating over time by comparing to its last known value. If your app’s age rating code has changed, consider informing parents or guardians by using the Significant Change API” oraz „A value of0is expected during development of your app when it is built and run from Xcode and Sandbox environments (including TestFlight)”. Pobrane dwukrotnie 26 lipca 2026 roku z identycznym brzmieniem; forum renderuje się przez JavaScript i wyświetla wiek odpowiedzi jako względny znacznik „1w”, a nie datę, więc nie podaję tu daty publikacji. Odpowiedź na forum deweloperskim jest słabszym dowodem niż strona dokumentacji, a dokumentacja Apple dlaageRatingCodew ogóle nie wspomina o wartości0. Wniosek wyciągnięty w tym artykule — że zapisanie0jako punktu odniesienia produkuje fałszywą zmianę przy pierwszej kompilacji z App Store — jest moją interpretacją tej odpowiedzi oraz udokumentowanego przez Apple wzorca porównawczego. ↩↩↩↩ -
Apple, Enabling App Store Server Notifications. Źródło progu TLS („your server must support the Transport Layer Security (TLS) 1.2 protocol or later”), konfiguracji adresów URL osobno dla każdego środowiska w App Store Connect, ograniczenia portów (443 albo 1024 i wyżej) oraz podsieci z listy dozwolonych („add the IP address subnet
17.0.0.0/8”, która „applies to both the sandbox and the production environments”). Towarzyszący artykuł Receiving App Store Server Notifications opisuje podpisany JWSsignedPayloadi na 26 lipca 2026 roku wspomina wyłącznie o obiekciedata, bez odniesienia doappData. ↩ -
Apple, Responding to App Store Server Notifications. Źródło kodów sukcesu („Send HTTP
200, or any HTTP code between200and206”), wyzwalacza ponowień („Send HTTP50xor40xto have the App Store retry the notification”), harmonogramu ponowień w wersji 2 („it retries five times, at 1, 12, 24, 48, and 72 hours after the previous attempt”), ograniczenia sandboksa („Retry notifications are available only in the production environment. In the sandbox environment, the App Store server attempts to send the notification one time”) oraz ścieżki odzyskiwania przezGet-Notification-History. ↩↩ -
Apple, subtype, App Store Server Notifications. Strona publikuje 19 możliwych wartości (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), każdą przypisaną do konkretnego typu powiadomienia. Ciąg
RESCIND_CONSENTnie występuje nigdzie w ładunku tej strony, odczytanej 26 lipca 2026 roku. ↩ -
Apple, PermissionButton, PermissionKit.
@MainActor @preconcurrency struct PermissionButton<Topic, Label> where Topic : QuestionTopic, Label : View, dostępny od iOS 26.2, iPadOS 26.2, Mac Catalyst 26.2, macOS 26.2 i visionOS 26.2, z dwoma wariantamiinit(question:label:)ograniczonymi odpowiednio doCommunicationTopiciSignificantAppUpdateTopic. ZastępujeCommunicationLimitsButton, który Apple wymienia na stronie frameworka pod nagłówkiem wycofanych API. ↩↩↩ -
Apple, AgeRangeService.showSignificantUpdateAcknowledgment(in:updateDescription:), SignificantUpdateAction oraz wartość środowiskowa SwiftUI showSignificantUpdateAcknowledgment. Metoda jest zadeklarowana jako
@MainActor func showSignificantUpdateAcknowledgment(in windowScene: UIWindowScene, updateDescription: String) async throwsi publikuje iOS 26.4, iPadOS 26.4 oraz Mac Catalyst 26.4, bez wiersza macOS;AgeRangeServicewymienia pod nagłówkiem „Displaying update acknowledgments” wyłącznie ten jeden wariant.SignificantUpdateActioni wartość środowiskowa publikują te same trzy platformy. Uwaga Important Apple przy metodzie: „Before calling this function, checkRegulatoryFeatureto determine if a person must acknowledge your significant app change”. Omówienie wartości środowiskowej dodaje, że akcję tę należy wywoływać „from aButtonoronAppear(perform:)”. ↩↩↩↩ -
Apple, requestAgeRange(ageGates:::in:), Declared Age Range. Apple publikuje dwa warianty: jeden przyjmujący
in viewController: UIViewControllerna iOS 26.0, iPadOS 26.0 i Mac Catalyst 26.0 oraz jeden przyjmującyin window: NSWindowna macOS 26.0. Istnienie wariantu zNSWindowdla żądania przedziału wiekowego i jego brak dla arkusza potwierdzenia cytowanego w przypisie 27 są podstawą odczytania pominięcia macOS jako luki, a nie decyzji kierunkowej; Apple nie opublikowało niczego w żadną stronę. ↩ -
Apple, Requesting people’s age range information in your app, Declared Age Range. Źródło arytmetyki bramek cytowanej w tekście („Można określić maksymalnie trzy bramki wiekowe, które tworzą maksymalnie cztery możliwe przedziały wiekowe”), ograniczenia rozpiętości („Każdy przedział musi obejmować co najmniej dwa lata”) oraz semantyki granic („Gdy wartość
lowerBoundwynosinil, osoba jest poniżej najniższej z Państwa bramek wiekowych” i „GdyupperBoundwynosinil, osoba osiąga najwyższą z Państwa bramek wiekowych lub ją przekracza”). Bramki na 13, 16 i 18 zwracają dokładnie te cztery przedziały, które Apple publikuje dla Teksasu, a oba przedziały ograniczone obustronnie spełniają minimum dwóch lat: 13-15 obejmuje trzy lata, a 16-17 dwa. Artykuł ostrzega także, że regiony, do których należy konto danej osoby, „określają bramki wiekowe, których system używa do zwracania przedziałów wiekowych, a mogą się one różnić od bramek podanych w żądaniu”. Odczytane z JSON dokumentacji Apple 26 lipca 2026 roku. ↩ -
Badanie własne autora, 26 lipca 2026: jak ustalono twierdzenia dotyczące platform. Wartości platform odczytano z ustawienia kompilacji
SUPPORTED_PLATFORMSkażdego projektu, a nie z kluczy celu wdrożenia, które Xcode zapisuje niezależnie od miejsca docelowego: Reps deklarujeappletvos appletvsimulator iphoneos iphonesimulator macosx xros xrsimulatorplus osobny celwatchos watchsimulator, a Ace Citizenship deklaruje wyłącznieiphoneos iphonesimulator. Ta metoda zaniża liczbę platform, czego dowodem jest Return: jedyną wartościąSUPPORTED_PLATFORMSw Return jestiphoneos iphonesimulator macosx xros xrsimulator, podczas gdy jego cele TV i watch niosą zamiast tegoSDKROOT = appletvosiSDKROOT = watchos, więc przegląd poSUPPORTED_PLATFORMSnie widzi żadnego z nich. Każde twierdzenie „wydaje na platformę X” w tym artykule pochodzi zatem z App Store Connect, a nie z ustawień kompilacji. Return: TV_OS 1.0 i 1.0.1 obie w stanie READY_FOR_DISTRIBUTION, IOS i MAC_OS 1.0.1 tak samo, a cel iOS osadzaReturnWatch Watch App(com.941apps.Return.watchkitapp) przez fazę Embed Watch Content, i tak właśnie aplikacja na zegarek trafia na nadgarstek. Reps: IOS 1.1 i MAC_OS 1.1 w stanie READY_FOR_DISTRIBUTION, żadna wersja TV_OS nigdy w tym stanie, a TV_OS 1.2 czeka w stanie WAITING_FOR_REVIEW od 2 czerwca 2026 roku, więc Reps wydaje na iOS i macOS. ↩ -
Badanie własne autora, 26 lipca 2026, na macOS 26.5.2 (kompilacja 25F84) z Xcode 26.6 (kompilacja 17F113) i Swift 6.3.3. Reguła doboru, podana tak, by dało się sprawdzić i odtworzyć zakres, a nie przyjmować go na wiarę: każdy wiersz tabeli aktywnych projektów w moim własnym pliku konfiguracyjnym agenta (
~/.claude/CLAUDE.md), za którym stoi projekt Xcode, co daje dokładnie siedem:Reps,Return,Banana List(wydawany jako Get Bananas),Ace-Citizenship,Water,ResumeGeniAppiYawara. Ta reguła jest zarazem wadą badania, ponieważRandorinie ma wiersza w tej tabeli, a to właśnieRandoriokazał się projektem, który miał znaczenie. Pięć z siedmiu ma rekord w App Store Connect (Reps 6776044339, Return 6756242021, Get Bananas 6756241534, Ace Citizenship 6532592671, ResumeGeni 6771154645);WateriYawaranie występują wśród 18 aplikacji na koncie, więc żadna z nich nigdy nie została zgłoszona i nazywanie którejkolwiek aplikacją jest nadużyciem. Liczba plików Swift na projekt: 77, 57, 55, 26, 34, 71 i 143. Skan pod kątem zgody objął*.swift,*.entitlements,*.plistiproject.pbxproj: 85, 65, 63, 34, 38, 74 i 149, co sumuje się do 508. Odtworzenie tych liczb wymaga ośmiu, a nie sześciu wykluczonych nazw katalogów:build,DerivedData,.build,Pods,.git,worktreesoraz — wyłącznie w Reps —.venv(142 listy właściwości wReps/.venviReps/server/.venv) i.xcode-state-backups(18). Przy wykluczeniu tylko pierwszych sześciu Reps daje 245, a suma 668, więc lista wykluczeń jest nośna, a każda nazwa, od której zależy, jest tu wypisana. Szesnaście wzorców, wszystkie z rozróżnianiem wielkości liter:PermissionKit,CommunicationLimits,AskPermission,AskCenter,SignificantAppUpdateTopic,SignificantUpdateAction,PermissionTopic,com.apple.developer.family-controls,FamilyControls,ManagedSettings,DeviceActivity,AuthorizationCenter,CKShare,sharedCloudDatabase,publicCloudDatabaseiageRatingCode. Zero pasujących plików we wszystkich siedmiu projektach, wliczającAskCenter. Dwa wzorce kontrolne przepuszczone przez identyczny potok zwróciły wynik niezerowy (StoreKittrafił w trzy pliki w Reps,CKContainer|NSPersistentCloudKitContainer|SwiftDataw 17 w Banana List), i stąd wiem, że potok czyta pliki, a nie zawodzi po cichu. Onboarding Ace Citizenship (IntroCarouselView,WelcomeView,AddStateView,AddRepresentativeView) nie zawiera żadnego pola wieku ani daty urodzenia, a jegoPrivacyInfo.xcprivacydeklaruje pusteNSPrivacyCollectedDataTypes; wiersz o uprawnieniu „Age 18 or older” pochodzi z~/Projects/acecitizenship.app/content/blog/n400-application-guide.md. ↩↩↩ -
Badanie własne autora, 26 lipca 2026: szerszy skan i Randori. Szerszy skan przepuścił te same 16 wzorców przez wszystkie repozytoria w
~/Projects, wykluczając te same osiem nazw plusnode_modulesoraz ścieżki objęte gitignore, i trafił w 21 plików. Siedem to kod: sześć plików Swift wRandori/Randori/oraz_archive/Oishii-AI/Oishii AI/Services/CloudKitManager.swift(publicCloudDatabase, w zarchiwizowanym projekcie). Pozostałe 14 to teksty lub stan maszynowy: dziewięć planów i dokumentów projektowych Randori, trzy wpisy we własnymcontent/blog/tej witryny (jeden z nich to szkic tego artykułu), jedna notatka przekazania w Obsidianie orazobsidian-signals/40-Projects/blakecrosley-com/wwdc-2026/.pulse_state.json, który zapisuje skrypt, a nie człowiek. Randori tocom.wayofyawara.randori, aplikacja 6789693294 w App Store Connect, z wersją 1.0 w stanie PREPARE_FOR_SUBMISSION.CKShareisharedCloudDatabasetrafiają w 20 linii w sześciu plikach:ConnectionStore.swift(10),RandoriApp.swift(5),ProfileCardView.swift(2) oraz po jednej linii wCKPostTransport.swift,CloudShareSheet.swiftiSocialContracts.swift.ConnectionStoreopisuje własny kształt w komentarzu nagłówkowym jako „one zone (ProfileCardZone), one record type (ConnectionCard), one share”, trzyma obok siebieprivateCloudDatabaseisharedCloudDatabaseoraz implementujeaccept(_ metadata: CKShare.Metadata)w linii 355,ensureOutboundShare()w linii 547 i blokowanie oraz odblokowywanie per konto.RandoriApp.swiftimplementujeuserDidAcceptCloudKitShareWithzarówno w delegacie aplikacji (linia 73), jak i w delegacie sceny okna (linia 97, z komentarzem „Warm: the app is running when the link is opened”), podczas gdyRandoriSceneDelegate.scene(_:willConnectTo:options:)odczytujeconnectionOptions.cloudKitShareMetadataw linii 88 pod komentarzem „Cold start: the invitation rides the connection options”, i dlatego tekst przypisuje zimny start opcjom połączenia, a nie wywołaniu zwrotnemu akceptacji.TagConsentStoreto własny rejestr zgód aplikacji, zakresowany per konto iCloud, a jego udokumentowane zachowanie domyślne to blokada: partnera, którego rekord polityki nie dotarł, nie da się oznaczyć.Randori/Randori.entitlementsżąda CloudKit, HealthKit iaps-environment. Każdy wzorzec zgody poza dwoma dotyczącymi udostępniania w CloudKit zwraca zero w całym tym repozytorium, podobnie jakStoreKit,signedPayloadinotificationType, więc aplikacja nic nie sprzedaje i nie prowadzi własnego serwera;docs/asc-metadata.mdprzygotowuje cenę darmową i klasyfikację 4+. ↩↩↩↩ -
Badanie własne autora, 26 lipca 2026: miejsca wywołań StoreKit i dowody serwerowe. StoreKit występuje w trzech projektach:
Reps/Reps/Services/RepsProStore.swift(subskrypcja odnawialna automatycznie, dwa poziomy),Ace Citizenship/StoreKitManager.swift(jeden produkt niekonsumowalny) iResumeGeni/Subscription/(jedna subskrypcja miesięczna, bramkowana po stronie serwera). Obsługa.pendingcytowana w tabeli znajduje się wRepsProStore.swift:134(case .userCancelled, .pending:),StoreKitManager.swift:69-73(osobnycase .pending:, którego własnereturn falseto linia 73) orazSubscriptionStore.swift:209-212(nazwany wynik oczekiwania). To miejsca wywołań zostawiają użytkownika bez wyjścia:RepsProPaywallView.swift:359-361zamyka ekran wyłącznie wewnątrzif await store.purchase(plan), aContentView.swift:484-485odblokowuje wyłącznie wewnątrzif success, więcfalsezwrócone dla zakupu w stanie oczekiwania nie zmienia na ekranie nic w żadnej z tych aplikacji. ResumeGeni dochodzi natomiast doPaywallView.swift:526, gałęzi.pending, która wyświetla komunikat „Waiting for approval. You’ll get access once it’s approved”. Endpoint App Store Server Notifications w ResumeGeni toPOST /api/appstore/notificationsw~/Projects/resumegeni/app/routers/appstore.py;POSTz{"signedPayload":"probe"}zwraca HTTP 400{"status":"invalid"}, co jest osiągalne dopiero za flagą włączającą i wewnątrz weryfikatora łańcucha certyfikatów Apple, aGETzwraca 405. Zarejestrowane są oba adresy środowiskowe, a dowodem na to jest dostarczenie, a nie dokument operacyjny: baza D1941-analytics, tabelafunnel_events, zawiera 56 wierszy, gdzieplatform='ios', a metadane niosąnotification_type, w przedziale od 2026-06-26T15:48:51Z do 2026-07-25T16:40:11Z, z czego 55 niesieenvironmentrównesandbox, a jedenproduction(2026-07-21T18:08:21Z). Liczbę 56 należy traktować jako dolną granicę, ponieważ usługa mapuje na wiersze lejka tylko pięć nazw zdarzeń; faktycznie zaobserwowane cztery typy to DID_RENEW (42), SUBSCRIBED (6), EXPIRED (5) i DID_CHANGE_RENEWAL_STATUS (3).app/services/app_store_notification_service.pywymienia 13 typów powiadomień, z czego osiem trafia do wykonywalnej gałęzi rozdzielającej w_funnel_event_name(linie 75-89): SUBSCRIBED, OFFER_REDEEMED, DID_RENEW, REFUND, REVOKE, EXPIRED, DID_CHANGE_RENEWAL_STATUS i DID_FAIL_TO_RENEW. Pozostałych pięć występuje wyłącznie w komentarzach: PRICE_INCREASE, RENEWAL_EXTENDED i METADATA_UPDATE w linii 73, EXTERNAL_PURCHASE_TOKEN i TEST w linii 187.RESCIND_CONSENT,CONSUMPTION_REQUEST,REFUND_DECLINEDiREFUND_REVERSEDzwracają zero trafień w całym tym repozytorium. Dla pełności obrazu, co nie jest dowodem:docs/SUBSCRIPTION_GO_LIVE.md:83faktycznie mówi „ustaw OBA adresy URL, produkcyjny i sandboksowy”, ale linia ta znajduje się w instrukcji operacyjnej pod nagłówkiem odnotowującym, że krok ten „needs your re-auth”, zapisuje więc zamiar, a nie dokonaną rejestrację; twierdzenie o rejestracji opiera się natomiast na dostarczonych powiadomieniach. ↩↩↩ -
Apple, Testing Ask to Buy in Xcode, StoreKit. Źródło opisu mechanizmu podanego przez Apple („With Ask to Buy, when a child wants to make an eligible purchase or download, the system sends the purchase request to the parent or guardian”) oraz zachowania przy odmowie („Your app doesn’t receive a transaction because you declined Ask to Buy”). Artykuł dokumentuje też przełącznik Ask to Buy w sekcji Purchase Options edytora konfiguracji StoreKit, a menedżer transakcji podaje stany Pending Ask to Buy, Ask to Buy Approved i Ask to Buy Declined. ↩↩↩
-
Apple, Product.PurchaseResult.pending i Transaction.updates, StoreKit, oba dostępne od iOS 15.0. Źródło opisu przypadku („The purchase is pending, and requires action from the customer”), ścieżki rozstrzygnięcia („If a pending purchase succeeds, StoreKit delivers the resulting
Transactionin the transactionupdates”) oraz przeznaczenia sekwencji („This sequence receives transactions that occur outside of the app, such as Ask to Buy transactions, offer code redemptions, and purchases that customers make in the App Store”). Enum Product.PurchaseResult publikuje trzy przypadki:success(_:),pendingiuserCancelled. Własny przykład Apple na stronie enuma opatruje gałąź oczekiwania komentarzem „The purchase requires action from the customer. If the transaction completes, it’s available throughTransaction.updates”. ↩↩ -
Każdy symbol w powyższym listingu rozdzielającym ścieżki zweryfikowany względem JSON dokumentacji Apple 26 lipca 2026 roku.
AgeRangeService.sharedtostatic let shared: AgeRangeService(iOS 26.0). Wartości środowiskowe SwiftUI torequestAgeRange,var requestAgeRange: DeclaredAgeRangeAction { get }(iOS 26.0), orazshowSignificantUpdateAcknowledgment,var showSignificantUpdateAcknowledgment: SignificantUpdateAction { get }(iOS 26.4). Próg@available(iOS 26.5, *)w listingu nie bierze się z żadnej z nich:AgeRangeService.AgeRangeDeclaration.confirmedpublikuje iOS 26.5, iPadOS 26.5, Mac Catalyst 26.5 i macOS 26.5, czyli o jedno wydanie później niż akcja potwierdzenia, więc każdy kod odróżniający dorosłego potwierdzonego dziedziczy ten wyższy próg. Własny projekt przykładowy Apple publikuje tę samą dostępność 26.5.6AgeRangeService.AgeRangeudostępnialowerBound,upperBound,ageRangeDeclarationzadeklarowane jakovar ageRangeDeclaration: AgeRangeService.AgeRangeDeclaration?orazactiveParentalControls. Porównanie== .confirmedz tą wartością opcjonalną jest poprawne, ponieważAgeRangeDeclarationzgodnie z sekcją relacji spełniaEquatableiHashable. InicjalizatorPermissionButtontoinit(question: PermissionQuestion<Topic>, @ViewBuilder label: @escaping () -> Label), w użytym tu wariancie ograniczony doSignificantAppUpdateTopic.26ChangedFeatureViewiAccountVerificationPromptto symbole zastępcze dla Państwa własnych widoków, a nie symbole Apple. ↩↩↩ -
Apple, Age ratings values and definitions, pomoc App Store Connect, odczytane 26 lipca 2026 roku jako źródło potwierdzające, że zmiany z 18 czerwca 2026 roku zapowiedziane w przypisie 9 weszły w życie. Pod nagłówkiem „Australia age rating values” tabela publikuje obecnie dwie klasyfikacje, 16+ i R 18+, bez wiersza 15+. Istnieje też odrębna sekcja „Vietnam age rating values”, wprowadzona słowami „As required by Article 38 of Vietnam Decree 147”, której wiersz 00+ zdefiniowano jako aplikacje, które „contain no objectionable material but may contain instances of the following content”, z wyliczeniem kontroli rodzicielskiej, weryfikacji wieku, treści tworzonych przez użytkowników, wiadomości i czatu, reklam oraz sporadycznych konkursów. Lista deskryptorów podana przez Apple 21 maja dla migracji australijskiej nie odpowiada obecnej liście wyzwalaczy 16+ na tej stronie; tej rozbieżności nie rozstrzygnąłem i nie opieram się na niej, ponieważ wyprowadzone tu twierdzenie mówi wyłącznie, że poziom 15+ zniknął, a tabela dla Wietnamu istnieje. ↩↩↩