Skip to content

feat(metadata): support privacy nutrition labels (data usage)#3596

Draft
EvanBacon wants to merge 1 commit intomainfrom
@bacon/metadata-data-usage
Draft

feat(metadata): support privacy nutrition labels (data usage)#3596
EvanBacon wants to merge 1 commit intomainfrom
@bacon/metadata-data-usage

Conversation

@EvanBacon
Copy link
Copy Markdown
Contributor

Summary

  • Adds a new privacy.dataUsage block to the eas-cli metadata schema (schema/metadata-0.json) so users can declare App Privacy Nutrition Labels alongside the rest of their App Store metadata.
  • Adds DataUsageTask (packages/eas-cli/src/metadata/apple/tasks/data-usage.ts) that pulls existing AppDataUsage rows from App Store Connect and pushes the local config back. The task is declarative: rows in ASC that aren't in the local config are deleted, missing rows are created. After a successful push the AppDataUsagesPublishState envelope is flipped to published: true so the changes take effect on the storefront.
  • Wires the new task into createAppleTasks() and the AppleData context type. Adds getDataUsage() / setDataUsage() to the config reader/writer.

Why this matters

Apple has required every new app submission since 2021 to declare what data the app collects, the purposes it's used for, and how it's linked to the user (Privacy Nutrition Labels / App Privacy details). Until now this was the biggest remaining gap in eas metadata — users had to maintain it by hand in App Store Connect even when everything else was managed declaratively.

Schema shape

{
  "apple": {
    "privacy": {
      "dataUsage": {
        "dataNotCollected": false,
        "categories": [
          {
            "category": "CONTACTS",
            "purposes": ["ANALYTICS", "APP_FUNCTIONALITY"],
            "protections": ["DATA_LINKED_TO_YOU"]
          }
        ]
      }
    }
  }
}

The category, purpose, and protection enums are pulled directly from @expo/apple-utils (AppDataUsageCategoryId, AppDataUsagePurposeId, AppDataUsageDataProtectionId). Each category in the local config is expanded into the cartesian product of (category × purpose × protection) rows on push, matching Apple's flat row model.

Enum reference

  • Categories: ADVERTISING_DATA, AUDIO, BROWSING_HISTORY, COARSE_LOCATION, CONTACTS, CRASH_DATA, CREDIT_AND_FRAUD, CUSTOMER_SUPPORT, DEVICE_ID, EMAIL_ADDRESS, EMAILS_OR_TEXT_MESSAGES, ENVIRONMENTAL_SCANNING, FITNESS, GAMEPLAY_CONTENT, HANDS, HEAD_MOVEMENT, HEALTH, NAME, OTHER_CONTACT_INFO, OTHER_DATA, OTHER_DIAGNOSTIC_DATA, OTHER_FINANCIAL_INFO, OTHER_USAGE_DATA, OTHER_USER_CONTENT, PAYMENT_INFORMATION, PERFORMANCE_DATA, PHONE_NUMBER, PHOTOS_OR_VIDEOS, PHYSICAL_ADDRESS, PRECISE_LOCATION, PRODUCT_INTERACTION, PURCHASE_HISTORY, SEARCH_HISTORY, SENSITIVE_INFO, USER_ID
  • Purposes: THIRD_PARTY_ADVERTISING, DEVELOPERS_ADVERTISING, ANALYTICS, PRODUCT_PERSONALIZATION, APP_FUNCTIONALITY, OTHER_PURPOSES
  • Protections / linkage: DATA_USED_TO_TRACK_YOU, DATA_LINKED_TO_YOU, DATA_NOT_LINKED_TO_YOU, DATA_NOT_COLLECTED

Test plan

  • yarn jest src/metadata/apple/tasks/__tests__/data-usage-test.ts — 11 tests covering empty state, single category, multi-category dedupe, dataNotCollected sentinel, create/delete diff, no-op when matching, and a download → upload round-trip.
  • yarn jest src/metadata/apple — full apple metadata suite, 147 tests pass.
  • yarn typecheck clean for the changed files (the unrelated @expo/eas-build-job / @expo/eas-json resolution errors that exist on main are unaffected).
  • yarn lint clean for the changed files.
  • yarn fmt.
  • Manual metadata:pull against an app that already has Privacy Nutrition Labels declared.
  • Manual metadata:push against an app with no labels yet, then verify in App Store Connect.

Open questions

  • Apple enum stability. The enum lists above match what @expo/apple-utils ships today. Apple has historically added categories (e.g. ENVIRONMENTAL_SCANNING, HANDS, HEAD_MOVEMENT for visionOS) without warning, so additionalProperties: false on the schema may need to relax over time. If a user's project hits an unknown enum, the schema validator will reject it.
  • DATA_NOT_COLLECTED sentinel. Apple represents "Data Not Collected" as a single row with no category/purpose and protection: DATA_NOT_COLLECTED. This PR collapses that into a top-level dataNotCollected: true toggle for ergonomics, and re-expands it into the sentinel row on push. I have not been able to verify that this is exactly how the iris endpoint expects it on a brand-new app — there may be a separate "no data" creation flow I'm missing. Worth manual verification before users rely on this.
  • Publish state. AppDataUsagesPublishState.updateAsync({ published: true }) is what asc-cli does, but the iris endpoint is non-standard (it's keyed by app id, not by a publish-state id) and not in the public ASC OpenAPI spec. If Apple changes the contract this is the most likely thing to break.
  • No CLI surface. This PR only wires the task into the existing metadata:push / metadata:pull flow. A dedicated metadata:privacy subcommand could come later if useful.

🤖 Generated with Claude Code

Adds a new `privacy.dataUsage` block to the eas-cli metadata schema and
a `DataUsageTask` that pushes/pulls App Privacy Nutrition Labels via the
@expo/apple-utils AppDataUsage* models. After a successful push the
publish state is flipped to published so the changes take effect.

Apple has required every new app submission since 2021 to declare what
data the app collects, so this closes the biggest remaining gap in
`eas metadata`.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
@github-actions
Copy link
Copy Markdown

github-actions bot commented Apr 9, 2026

❌ It looks like a changelog entry is missing for this PR. Add it manually to CHANGELOG.md.
⏩ If this PR doesn't require a changelog entry, such as if it's an internal change that doesn't affect the user experience, you can add the "no changelog" label to the PR.

@github-actions
Copy link
Copy Markdown

github-actions bot commented Apr 9, 2026

Subscribed to pull request

File Patterns Mentions
**/* @douglowder
packages/eas-cli/schema/** @byCedric
packages/eas-cli/src/metadata/** @byCedric

Generated by CodeMention

@codecov
Copy link
Copy Markdown

codecov bot commented Apr 9, 2026

Codecov Report

❌ Patch coverage is 88.81119% with 16 lines in your changes missing coverage. Please review.
✅ Project coverage is 54.40%. Comparing base (8c170af) to head (9d93c6e).

Files with missing lines Patch % Lines
...ges/eas-cli/src/metadata/apple/tasks/data-usage.ts 91.61% 11 Missing ⚠️
...ckages/eas-cli/src/metadata/apple/config/writer.ts 44.45% 5 Missing ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##             main    #3596      +/-   ##
==========================================
+ Coverage   54.26%   54.40%   +0.14%     
==========================================
  Files         821      822       +1     
  Lines       35327    35470     +143     
  Branches     7363     7412      +49     
==========================================
+ Hits        19166    19293     +127     
- Misses      16074    16090      +16     
  Partials       87       87              

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

@EvanBacon EvanBacon marked this pull request as draft April 10, 2026 00:12
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant