The Consent Lifecycle: What Ships After the Age Check

On November 4, 2025 Apple added a notification type to App Store Server Notifications that fires on a parental decision rather than a payment: RESCIND_CONSENT, which indicates “the parent or guardian has withdrawn consent for a child’s app usage.”12

It is the only one of the service’s 23 types whose payload carries an appData object instead of transaction information, and appData holds a signed app transaction that exists “even if a customer makes no in-app purchases.”319 An app that has never sold anything therefore acquires its first reason to run a notification endpoint.

The social media declaration covers reading a person’s age range: the entitlement, the gates, and what the returned bounds mean. Treat that as settled and start one step later. Age assurance answers how old someone is. The three APIs here answer who approves, whether an approval already granted survives a change to your app, and what happens when a guardian takes it back.21012

TL;DR

  • Three APIs sit downstream of the age check, and Apple ships them as a set: the Significant Change API under PermissionKit, AppStore.ageRatingCode in StoreKit, and the RESCIND_CONSENT server notification.45
  • Apple ties its four-tool list to Texas, Utah, and Louisiana rather than Texas alone, every date it gave for those three has passed, and Apple scopes the duty to “certain regions, where legally required.”5815
  • The gate deciding whether you run any of it is AgeRangeService.requiredRegulatoryFeatures, new in 26.4, and the flow it gates costs iOS 26.5 to write in full.736
  • Apple refuses to define a significant change and says so four times, routing you to counsel. The single concrete example it publishes, it attributes to the statute: Apple writes that Texas law counts a change to your app’s age rating.48
  • Your rating can change without a build from you. Apple retired Australia’s 15+ tier and gave Vietnam a new four-tier scheme on June 18, 2026, both without asking any developer to submit anything.937
  • Revocation is the branch nobody builds, and Apple’s docs barely build it either: RESCIND_CONSENT appears in none of the lifecycle tables on the page a server team reads.2

Grant, re-grant, revoke. Apple documents the first two paths with a sample project and a sandbox test matrix. The third gets one enum value and four fields, which turned out to be roughly the attention it gets in my own code too.

The calendar Apple published four times

Every date below comes from Apple’s own developer news, and the sequence matters more than any single entry.

Apple announced Texas SB2420 on October 8, 2025, dated “Beginning January 1, 2026,” and described its own response rather than the statute’s text: new Apple Accounts for users under 18 would 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.”13 On November 4 Apple named the tools and shipped them in the 26.2 betas.4 On December 23 the plan stopped: “A recent injunction issued by a district court suspended enforcement of Texas state law SB2420 … Apple will pause previously announced implementation plans.”14 On June 3, 2026 it restarted: “Due to a recent court ruling lifting an injunction on Texas law SB 2420 … These changes will go into effect starting June 4, 2026.”5

Eight months of whiplash on one statute, and the APIs never moved once. They shipped in 26.2, stayed available for sandbox testing throughout the injunction, and were waiting when it lifted.14 Anyone who read the December pause as permission to defer lost five months of runway.

The rest of the map arrived on February 24, 2026, and reaches past Texas.

Jurisdiction Date Apple states applies What Apple states applies
Australia, Brazil, Singapore February 24, 2026 Apple blocks downloads of apps rated 18+ “unless they have been confirmed to be adults through reasonable methods”15
Utah May 6, 2026 Age categories shared for new Apple Accounts on request15
Texas June 4, 2026 New Apple Accounts “are now subject to the law”: consent for downloads, In-App Purchases, and significant changes on behalf of minors under 18, and guardians can revoke5
Louisiana July 1, 2026 Age categories shared for new Apple Accounts on request15

Texas is the outlier on threshold, not on tooling. Apple describes Texas as consent “on behalf of minors under the age of 18” and publishes its categories as “under 13, 13-15, 16-17, or over 18,” which is exactly what the API produces: “You can specify up to three age gates, which create up to four possible age ranges.”4529 For Utah and Louisiana Apple names age-category sharing on request, then states that all four tools “have been expanded to help developers meet compliance obligations for Louisiana and Utah,” naming the Significant Change API among them.15 The tooling is not Texas-only. Whether the obligations are, Apple declines to say: it scopes the duty to “certain regions, where legally required” and sends the question to counsel instead.8

Do not read the table as a statement of law or as a compliance determination. It records what Apple announced, and the date Apple attached to it. I am an engineer, not a lawyer, and everything below stops at the API boundary.

The Q&A that routes compliance questions to counsel carries better news for release planning: asked whether any of this changes review, Apple answers “No, there are no changes to the App Review process.”8 The obligations land at runtime in named jurisdictions, not at submission.

So the split is clean. Whether your app owes consent in Texas, Utah, or Louisiana is a question for a lawyer licensed there. Which SDK to build against is not: Apple states that “you must build your app against the iOS 26.2 and iPadOS 26.2 SDKs, or later, with Xcode 26.2 (17C52) or later” to reach the frameworks at all, and that existing accounts on iOS 18 or earlier “won’t be affected.”8

Apple will not tell you what “significant” means

The definitional hole sits at the center of the feature, and four Apple surfaces hand it back rather than fill it: the symbol page (“You determine what constitutes a significant update based on applicable regulations”),12 both news posts (“it’s the developer’s responsibility to determine when there’s a significant change to their app”),45 and the Q&A, asked directly whether a terms or privacy change counts (“It depends. You determine what constitutes a significant app update based on applicable laws”).8

Apple supplies exactly one worked example, and it comes from the statute rather than from Apple: “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.”4

What Apple does define is the string you write. SignificantAppUpdateTopic carries a good and a bad example side by side, which is unusual candor for a symbol page:

// 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."
)

Apple’s instruction around that listing is blunt: “Use concise, understandable language that clearly explains what changed in your app. Parents and guardians see this description when deciding whether to grant permission.”12 Release-notes boilerplate now goes in front of a parent deciding whether their child keeps access, which makes it the highest-stakes changelog string most apps will ever ship.

The obligation attached to the answer is not vague at all, though Apple hedges its trigger. Apple makes you “responsible for preventing access to your app or features when required,” then states flatly that “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.”8 The words “all app and account data” are doing a lot of work there. Apple leaves the blast radius to you and names the whole account as the upper bound.

The state machine, and where Apple’s own sample leaks

Apple published a sample project, “Implementing age assurance and permissions,” which is the only end-to-end description of the flow.6 Reading it as a state machine surfaces four branches and one listing worth rewriting.

The first branch is the cheap one. AgeRangeService.requiredRegulatoryFeatures returns Set<AgeRangeService.RegulatoryFeature>, with three members: declaredAgeRangeRequired, significantAppChangeRequiresParentalConsent, and significantAppChangeRequiresAdultNotification.7 Apple’s sample checks it first, and “When neither feature is present, the app skips the flow entirely.”6 Apple frames the property as reflecting a person’s “region and account settings,” which reads to me as a promise that users outside the named jurisdictions come back with nothing, though Apple guarantees nothing of the kind.7

The remaining branches split on age and on how the age was established:

Person Regulatory feature required Apple’s sample does
Minor significantAppChangeRequiresParentalConsent Sends a PermissionQuestion to the guardian6
Adult, confirmed method significantAppChangeRequiresAdultNotification Presents the system acknowledgment sheet6
Adult, no confirmed method either Blocks access “until the person verifies their account in Settings”6
Declined sharing either Parses the case, then shows no branch for it6

The third row is the one to sit with. An adult who never attached a payment method to their Apple Account gets blocked by Apple’s reference implementation, in a flow built to protect children. Apple offers no alternative branch and no override.

Composed against the published declarations, the routing looks like this. The adult and minor split follows Apple’s sample plus the sandbox test matrix, where an 18-plus account returns a lowerBound of 18 with no upper bound and an ageRangeDeclaration of confirmed or selfDeclared. Note the availability floor: reading .confirmed costs you iOS 26.5, a release later than everything else in the flow.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))
        }
    }
}

Sending the question is the easy half. Receiving the answer is where Apple’s sample leaks, and the listing is short enough to read closely:6

for await response in AskCenter.shared.responses(for: SignificantAppUpdateTopic.self) {
    guard response.choice.answer == .approval else {
        return
    }
    versionManager.handleAllChanges()
}

A return inside for await abandons the sequence. One denial and the app stops observing that topic until the next launch, so a guardian who taps Decline and reconsiders a minute later sends an approval into a stream nobody is reading. Write continue and record the denial instead. Reading the listing as a defect rather than as intent is my inference; the fix costs one keyword either way. Design the listener as though background launch does not happen: only the deprecated sequence it replaced ever promised it.2021

Pending is a state, and it has no timeout you control

The middle of the lifecycle is where the design work actually lives, and five documented behaviors shape it. Start with the one Apple half-answers: PermissionQuestion.expirationDate is an Optional<Date> after which “the person that receives the question can no longer respond,” and Apple publishes no default for it.16

A child can cancel before the guardian ever sees the question: “At any point during the send request flow, the child has the option to cancel the request … In this scenario, the system doesn’t deliver a response to the calling app for that specific question.”17 No response, no error, no callback. Your pending state stays pending forever unless you time it out yourself.

Asking an adult throws. The sandbox article documents a case the symbol page leaves blank: “Calling AskCenter.ask(_:) for an adult user throws this error because they don’t meet the requirements for parental permission requests.”11 AskError.notAvailable is one of two cases in the enum carrying no abstract on its own page, and it is the one an adult triggers.18 Route by age before you ask rather than by catching the throw.

Approval state belongs in iCloud rather than the app container. Apple’s sample writes each acknowledged change into NSUbiquitousKeyValueStore and observes didChangeExternallyNotification “so other devices don’t present the same flow again.”6 A parent approving on the iPhone should not produce a second request on the iPad, and PermissionKit does none of that for you.

The best detail in the sample solves a problem most teams would ship wrong. New installs should not consent to a change they never experienced, so Apple reads AppTransaction.originalAppVersion and “automatically marks any changes the app introduced at or before that version as handled.”619 Consent tracking is therefore per change and per person: change identifiers with introduction versions, not a Boolean.

Your rating can change without a build

AppStore.ageRatingCode is a static var returning Int? asynchronously, available from 26.2 across iOS, iPadOS, macOS, tvOS, visionOS, and watchOS.10 Apple’s stated use is comparison rather than interpretation: “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.”10 Comparison is all you get, because Apple publishes no mapping from integer to rating tier anywhere in the documentation. You cannot ask whether you are 13+, only whether you are what you were last time, so the real contract is that you persist the previous value and own the first run.

Why an app would watch its own rating becomes obvious once you notice that ratings move without releases. On May 21, 2026 Apple announced that starting June 18, “The 15+ age rating will no longer be available on the App Store in Australia,” and gave Vietnam a region-specific four-tier scheme derived from existing questionnaire answers.9 Both landed: App Store Connect Help’s Australia table now publishes 16+ and R 18+ with no 15+ row, and Vietnam has a table of its own.37 Neither change asked for a submission, a build, or any developer action, and Apple writes that Texas law counts a rating change as a significant change.4

Contrast a rating change you initiate: “When a developer updates their app’s age rating, the rating is updated on all user devices once the version is live.”4 Your own changes ride a release you can instrument. Apple’s do not, which is the gap the property fills.

The testing problem is worse than a gap. An Apple Staff reply on the Developer Forums states it directly: “A value of 0 is expected during development of your app when it is built and run from Xcode and Sandbox environments (including TestFlight).”22 Zero is not nil, so Apple’s own documented example passes its guard let and hands back 0 as a valid rating, which is exactly what the developer who filed that thread saw on a physical device.1022

Follow that through. Persist 0 as your baseline and the first App Store launch reads a real code, sees a change, and asks a parent to re-consent to nothing. Treating 0 as a sentinel you never write to the baseline is my inference from Apple’s two statements, and it is the one line of defensive code I would not ship without.

Apple’s availability metadata carries a further finding no prose page states. The platform rows across these four capabilities put holes in different places:

Capability iOS / iPadOS macOS Mac Catalyst visionOS tvOS / watchOS
AppStore.ageRatingCode10 26.2 26.2 no row 26.2 26.2
requiredRegulatoryFeatures7 26.4 26.4 26.4 no row no row
SignificantAppUpdateTopic, PermissionButton1226 26.2 26.2 26.2 26.2 no row
showSignificantUpdateAcknowledgment27 26.4 no row 26.4 no row no row

Two asymmetries fall out. A native macOS app can learn that significantAppChangeRequiresAdultNotification applies to a person and has no API to satisfy it: showSignificantUpdateAcknowledgment(in:updateDescription:) takes a UIWindowScene and publishes no macOS row, and the SwiftUI SignificantUpdateAction stops at the same three platforms.27 Apple did supply an NSWindow variant for the neighboring requestAgeRange call, so the omission reads as a gap rather than a policy, which is my inference.28

Second, detection reaches further than remediation. ageRatingCode publishes tvOS and watchOS rows; nothing that asks for consent does.10 Those targets can observe a rating change with no documented way to respond, and Return ships both: its tvOS 1.0.1 sits at READY_FOR_DISTRIBUTION in App Store Connect, and the distributed iOS app embeds a watch app.30 Both readings assume Apple’s metadata is complete, which the same metadata undercuts: ageRatingCode publishes no Mac Catalyst row while every neighboring symbol does.10

What a server is for once Apple blocks the launch

Apple states the revocation behavior in one sentence: “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.”8

Read the order of those clauses. The system already blocks access, so RESCIND_CONSENT is not an enforcement hook and building it as one wastes it. It is the only signal you get about a user whose device your code can no longer run on. Apple says nothing about how to file that event, so the closest analogue I have found is an account deletion request: subscriptions to reconcile, state to freeze, and a household that may reappear.

The payload is unusually thin. appData carries four fields: appAppleId, bundleId, environment, and signedAppTransactionInfo.3 No transaction, no subscription, no renewal info, and no account identifier of yours. The link to a person runs through the signed app transaction, whose appTransactionID the App Store generates “for each Apple Account that downloads your app and for each family group member for apps that support Family Sharing.”19 So a free app does have a durable per-account key to match against, and an app that never stored one gets a notification it cannot attribute.

The transport carries no surprises: a version 2 URL per environment in App Store Connect over TLS 1.2 or later, 17.0.0.0/8 on the allow list, 200 through 206 for success, and 40x or 50x to buy five retries at 1, 12, 24, 48, and 72 hours in production only.2324

RESCIND_CONSENT also carries no subtype. Each of the 19 published subtype values scopes itself to a named notification type, and the string RESCIND_CONSENT appears nowhere on that page.25 More telling, Apple’s notificationType page maps 40 events across eight tables under “Handle use cases for In-App Purchase life-cycle events,” and RESCIND_CONSENT appears in none of them; the value exists in the possible-values list and nowhere else on the page.2 Apple documented the notification in the age-assurance material and never wired it into the reference a server team actually reads.

What my own projects already get wrong

I searched my own code before writing about anyone else’s. The selection rule was mechanical, and the rule is where the bug was: every row of the Active Projects table in my own CLAUDE.md that has an Xcode project behind it, which comes to seven projects and 508 files covering every Swift file, entitlements file, property list, and project.pbxproj.31 Zero matches for PermissionKit, AskCenter, SignificantAppUpdateTopic, FamilyControls, ManagedSettings, DeviceActivity, CKShare, sharedCloudDatabase, or ageRatingCode. Five of the seven carry an App Store Connect record. Water and Yawara carry none and have never shipped, so read those two as code rather than as apps in anyone’s hands.31 Ace Citizenship looked like the likeliest home for minor users and is the least likely: its companion site states N-400 eligibility as “Age 18 or older,” and the app asks for no birthdate anywhere.31

Then I ran the identical patterns across every repository under ~/Projects, which is the scan I should have run first. The same patterns match 21 files, seven of them code, and six of those seven sit in one iOS project the registry never gained a row for.32

Randori is a jiu-jitsu training log, and it holds exactly the consent surface the pattern list exists to catch: 20 matches for CKShare and sharedCloudDatabase across ConnectionStore.swift, RandoriApp.swift, CKPostTransport.swift, ProfileCardView.swift, CloudShareSheet.swift, and SocialContracts.swift, where acceptance runs through userDidAcceptCloudKitShareWith when the app is already running and through connectionOptions.cloudKitShareMetadata on cold launch.32 Randori also ships its own consent primitive for wearing someone else’s name: TagConsentStore fails closed, so an athlete whose client has not published a tagging policy cannot be tagged at all.32

So the one project with a real consent surface wrote its own ledger and adopted none of Apple’s. It owes three things I have not built. Turning on the person-to-person surfaces its contracts keep dormant is the textbook SignificantAppUpdateTopic case. ageRatingCode has no stored baseline, and an unreleased 1.0 has only ever run from Xcode, sandbox, or TestFlight, which is precisely where Apple says the property returns 0.22 Revocation has nowhere to land: Randori sells nothing and runs no server of its own.32

The more interesting finding is that guardian consent already ships in three of the original seven under an older name.

Ask to Buy is the same mechanism with the same shape, and Apple describes it in nearly the same words: “when a child wants to make an eligible purchase or download, the system sends the purchase request to the parent or guardian.”34 It surfaces as Product.PurchaseResult.pending, and an approval arrives through Transaction.updates rather than at the call site, because that sequence carries “transactions that occur outside of the app, such as Ask to Buy transactions.”35 A decline delivers nothing: “Your app doesn’t receive a transaction because you declined Ask to Buy.”34

Three of the seven sell something, and each handles the pending state differently:33

App Product .pending handling
ResumeGeni Monthly subscription A named .pending outcome with a documented reconcile path
Reps Subscription, two tiers case .userCancelled, .pending: collapsed into one branch
Ace Citizenship Non-consumable A separate case .pending: returning false, identical to cancel

Two of three report a guardian deliberating as a user declining, and what the user sees is worse than a wrong label. Reps dismisses its paywall only when purchase returns true; Ace advances past its paywall only when its own call returns success. On .pending neither call returns anything actionable, so both paywalls stay open with no error, no toast, and no spinner: a dead tap.33 The parent approves minutes later, and the transaction lands in a listener whose interface never acknowledged that a request happened.

The collapsed branch is exactly the failure PermissionKit invites at higher stakes, and I shipped it twice before Apple gave the pattern a second API. Pending is not an exotic branch. Pending is what a consent flow looks like from inside an app, and the right default is a state you render rather than a value you return.

One app is already downstream of the server half, which makes the missing branch concrete. ResumeGeni’s version 2 endpoint has recorded at least 56 notifications since June 26, 2026, 55 of them from sandbox and one from production, which is how I know both environment URLs are registered rather than only one. Its handler names 13 notification types and dispatches eight; the remaining five appear only in comments. RESCIND_CONSENT is in neither group, and nowhere else in the repository either.33

FAQ

Different frameworks, and the split is easy to get backwards. Parental consent runs through PermissionKit: a SignificantAppUpdateTopic wrapped in PermissionQuestion(significantAppUpdateTopic:), sent with PermissionButton, answered through AskCenter.shared.responses(for:).12162126 Adult acknowledgment runs through Declared Age Range instead, as showSignificantUpdateAcknowledgment(in:updateDescription:).27 Check requiredRegulatoryFeatures first, because asking an adult through PermissionKit throws AskError.notAvailable.711

Enable Developer Mode, then open Settings, Developer, Sandbox Apple Account, sign in, select the account, tap Manage, and choose Revoke App Consent. Enter your bundle identifier and tap Revoke Consent; the system shows “Notification Triggered.”11 With a version 2 URL configured, your server receives RESCIND_CONSENT carrying an appData object whose bundleId and environment fields confirm the notification matched the right app.311 Sandbox sends each notification once with no retries, so an endpoint returning a 50x mid-test gets no second chance.24

Why would an app watch its own age rating at runtime?

Because Apple changes ratings on the storefront without any submission from you, and Apple writes that Texas law counts a rating change as a significant change, then points you at the Significant Change API to request parental consent.4 June 18, 2026 is the worked example: Australia lost its 15+ tier and Vietnam gained a four-tier scheme, both applied to existing apps from existing questionnaire answers.937 Apple publishes no mapping from the property’s integer to a rating tier, so comparison against a value you stored is the only operation it supports.10

The system already does, which is the point most implementations miss. Apple states that when a guardian revokes consent, “Apple will prevent the app from launching,” then directs you to the notification to handle the event rather than to enforce it.8 So the work left is server-shaped: freeze account state, reconcile any subscription, and stop pushing to a device that cannot open your app. Treat it the way you treat an account deletion, not the way you treat a failed authorization check.

Key Takeaways

For iOS developers: - Audit your existing Product.PurchaseResult switch today. Ask to Buy is guardian consent that already ships, .pending is how it surfaces, and collapsing that case into .userCancelled is the defect PermissionKit will invite at higher stakes.3435 - Budget iOS 26.5, not 26.4, if your flow distinguishes a confirmed adult from a self-declared one.36 - Fix the return in Apple’s sample listener before you copy it, and never let 0 reach your stored ageRatingCode baseline.622

For teams shipping to multiple Apple platforms: - Check the availability rows before planning the work. A native macOS app can detect that adult acknowledgment applies and has no API to present it; tvOS and watchOS can read ageRatingCode with no consent API behind it.71027 - Store acknowledgment in NSUbiquitousKeyValueStore keyed per change, and use AppTransaction.originalAppVersion to exempt new installs from changes that predate them.619

For backend and release owners: - Record appTransactionID per account before you need it, then stand up the version 2 endpoint even for a free app. An endpoint built after the fact receives revocation events it cannot attribute to anyone.319 - Route the significant-change description past whoever owns your product copy. It is the only string a guardian reads while deciding whether your app keeps a user.12


Three of the four enforcement points in this cycle fire on something you control: the launch screen key on an SDK, the @State macro on a toolchain, and the social media declaration on a submission. Guardian consent fires on a court docket and a storefront rating table. The full series hub is the Apple Ecosystem Series.

References


  1. Apple, App Store Server Notifications changelog. Under the November 4, 2025 heading, New features: “Updated the responseBodyV2DecodedPayload to include the new payload object, appData” and “Added the notification type RESCIND_CONSENT to notificationType.” The next two entries in the changelog are December 10, 2025 and April 27, 2026, neither touching consent. Read from Apple’s documentation JSON on July 26, 2026, since the HTML page renders through JavaScript. 

  2. Apple, notificationType, App Store Server Notifications. Source of the RESCIND_CONSENT definition (“A notification type that indicates the parent or guardian has withdrawn consent for a child’s app usage”) and of the count used here: the page publishes 23 possible values (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). The string RESCIND_CONSENT occurs exactly once in the page payload, inside the possible-values list; the eight tables under “Handle use cases for In-App Purchase life-cycle events” carry 40 event rows between them (4, 6, 7, 7, 8, 6, 6, and 4 rows including each header) and none of them names it. Note that REVOKE is a Family Sharing entitlement loss, not a consent withdrawal: “an In-App Purchase the customer was entitled to through Family Sharing is no longer available through sharing.” Read from Apple’s documentation JSON on July 26, 2026. 

  3. Apple, appData, App Store Server Notifications, introduced at version 2.19. Source of “The appData object is part of the responseBodyV2DecodedPayload. This object is present in the payload when the notificationType is RESCIND_CONSENT” and of the four properties: appAppleId (“available for apps that users download from the App Store. It isn’t present in the sandbox environment”), bundleId, environment, and signedAppTransactionInfo (a JWSAppTransaction). The responseBodyV2DecodedPayload page states the exclusivity independently, describing appData as a field that “appears when the notificationType is RESCIND_CONSENT” and adding that “The data, appData, summary, and externalPurchaseToken fields are mutually exclusive. The payload contains only one of these fields.” Those two pages are the basis for calling RESCIND_CONSENT the only notification type whose payload carries appData; the string appData appears nowhere on the notificationType page. 

  4. Apple, Next steps for apps distributed in Texas, Apple Developer News, November 4, 2025. Source of the Texas age categories (“under 13, 13-15, 16-17, or over 18”), the framework name Apple uses (“the Significant Change API under the PermissionKit framework”), the developer-responsibility sentence (“It’s the developer’s responsibility to determine when there’s a significant change to their app”), the age rating example (“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”), the StoreKit framing (“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”), the revocation behavior (“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”), and the four-item “Next steps” implementation list. Also the source for dating the beta availability to iOS 26.2 and iPadOS 26.2. Fetched July 26, 2026. 

  5. Apple, Update for Apps Distributed in Texas, Apple Developer News, June 3, 2026. Source of the lifted injunction (“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”), the scope (“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”), the effective date (“These changes will go into effect starting June 4, 2026”), the repeated developer-responsibility reminder, and the same four-item implementation list. Fetched July 26, 2026. 

  6. Apple, Implementing age assurance and permissions, Declared Age Range sample code. Availability: iOS 26.5, iPadOS 26.5, Mac Catalyst 26.5, Xcode 27.0 beta; the article instructs signing in to iCloud “on a device with iOS 26.4 or later” before running. Source of the empty-set behavior (“When neither feature is present, the app skips the flow entirely”), the age gate of 18, the four parsed categories (.minor, .verifiedAdult described as “an adult with a confirmed payment method”, .unverifiedAdult as “an adult without account verification”, .declinedSharing), the unverified-adult outcome (“the app sets the phase to .blocked and prevents access until the person verifies their account in Settings”), the minor path building a SignificantAppUpdateTopic and a PermissionQuestion, the PermissionButton send, the AskCenter.shared.responses(for:) listener listing quoted verbatim here, the denial behavior (“When the parent denies the request, the app prevents the minor from using it”), the NSUbiquitousKeyValueStore acknowledgment tracking with didChangeExternallyNotification (“so other devices don’t present the same flow again”), and the originalAppVersion exemption (“People who install the app when a significant change is already present don’t need to acknowledge it”). The project also requires the Declared Age Range capability and the iCloud key-value storage service. Read from Apple’s documentation JSON on July 26, 2026. 

  7. Apple, AgeRangeService.requiredRegulatoryFeatures and AgeRangeService.RegulatoryFeature, Declared Age Range. Both available from iOS 26.4, iPadOS 26.4, Mac Catalyst 26.4, and macOS 26.4, with no visionOS, tvOS, or watchOS row. Declaration: var requiredRegulatoryFeatures: Set<AgeRangeService.RegulatoryFeature> { get async throws }, throwing notAvailable “if the regulatory feature’s service is unavailable.” The enum publishes exactly three cases: 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”), and 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. Source of the SDK floor (“you must build your app against the iOS 26.2 and iPadOS 26.2 SDKs, or later, with Xcode 26.2 (17C52) or later”), the legacy-account carve-out (“Existing Apple Accounts running iOS 18 and iPadOS 18, or earlier … won’t be affected,” a sentence whose omitted clause specifies “including adult and child accounts for kids and teens”), the responsibility answer (“Yes, developers are responsible for their own age restrictions” and “For questions about your compliance obligations, consult your legal counsel”), the regional scoping quoted twice in this article (“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,” restated later as “In regions where legally required, you need to check the age of the people using your app with the Declared Age Range API”), the access obligation (“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”), the revocation behavior (“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”), the App Review answer (“No, there are no changes to the App Review process”), and the terms-and-privacy answer (“It depends. You determine what constitutes a significant app update based on applicable laws”). Read July 26, 2026. 

  9. Apple, Upcoming changes to age ratings in Australia and Vietnam, Apple Developer News, May 21, 2026. Source of “Starting June 18, 2026, age ratings on the App Store will be updated in Australia and Vietnam,” the Australian change (“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+”) and its three descriptors (unrestricted web access; frequent medical or treatment information; loot boxes), and the Vietnamese change (“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+)”). Neither change asks the developer to submit a build. Fetched July 26, 2026. 

  10. Apple, AppStore.ageRatingCode, StoreKit. Declaration static var ageRatingCode: Int? { get async }, available from iOS 26.2, iPadOS 26.2, macOS 26.2, tvOS 26.2, visionOS 26.2, and watchOS 26.2, with no Mac Catalyst row. Return value: “An integer representing the current age rating code, or nil if the age rating is unavailable.” Source of the comparison framing (“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”) and of the chain to PermissionKit (“If your app’s age rating has changed, consider informing parents or guardians by using the Significant Change API”), a sentence in which “Significant Change API” is the link text and Apple’s SignificantAppUpdateTopic page is the link target. Apple’s own example on the page is a guard let around the property that prints when the value is unavailable. Searched Apple’s documentation for a mapping from these integers to rating tiers (4+, 9+, 13+, 16+, 18+) on July 26, 2026 and found none on this page, on the AppStore type page, or in App Store Connect Help’s age ratings reference. 

  11. Apple, Testing age assurance in sandbox, StoreKit. Source of the on-device path (Settings, Developer, Sandbox Apple Account, Manage, then “Age Assurance or Revoke App Consent”), the six-row test matrix whose 18-plus rows return a lower bound of 18 with no upper bound and an age declaration of selfDeclared or confirmed, the adult-ask behavior (“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”), the revocation steps ending in the confirmation “Notification Triggered” with “A notification will be sent to the developer server soon,” and the payload note (“your server receives a RESCIND_CONSENT notificationType. The notification payload includes an appData object with app metadata, including the bundleId and environment fields”). The three minor rows in the matrix are under 13 approved, 13 to 15 approved, and 16 to 17 declined, all with an age declaration of guardianDeclared

  12. Apple, SignificantAppUpdateTopic, PermissionKit. Available from iOS 26.2, iPadOS 26.2, Mac Catalyst 26.2, macOS 26.2, and visionOS 26.2; declared struct SignificantAppUpdateTopic, conforming to QuestionTopic, with init(description: String). Source of the definitional deferral (“You determine what constitutes a significant update based on applicable regulations”), the description guidance (“Use concise, understandable language that clearly explains what changed in your app. Parents and guardians see this description when deciding whether to grant permission”), and both code comments in the Specific/Vague listing reproduced verbatim in this article. 

  13. Apple, New requirements for apps available in Texas, Apple Developer News, October 8, 2025. Source of the original announcement (“Beginning January 1, 2026, a new state law in Texas … introduces age assurance requirements for app marketplaces and developers,” the elided text naming SB2420), the Family Sharing requirement (“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”), and the advance notice of Utah and Louisiana (“Similar requirements will come into effect later next year”). Fetched July 26, 2026. 

  14. Apple, Update on age requirements for apps distributed in Texas, Apple Developer News, December 23, 2025. Source of the injunction (“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”), the continued sandbox availability of all four tools, and their extension to Utah and Louisiana. Fetched July 26, 2026. 

  15. Apple, Age requirements for apps distributed in Brazil, Australia, Singapore, Utah, and Louisiana, Apple Developer News, February 24, 2026. Source of the 18-plus download restriction (“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”), the Utah and Louisiana dates (“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”), the expansion sentence quoted in this article and the four links that follow it (“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 under PermissionKit, new age rating property type in StoreKit, App Store Server Notifications), the Brazil loot box consequence, and the first public naming of the 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”). Fetched July 26, 2026. 

  16. Apple, PermissionQuestion and expirationDate, PermissionKit. final class PermissionQuestion<Topic> where Topic : QuestionTopic, available from iOS 26.0 with four initializers: init(handle:), init(handles:), init(communicationTopic:), and init(significantAppUpdateTopic:), the last introduced at iOS 26.2 and described as creating “a permission question that asks parents or guardians for permission to continue using your app after a significant update.” expirationDate is declared final var expirationDate: Date? with the discussion “Once the date passes, the person that receives the question can no longer respond.” Apple publishes no default value for the property and no setter guidance for the significant-update topic. 

  17. Apple, Creating a communication experience, PermissionKit. Source of the cancellation behavior quoted here: “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.” Also the source of the framework’s iMessage constraint, which the PermissionKit landing page states as an Important aside: “Communication experiences using the PermissionKit framework are only available using iMessage.” Note that the article documents only the CommunicationTopic flow; Apple publishes no equivalent article for the significant-update flow, and the article’s code samples call CommunicationLimits.current.permissionResponses, a symbol that returns 404 in Apple’s documentation as of July 26, 2026. 

  18. Apple, AskError, PermissionKit. enum AskError, conforming to LocalizedError, with six cases. Four carry abstracts and arrive at iOS 26.1: unknown, communicationLimitsNotEnabled (“Indicates communication limits isn’t enabled to send permission requests”), contactSyncNotSetup, and invalidQuestion. Two publish no abstract on their own pages: systemError(underlyingError:) at iOS 26.1, and notAvailable at iOS 26.2, arriving alongside SignificantAppUpdateTopic. The meaning of notAvailable appears only in the sandbox testing article cited at note 11; systemError at least names its cause in its signature. Whether communicationLimitsNotEnabled or contactSyncNotSetup can also surface on a significant-update ask is unstated. 

  19. Apple, appTransactionID and originalAppVersion on AppTransaction, StoreKit. Source of the identifier semantics (“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”), its stability across redownload, refund, repurchase, and storefront change, its presence in App Store Server Notifications version 2 payloads, and the crucial line for free apps: “The appTransactionID is available even if a customer makes no in-app purchases.” originalAppVersion is “The app version that the customer originally purchased from the App Store,” carrying CFBundleShortVersionString on macOS and CFBundleVersion elsewhere, and always 1.0 in the sandbox environment. The corresponding server-side type is appTransactionId in the App Store Server API, introduced at version 1.15. 

  20. Apple, CommunicationLimits and updates, PermissionKit. updates is declared final var updates: some AsyncSequence<PermissionResponse<CommunicationTopic>, Never> { get } and abstracted as “Registers the communication topic with the system, so your app can be launched on-demand in the background to receive permission updates.” Apple’s documentation groups updates and both CommunicationLimits.ask(_:in:) overloads under a Deprecated APIs heading; the class itself remains current for isKnownHandle(_:) and knownHandles(in:). The replacement sequence drops the promise: neither the abstract nor the discussion of AskCenter.responses(for:) mentions background launch at all, which is the whole of the documentation gap the body flags. 

  21. Apple, AskCenter and responses(for:), PermissionKit, both available from iOS 26.2, iPadOS 26.2, Mac Catalyst 26.2, macOS 26.2, and visionOS 26.2. AskCenter is a final class reached through static let shared, described as routing “your questions through the appropriate family sharing channels” and delivering “responses back to your app when parents make their decisions.” responses(for:) is declared final func responses<Topic>(for topicType: Topic.Type) -> some AsyncSequence<PermissionResponse<Topic>, Never> where Topic : QuestionTopic and abstracted as “Registers the topic type with the system and returns an asynchronous sequence of responses,” with no mention of background launch. Four ask(_:in:) overloads exist: two taking UIViewController on iOS, iPadOS, and visionOS, and two taking NSWindow on macOS, one of each per topic type. PermissionResponse exposes choice and question; PermissionChoice.Answer publishes exactly two cases, approval and denial

  22. Apple Staff reply on AppStore.ageRatingCode always returns 0 on real device, Apple Developer Forums. Original post from April 2026 reporting 0 on a physical device running iOS 26.4 with a sandbox account signed in and the age rating configured in App Store Connect. The reply, from an author labeled Apple Staff, states: “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,” and “A value of 0 is expected during development of your app when it is built and run from Xcode and Sandbox environments (including TestFlight).” Fetched twice on July 26, 2026 with identical wording; the forum renders through JavaScript and displays the reply’s age as the relative timestamp “1w” rather than a date, so no publication date is reported here. A developer forum reply is weaker evidence than a documentation page, and Apple’s documentation for ageRatingCode does not mention the 0 value at all. The consequence drawn in this article, that persisting 0 as a baseline manufactures a false change on the first App Store build, is my inference from that reply and from Apple’s documented comparison pattern. 

  23. Apple, Enabling App Store Server Notifications. Source of the TLS floor (“your server must support the Transport Layer Security (TLS) 1.2 protocol or later”), the per-environment URL configuration in App Store Connect, the port constraint (443, or 1024 and above), and the allow-list subnet (“add the IP address subnet 17.0.0.0/8,” which “applies to both the sandbox and the production environments”). The companion Receiving App Store Server Notifications article describes the JWS-signed signedPayload and, as of July 26, 2026, mentions only the data object without referencing appData

  24. Apple, Responding to App Store Server Notifications. Source of the success codes (“Send HTTP 200, or any HTTP code between 200 and 206”), the retry trigger (“Send HTTP 50x or 40x to have the App Store retry the notification”), the version 2 retry schedule (“it retries five times, at 1, 12, 24, 48, and 72 hours after the previous attempt”), the sandbox limitation (“Retry notifications are available only in the production environment. In the sandbox environment, the App Store server attempts to send the notification one time”), and the recovery path through Get-Notification-History

  25. Apple, subtype, App Store Server Notifications. The page publishes 19 possible values (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), each scoped to a named notification type. The string RESCIND_CONSENT does not appear anywhere in the page payload, read July 26, 2026. 

  26. Apple, PermissionButton, PermissionKit. @MainActor @preconcurrency struct PermissionButton<Topic, Label> where Topic : QuestionTopic, Label : View, available from iOS 26.2, iPadOS 26.2, Mac Catalyst 26.2, macOS 26.2, and visionOS 26.2, with two init(question:label:) overloads constrained to CommunicationTopic and SignificantAppUpdateTopic respectively. It supersedes CommunicationLimitsButton, which Apple lists under Deprecated APIs on the framework page. 

  27. Apple, AgeRangeService.showSignificantUpdateAcknowledgment(in:updateDescription:), SignificantUpdateAction, and the SwiftUI environment value showSignificantUpdateAcknowledgment. The method is declared @MainActor func showSignificantUpdateAcknowledgment(in windowScene: UIWindowScene, updateDescription: String) async throws and publishes iOS 26.4, iPadOS 26.4, and Mac Catalyst 26.4, with no macOS row; AgeRangeService lists only this one overload under “Displaying update acknowledgments.” SignificantUpdateAction and the environment value publish the same three platforms. Apple’s Important aside on the method: “Before calling this function, check RegulatoryFeature to determine if a person must acknowledge your significant app change.” The environment value’s discussion adds that you “Call this action from a Button or onAppear(perform:).” 

  28. Apple, requestAgeRange(ageGates:::in:), Declared Age Range. Apple publishes two overloads: one taking in viewController: UIViewController on iOS 26.0, iPadOS 26.0, and Mac Catalyst 26.0, and one taking in window: NSWindow on macOS 26.0. The existence of an NSWindow variant for the age-range request, and its absence for the acknowledgment sheet cited at note 27, is the basis for reading the macOS omission as a gap rather than a policy decision; Apple has published nothing either way. 

  29. Apple, Requesting people’s age range information in your app, Declared Age Range. Source of the gate arithmetic quoted in the body (“You can specify up to three age gates, which create up to four possible age ranges”), the spacing constraint (“Each range must be at least two years in duration”), and the boundary semantics (“When the lowerBound value is nil, the person is below your lowest age gate” and “When the upperBound is nil, the person meets or exceeds your highest age gate”). Gates at 13, 16, and 18 return exactly the four ranges Apple publishes for Texas, and both bounded ranges clear the two-year minimum: 13 to 15 spans three years and 16 to 17 spans two. The article also warns that the regions a person’s account belongs to “determine the age gates that the system uses to return age ranges, and it may differ from the age gates you specify in your request.” Read from Apple’s documentation JSON on July 26, 2026. 

  30. Author’s survey, July 26, 2026: how the platform claims were established. Platform values read from each project’s SUPPORTED_PLATFORMS build setting rather than from deployment-target keys, which Xcode writes regardless of destination: Reps declares appletvos appletvsimulator iphoneos iphonesimulator macosx xros xrsimulator plus a separate watchos watchsimulator target, and Ace Citizenship declares iphoneos iphonesimulator only. That method under-reports platforms, and Return is the proof: Return’s only SUPPORTED_PLATFORMS value is iphoneos iphonesimulator macosx xros xrsimulator, while its TV and watch targets carry SDKROOT = appletvos and SDKROOT = watchos instead, so a SUPPORTED_PLATFORMS sweep sees neither. Every “ships platform X” claim in this article therefore comes from App Store Connect rather than from a build setting. Return: TV_OS 1.0 and 1.0.1 both READY_FOR_DISTRIBUTION, IOS and MAC_OS 1.0.1 likewise, and the iOS target embeds ReturnWatch Watch App (com.941apps.Return.watchkitapp) through an Embed Watch Content phase, which is how a watch app reaches a wrist. Reps: IOS 1.1 and MAC_OS 1.1 READY_FOR_DISTRIBUTION, no TV_OS version ever at that state, and TV_OS 1.2 sitting at WAITING_FOR_REVIEW since June 2, 2026, so Reps ships iOS and macOS. 

  31. Author’s survey, July 26, 2026, on macOS 26.5.2 (build 25F84) with Xcode 26.6 (build 17F113) and Swift 6.3.3. Selection rule, stated so the scope can be checked and reproduced rather than taken on trust: every row of the Active Projects table in my own agent config (~/.claude/CLAUDE.md) that has an Xcode project behind it, which yields exactly seven: Reps, Return, Banana List (shipping as Get Bananas), Ace-Citizenship, Water, ResumeGeniApp, and Yawara. The rule is also the flaw in the survey, because Randori has no row in that table and Randori turned out to be the project that mattered. Five of the seven have an App Store Connect record (Reps 6776044339, Return 6756242021, Get Bananas 6756241534, Ace Citizenship 6532592671, ResumeGeni 6771154645); Water and Yawara appear nowhere among the account’s 18 apps, so neither has ever been submitted and calling either one an app overstates it. Swift file counts per project: 77, 57, 55, 26, 34, 71, and 143. The consent scan covered *.swift, *.entitlements, *.plist, and project.pbxproj: 85, 65, 63, 34, 38, 74, and 149, which sum to 508. Reproducing those counts needs eight pruned directory names rather than six: build, DerivedData, .build, Pods, .git, worktrees, and, in Reps alone, .venv (142 property lists across Reps/.venv and Reps/server/.venv) and .xcode-state-backups (18). Prune only the first six and Reps reads 245 while the total reads 668, so the exclusion list is load-bearing and every name it depends on is printed here. The 16 patterns, all case-sensitive: PermissionKit, CommunicationLimits, AskPermission, AskCenter, SignificantAppUpdateTopic, SignificantUpdateAction, PermissionTopic, com.apple.developer.family-controls, FamilyControls, ManagedSettings, DeviceActivity, AuthorizationCenter, CKShare, sharedCloudDatabase, publicCloudDatabase, and ageRatingCode. Zero matching files in all seven projects, AskCenter included. Two control patterns run through the identical pipeline returned nonzero (StoreKit matched three files in Reps, CKContainer|NSPersistentCloudKitContainer|SwiftData matched 17 in Banana List), which is how I know the pipeline reads files rather than failing quietly. Ace Citizenship’s onboarding (IntroCarouselView, WelcomeView, AddStateView, AddRepresentativeView) contains no age or birthdate field, and its PrivacyInfo.xcprivacy declares an empty NSPrivacyCollectedDataTypes; the “Age 18 or older” eligibility line comes from ~/Projects/acecitizenship.app/content/blog/n400-application-guide.md

  32. Author’s survey, July 26, 2026: the wider scan and Randori. The wider scan ran the same 16 patterns over every repository under ~/Projects, pruning the same eight names plus node_modules and excluding gitignored paths, and matched 21 files. Seven are code: six Swift files under Randori/Randori/ and _archive/Oishii-AI/Oishii AI/Services/CloudKitManager.swift (publicCloudDatabase, in an archived project). The other 14 are prose or machine state: nine Randori plans and design documents, three posts in this site’s own content/blog/ (one of them the draft of this article), one Obsidian handoff note, and obsidian-signals/40-Projects/blakecrosley-com/wwdc-2026/.pulse_state.json, which a script writes rather than a person. Randori is com.wayofyawara.randori, App Store Connect app 6789693294 with version 1.0 at PREPARE_FOR_SUBMISSION. CKShare and sharedCloudDatabase match 20 lines across six files: ConnectionStore.swift (10), RandoriApp.swift (5), ProfileCardView.swift (2), and one line each in CKPostTransport.swift, CloudShareSheet.swift, and SocialContracts.swift. ConnectionStore describes its own shape in its header comment as “one zone (ProfileCardZone), one record type (ConnectionCard), one share,” holds privateCloudDatabase and sharedCloudDatabase side by side, and implements accept(_ metadata: CKShare.Metadata) at line 355, ensureOutboundShare() at line 547, and per-account block and unblock. RandoriApp.swift implements userDidAcceptCloudKitShareWith on both the app delegate (line 73) and the window scene delegate (line 97, commented “Warm: the app is running when the link is opened”), while RandoriSceneDelegate.scene(_:willConnectTo:options:) reads connectionOptions.cloudKitShareMetadata at line 88 under the comment “Cold start: the invitation rides the connection options,” which is why the body assigns cold launch to the connection options rather than to the acceptance callback. TagConsentStore is the app’s own consent ledger, scoped per iCloud account, and its documented default is fail-closed: a partner whose policy record has not arrived cannot be tagged. Randori/Randori.entitlements requests CloudKit, HealthKit, and aps-environment. Every consent pattern except the two CloudKit sharing ones returns zero across that repository, and so do StoreKit, signedPayload, and notificationType, so the app sells nothing and runs no server of its own; docs/asc-metadata.md prepares a free price and a 4+ age rating. 

  33. Author’s survey, July 26, 2026: StoreKit call sites and server evidence. StoreKit appears in three projects: Reps/Reps/Services/RepsProStore.swift (auto-renewable, two tiers), Ace Citizenship/StoreKitManager.swift (one non-consumable), and ResumeGeni/Subscription/ (one monthly subscription, gated server-side). The .pending handling quoted in the table sits at RepsProStore.swift:134 (case .userCancelled, .pending:), StoreKitManager.swift:69-73 (a separate case .pending: whose own return false is line 73), and SubscriptionStore.swift:209-212 (a named pending outcome). The call sites are what strand the user: RepsProPaywallView.swift:359-361 dismisses only inside if await store.purchase(plan), and ContentView.swift:484-485 unlocks only inside if success, so a false returned for a pending purchase changes nothing on screen in either app. ResumeGeni instead reaches PaywallView.swift:526, a .pending branch that posts the toast “Waiting for approval. You’ll get access once it’s approved.” ResumeGeni’s App Store Server Notifications endpoint is POST /api/appstore/notifications in ~/Projects/resumegeni/app/routers/appstore.py; a POST of {"signedPayload":"probe"} returns HTTP 400 {"status":"invalid"}, which is reachable only past the enable flag and inside Apple’s certificate-chain verifier, and GET returns 405. Both environment URLs are registered, and the evidence for that is delivery rather than a runbook: the 941-analytics D1 database, table funnel_events, holds 56 rows where platform='ios' and the metadata carries notification_type, spanning 2026-06-26T15:48:51Z to 2026-07-25T16:40:11Z, of which 55 carry an environment of sandbox and one carries production (2026-07-21T18:08:21Z). Treat 56 as a lower bound, because the service maps only five event names to funnel rows; the four types actually observed are DID_RENEW (42), SUBSCRIBED (6), EXPIRED (5), and DID_CHANGE_RENEWAL_STATUS (3). app/services/app_store_notification_service.py names 13 notification types, of which eight reach an executable dispatch branch in _funnel_event_name (lines 75 to 89): SUBSCRIBED, OFFER_REDEEMED, DID_RENEW, REFUND, REVOKE, EXPIRED, DID_CHANGE_RENEWAL_STATUS, and DID_FAIL_TO_RENEW. The other five occur only inside comments: PRICE_INCREASE, RENEWAL_EXTENDED, and METADATA_UPDATE on line 73, EXTERNAL_PURCHASE_TOKEN and TEST on line 187. RESCIND_CONSENT, CONSUMPTION_REQUEST, REFUND_DECLINED, and REFUND_REVERSED return zero matches across that entire repository. For completeness on what is not evidence: docs/SUBSCRIPTION_GO_LIVE.md:83 does say “set BOTH the Production and Sandbox URL,” but the line is a runbook instruction under a heading noting the step “needs your re-auth,” so it records an intention rather than a completed registration, and the registration claim above rests on the delivered notifications instead. 

  34. Apple, Testing Ask to Buy in Xcode, StoreKit. Source of Apple’s description of the mechanism (“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”) and of the decline behavior (“Your app doesn’t receive a transaction because you declined Ask to Buy”). The article also documents the Ask to Buy toggle under Purchase Options in the StoreKit configuration editor, and the transaction manager states Pending Ask to Buy, Ask to Buy Approved, and Ask to Buy Declined. 

  35. Apple, Product.PurchaseResult.pending and Transaction.updates, StoreKit, both available from iOS 15.0. Source of the case abstract (“The purchase is pending, and requires action from the customer”), the resolution path (“If a pending purchase succeeds, StoreKit delivers the resulting Transaction in the transaction updates”), and the sequence’s purpose (“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”). The Product.PurchaseResult enum publishes three cases: success(_:), pending, and userCancelled. Apple’s own example on the enum page comments the pending branch as “The purchase requires action from the customer. If the transaction completes, it’s available through Transaction.updates.” 

  36. Every symbol in the routing listing above verified against Apple’s documentation JSON on July 26, 2026. AgeRangeService.shared is static let shared: AgeRangeService (iOS 26.0). The SwiftUI environment values are requestAgeRange, var requestAgeRange: DeclaredAgeRangeAction { get } (iOS 26.0), and showSignificantUpdateAcknowledgment, var showSignificantUpdateAcknowledgment: SignificantUpdateAction { get } (iOS 26.4). The listing’s @available(iOS 26.5, *) floor comes from neither of those: AgeRangeService.AgeRangeDeclaration.confirmed publishes iOS 26.5, iPadOS 26.5, Mac Catalyst 26.5, and macOS 26.5, one release later than the acknowledgment action, so any code that distinguishes a confirmed adult inherits the higher floor. Apple’s own sample project publishes the same 26.5 availability.6 AgeRangeService.AgeRange exposes lowerBound, upperBound, ageRangeDeclaration declared var ageRangeDeclaration: AgeRangeService.AgeRangeDeclaration?, and activeParentalControls. The == .confirmed comparison against that optional is valid because AgeRangeDeclaration conforms to Equatable and Hashable per its relationships section. PermissionButton’s initializer is init(question: PermissionQuestion<Topic>, @ViewBuilder label: @escaping () -> Label), constrained to SignificantAppUpdateTopic in the overload used here.26 ChangedFeatureView and AccountVerificationPrompt are placeholders for your own views, not Apple symbols. 

  37. Apple, Age ratings values and definitions, App Store Connect Help, read July 26, 2026, as the confirming source that the June 18, 2026 changes announced at note 9 took effect. Under “Australia age rating values” the table now publishes two ratings, 16+ and R 18+, with no 15+ row. A separate “Vietnam age rating values” section now exists, introduced as “As required by Article 38 of Vietnam Decree 147,” and its 00+ row is defined as apps that “contain no objectionable material but may contain instances of the following content,” listing parental controls, age assurance, user-generated content, messaging and chat, advertising, and infrequent contests. Apple’s May 21 descriptor list for the Australian migration does not match the current 16+ trigger list on this page, a discrepancy I have not resolved and do not rely on, because the claim drawn here is only that the 15+ tier is gone and the Vietnam table exists. 

Artigos relacionados

The App Store's Social Media Checkbox and What It Costs

September's App Store rule makes every submission declare social media capabilities. The under-13 carve-out costs an ent…

30 min de leitura

canOpenURL Is Deprecated: What to Call Instead

Apple deprecated canOpenURL in three sentences, and halved the scheme allowlist ceiling to 25 entries. The replacement, …

29 min de leitura