← Wszystkie wpisy

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.ageRatingCode w StoreKit oraz powiadomienie serwerowe RESCIND_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_CONSENT nie 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

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


  1. Apple, App Store Server Notifications changelog. Pod nagłówkiem z 4 listopada 2025 roku, w sekcji nowych funkcji: „Updated the responseBodyV2DecodedPayload to include the new payload object, appData” oraz „Added the notification type RESCIND_CONSENT to notificationType”. 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. 

  2. 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ąg RESCIND_CONSENT wystę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ć, że REVOKE oznacza 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. 

  3. Apple, appData, App Store Server Notifications, wprowadzone w wersji 2.19. Źródło zdania „The appData object is part of the responseBodyV2DecodedPayload. This object is present in the payload when the notificationType is RESCIND_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, environment i signedAppTransactionInfo (typu JWSAppTransaction). Strona responseBodyV2DecodedPayload stwierdza tę wyłączność niezależnie, opisując appData jako pole, które „appears when the notificationType is RESCIND_CONSENT”, i dodając, że „The data, appData, summary, and externalPurchaseToken fields are mutually exclusive. The payload contains only one of these fields”. Te dwie strony są podstawą dla nazwania RESCIND_CONSENT jedynym typem powiadomienia, którego ładunek niesie appData; ciąg appData nie występuje nigdzie na stronie notificationType

  4. 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. 

  5. 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. 

  6. 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, .verifiedAdult opisane jako „an adult with a confirmed payment method”, .unverifiedAdult jako „an adult without account verification”, .declinedSharing), wyniku dla niezweryfikowanego dorosłego („the app sets the phase to .blocked and prevents access until the person verifies their account in Settings”), ścieżki małoletniego budującej SignificantAppUpdateTopic i PermissionQuestion, wysyłki przez PermissionButton, listingu nasłuchu AskCenter.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ń w NSUbiquitousKeyValueStore wraz z didChangeExternallyNotification („so other devices don’t present the same flow again”) oraz zwolnienia opartego na originalAppVersion („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. 

  7. 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ąca notAvailable „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”) oraz significantAppChangeRequiresParentalConsent („Indicates a parent or guardian is required to acknowledge and consent to a significant app change”). 

  8. 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_CONSENT value from notificationType”), 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. 

  9. 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. 

  10. 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, or nil if 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 strona SignificantAppUpdateTopic jego celem. Własny przykład Apple na tej stronie to guard let wokół 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 typu AppStore, ani w materiałach pomocy App Store Connect dotyczących klasyfikacji wiekowych. 

  11. 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 selfDeclared lub confirmed, zachowania przy pytaniu osoby dorosłej („For 18+ test cases, PermissionKit throws AskError.notAvailable rather than returning a PermissionChoice. Calling AskCenter.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 a RESCIND_CONSENT notificationType. The notification payload includes an appData object with app metadata, including the bundleId and environment fields”). Trzy wiersze dla małoletnich w macierzy to: poniżej 13 zatwierdzone, 13-15 zatwierdzone i 16-17 odrzucone, wszystkie z deklaracją wieku guardianDeclared

  12. 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 z QuestionTopic, z init(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. 

  13. 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. 

  14. 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. 

  15. 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. 

  16. 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:) i init(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”. expirationDate jest zadeklarowana jako final 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. 

  17. 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 PermissionKit framework are only available using iMessage”. Warto zauważyć, że artykuł dokumentuje wyłącznie przepływ CommunicationTopic; 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. 

  18. Apple, AskError, PermissionKit. enum AskError, zgodny z LocalizedError, 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”), contactSyncNotSetup i invalidQuestion. Dwa nie publikują opisu na własnych stronach: systemError(underlyingError:) w iOS 26.1 oraz notAvailable w iOS 26.2, pojawiający się wraz z SignificantAppUpdateTopic. Znaczenie notAvailable występuje wyłącznie w artykule o testowaniu w sandboksie cytowanym w przypisie 11; systemError przynajmniej nazywa swoją przyczynę w sygnaturze. Czy communicationLimitsNotEnabled lub contactSyncNotSetup mogą wystąpić także przy pytaniu o istotną aktualizację, pozostaje niedopowiedziane. 

  19. Apple, appTransactionID i originalAppVersion w AppTransaction, StoreKit. Źródło semantyki identyfikatora („The App Store generates a single, globally unique appTransactionID for 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: „The appTransactionID is available even if a customer makes no in-app purchases”. originalAppVersion to „The app version that the customer originally purchased from the App Store”, niosąca CFBundleShortVersionString na macOS i CFBundleVersion gdzie indziej, a w środowisku sandbox zawsze 1.0. Odpowiadający jej typ po stronie serwera to appTransactionId w App Store Server API, wprowadzony w wersji 1.15. 

  20. Apple, CommunicationLimits i updates, PermissionKit. updates jest zadeklarowane jako final 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 grupuje updates oraz obie wersje CommunicationLimits.ask(_:in:) pod nagłówkiem wycofanych API; sama klasa pozostaje aktualna dla isKnownHandle(_:) i knownHandles(in:). Sekwencja zastępcza porzuca tę obietnicę: ani opis, ani omówienie AskCenter.responses(for:) nie wspominają o uruchamianiu w tle, i na tym polega cała luka dokumentacyjna sygnalizowana w tekście. 

  21. 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. AskCenter to final class osiągana przez static 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 jako final func responses<Topic>(for topicType: Topic.Type) -> some AsyncSequence<PermissionResponse<Topic>, Never> where Topic : QuestionTopic i opisane jako „Registers the topic type with the system and returns an asynchronous sequence of responses”, bez wzmianki o uruchamianiu w tle. Istnieją cztery warianty ask(_:in:): dwa przyjmujące UIViewController na iOS, iPadOS i visionOS oraz dwa przyjmujące NSWindow na macOS, po jednym na każdy typ tematu. PermissionResponse udostępnia choice i question; PermissionChoice.Answer publikuje dokładnie dwa przypadki, approval i denial

  22. 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ść 0 na 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: „The ageRatingCode API 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 of 0 is 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 dla ageRatingCode w ogóle nie wspomina o wartości 0. Wniosek wyciągnięty w tym artykule — że zapisanie 0 jako punktu odniesienia produkuje fałszywą zmianę przy pierwszej kompilacji z App Store — jest moją interpretacją tej odpowiedzi oraz udokumentowanego przez Apple wzorca porównawczego. 

  23. 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 JWS signedPayload i na 26 lipca 2026 roku wspomina wyłącznie o obiekcie data, bez odniesienia do appData

  24. Apple, Responding to App Store Server Notifications. Źródło kodów sukcesu („Send HTTP 200, or any HTTP code between 200 and 206”), wyzwalacza ponowień („Send HTTP 50x or 40x to 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 przez Get-Notification-History

  25. 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_CONSENT nie występuje nigdzie w ładunku tej strony, odczytanej 26 lipca 2026 roku. 

  26. 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 wariantami init(question:label:) ograniczonymi odpowiednio do CommunicationTopic i SignificantAppUpdateTopic. Zastępuje CommunicationLimitsButton, który Apple wymienia na stronie frameworka pod nagłówkiem wycofanych API. 

  27. Apple, AgeRangeService.showSignificantUpdateAcknowledgment(in:updateDescription:), SignificantUpdateAction oraz wartość środowiskowa SwiftUI showSignificantUpdateAcknowledgment. Metoda jest zadeklarowana jako @MainActor func showSignificantUpdateAcknowledgment(in windowScene: UIWindowScene, updateDescription: String) async throws i publikuje iOS 26.4, iPadOS 26.4 oraz Mac Catalyst 26.4, bez wiersza macOS; AgeRangeService wymienia pod nagłówkiem „Displaying update acknowledgments” wyłącznie ten jeden wariant. SignificantUpdateAction i wartość środowiskowa publikują te same trzy platformy. Uwaga Important Apple przy metodzie: „Before calling this function, check RegulatoryFeature to determine if a person must acknowledge your significant app change”. Omówienie wartości środowiskowej dodaje, że akcję tę należy wywoływać „from a Button or onAppear(perform:)”. 

  28. Apple, requestAgeRange(ageGates:::in:), Declared Age Range. Apple publikuje dwa warianty: jeden przyjmujący in viewController: UIViewController na iOS 26.0, iPadOS 26.0 i Mac Catalyst 26.0 oraz jeden przyjmujący in window: NSWindow na macOS 26.0. Istnienie wariantu z NSWindow dla żą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ę. 

  29. 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ść lowerBound wynosi nil, osoba jest poniżej najniższej z Państwa bramek wiekowych” i „Gdy upperBound wynosi nil, 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. 

  30. Badanie własne autora, 26 lipca 2026: jak ustalono twierdzenia dotyczące platform. Wartości platform odczytano z ustawienia kompilacji SUPPORTED_PLATFORMS każdego projektu, a nie z kluczy celu wdrożenia, które Xcode zapisuje niezależnie od miejsca docelowego: Reps deklaruje appletvos appletvsimulator iphoneos iphonesimulator macosx xros xrsimulator plus osobny cel watchos watchsimulator, a Ace Citizenship deklaruje wyłącznie iphoneos iphonesimulator. Ta metoda zaniża liczbę platform, czego dowodem jest Return: jedyną wartością SUPPORTED_PLATFORMS w Return jest iphoneos iphonesimulator macosx xros xrsimulator, podczas gdy jego cele TV i watch niosą zamiast tego SDKROOT = appletvos i SDKROOT = watchos, więc przegląd po SUPPORTED_PLATFORMS nie 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 osadza ReturnWatch 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. 

  31. 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, ResumeGeniApp i Yawara. Ta reguła jest zarazem wadą badania, ponieważ Randori nie ma wiersza w tej tabeli, a to właśnie Randori okazał 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); Water i Yawara nie 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, *.plist i project.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, worktrees oraz — wyłącznie w Reps — .venv (142 listy właściwości w Reps/.venv i Reps/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, publicCloudDatabase i ageRatingCode. Zero pasujących plików we wszystkich siedmiu projektach, wliczając AskCenter. Dwa wzorce kontrolne przepuszczone przez identyczny potok zwróciły wynik niezerowy (StoreKit trafił w trzy pliki w Reps, CKContainer|NSPersistentCloudKitContainer|SwiftData w 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 jego PrivacyInfo.xcprivacy deklaruje puste NSPrivacyCollectedDataTypes; wiersz o uprawnieniu „Age 18 or older” pochodzi z ~/Projects/acecitizenship.app/content/blog/n400-application-guide.md

  32. 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 plus node_modules oraz ścieżki objęte gitignore, i trafił w 21 plików. Siedem to kod: sześć plików Swift w Randori/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łasnym content/blog/ tej witryny (jeden z nich to szkic tego artykułu), jedna notatka przekazania w Obsidianie oraz obsidian-signals/40-Projects/blakecrosley-com/wwdc-2026/.pulse_state.json, który zapisuje skrypt, a nie człowiek. Randori to com.wayofyawara.randori, aplikacja 6789693294 w App Store Connect, z wersją 1.0 w stanie PREPARE_FOR_SUBMISSION. CKShare i sharedCloudDatabase trafiają w 20 linii w sześciu plikach: ConnectionStore.swift (10), RandoriApp.swift (5), ProfileCardView.swift (2) oraz po jednej linii w CKPostTransport.swift, CloudShareSheet.swift i SocialContracts.swift. ConnectionStore opisuje własny kształt w komentarzu nagłówkowym jako „one zone (ProfileCardZone), one record type (ConnectionCard), one share”, trzyma obok siebie privateCloudDatabase i sharedCloudDatabase oraz implementuje accept(_ metadata: CKShare.Metadata) w linii 355, ensureOutboundShare() w linii 547 i blokowanie oraz odblokowywanie per konto. RandoriApp.swift implementuje userDidAcceptCloudKitShareWith zaró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 gdy RandoriSceneDelegate.scene(_:willConnectTo:options:) odczytuje connectionOptions.cloudKitShareMetadata w 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. TagConsentStore to 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 i aps-environment. Każdy wzorzec zgody poza dwoma dotyczącymi udostępniania w CloudKit zwraca zero w całym tym repozytorium, podobnie jak StoreKit, signedPayload i notificationType, więc aplikacja nic nie sprzedaje i nie prowadzi własnego serwera; docs/asc-metadata.md przygotowuje cenę darmową i klasyfikację 4+. 

  33. 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) i ResumeGeni/Subscription/ (jedna subskrypcja miesięczna, bramkowana po stronie serwera). Obsługa .pending cytowana w tabeli znajduje się w RepsProStore.swift:134 (case .userCancelled, .pending:), StoreKitManager.swift:69-73 (osobny case .pending:, którego własne return false to linia 73) oraz SubscriptionStore.swift:209-212 (nazwany wynik oczekiwania). To miejsca wywołań zostawiają użytkownika bez wyjścia: RepsProPaywallView.swift:359-361 zamyka ekran wyłącznie wewnątrz if await store.purchase(plan), a ContentView.swift:484-485 odblokowuje wyłącznie wewnątrz if success, więc false zwrócone dla zakupu w stanie oczekiwania nie zmienia na ekranie nic w żadnej z tych aplikacji. ResumeGeni dochodzi natomiast do PaywallView.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 to POST /api/appstore/notifications w ~/Projects/resumegeni/app/routers/appstore.py; POST z {"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, a GET zwraca 405. Zarejestrowane są oba adresy środowiskowe, a dowodem na to jest dostarczenie, a nie dokument operacyjny: baza D1 941-analytics, tabela funnel_events, zawiera 56 wierszy, gdzie platform='ios', a metadane niosą notification_type, w przedziale od 2026-06-26T15:48:51Z do 2026-07-25T16:40:11Z, z czego 55 niesie environment równe sandbox, a jeden production (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.py wymienia 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_DECLINED i REFUND_REVERSED zwracają zero trafień w całym tym repozytorium. Dla pełności obrazu, co nie jest dowodem: docs/SUBSCRIPTION_GO_LIVE.md:83 faktycznie 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. 

  34. 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. 

  35. 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 Transaction in the transaction updates”) 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(_:), pending i userCancelled. 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 through Transaction.updates”. 

  36. Każdy symbol w powyższym listingu rozdzielającym ścieżki zweryfikowany względem JSON dokumentacji Apple 26 lipca 2026 roku. AgeRangeService.shared to static let shared: AgeRangeService (iOS 26.0). Wartości środowiskowe SwiftUI to requestAgeRange, var requestAgeRange: DeclaredAgeRangeAction { get } (iOS 26.0), oraz showSignificantUpdateAcknowledgment, var showSignificantUpdateAcknowledgment: SignificantUpdateAction { get } (iOS 26.4). Próg @available(iOS 26.5, *) w listingu nie bierze się z żadnej z nich: AgeRangeService.AgeRangeDeclaration.confirmed publikuje 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.6 AgeRangeService.AgeRange udostępnia lowerBound, upperBound, ageRangeDeclaration zadeklarowane jako var ageRangeDeclaration: AgeRangeService.AgeRangeDeclaration? oraz activeParentalControls. Porównanie == .confirmed z tą wartością opcjonalną jest poprawne, ponieważ AgeRangeDeclaration zgodnie z sekcją relacji spełnia Equatable i Hashable. Inicjalizator PermissionButton to init(question: PermissionQuestion<Topic>, @ViewBuilder label: @escaping () -> Label), w użytym tu wariancie ograniczony do SignificantAppUpdateTopic.26 ChangedFeatureView i AccountVerificationPrompt to symbole zastępcze dla Państwa własnych widoków, a nie symbole Apple. 

  37. 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. 

Powiązane artykuły

Pole wyboru „media społecznościowe" w App Store i jego cena

Wrześniowa zasada App Store nakazuje deklarowanie funkcji społecznościowych w każdym zgłoszeniu. Wyłączenie dzieci poniż…

24 min czytania

Xcode 27 usuwa ld64 i wymaga unikalnych nazw modułów

Xcode 27 usuwa linker ld64 i wymaga unikalnych nazw modułów Clang. Oba pęknięcia widać przy aktualizacji toolchaina: aud…

20 min czytania