Skip to content

Add PrivacyInfo.xcprivacy declaring NSPrivacyAccessedAPICategoryUserDefaults#2471

Open
Neelagiri65 wants to merge 1 commit into
Dimillian:mainfrom
Neelagiri65:add-privacy-manifest
Open

Add PrivacyInfo.xcprivacy declaring NSPrivacyAccessedAPICategoryUserDefaults#2471
Neelagiri65 wants to merge 1 commit into
Dimillian:mainfrom
Neelagiri65:add-privacy-manifest

Conversation

@Neelagiri65

Copy link
Copy Markdown

Hi @Dimillian — IceCubesApp doesn't currently ship a PrivacyInfo.xcprivacy, and the app reads UserDefaults in a couple of places that fall under Apple's Required Reason API rules (in force since May 2024). App Store Connect rejects submissions for this with ITMS-91053 ("Missing API declaration").

This PR adds the manifest with the narrowest declaration the code actually needs.

Why these two reason codes

Call site Category Reason
Packages/Env/Sources/Env/UserPreferences.swift:88UserDefaults.standard NSPrivacyAccessedAPICategoryUserDefaults CA92.1 (same-app data)
Packages/Env/Sources/Env/UserPreferences.swift:109UserDefaults(suiteName: "group.com.thomasricouard.IceCubesApp") shared with widget/notification extensions NSPrivacyAccessedAPICategoryUserDefaults 1C8F.1 (same-app-group data)
IceCubesApp/App/Main/IceCubesApp.swift:42register(defaults:) inside #if DEBUG same wouldn't ship in release; CA92.1 covers it if it ever does

No other Required Reason API categories apply — I checked for FileTimestamp, SystemBootTime, DiskSpace, and ActiveKeyboards symbols across the codebase and didn't find real usages. No tracking domains. No AI service endpoints. So this manifest stays minimal.

Bundling

The project uses Xcode's synchronized folder groups (PBXFileSystemSynchronizedRootGroup × 9 in the pbxproj), so dropping the file into IceCubesApp/ next to Info.plist should add it to the main app target automatically. Please verify it lands in the Copy Bundle Resources phase before merging.

Out of scope for this PR

The extension targets — IceCubesActionExtension, IceCubesShareExtension, IceCubesNotifications, IceCubesAppWidgetsExtension — all import Env and therefore transitively use UserDefaults. Each is a separate bundle and will need its own PrivacyInfo.xcprivacy copy to be fully compliant. Happy to follow up with a second PR for those if you'd like; I split it out so the main-app fix is reviewable on its own.

How I found this

I ran PrivacyLint — a Swift CLI I built to catch this category of rejection — against the repo. It flagged 1 blocking error (no manifest) and 19 Required-Reason warnings; on triage, the two UserDefaults ones above are real and the other 14 (creationDate) are SwiftData @Model property names rather than URLResourceValues.creationDate calls, so they don't need declaration. PrivacyLint v1 can't statically disambiguate those — it's a documented limitation I'm fixing in v0.2.

Happy to iterate on anything here.

…efaults

The app reads UserDefaults in two ways that require declaration under
Apple's Required Reason API rules (in force since May 2024):

  - UserDefaults.standard in Packages/Env/Sources/Env/UserPreferences.swift:88
    (the prepareTranslationType path) → CA92.1 (same-app data).
  - UserDefaults(suiteName: 'group.com.thomasricouard.IceCubesApp') in
    Packages/Env/Sources/Env/UserPreferences.swift:109, shared with the
    widget/notification extensions → 1C8F.1 (same-app-group data).

Without this declaration, App Store Connect will block new submissions
with ITMS-91053 ('Missing API declaration').

Xcode's synchronized folder groups should include the file in the main
app target automatically; please verify in the Copy Bundle Resources
build phase before merging.

Note: extension targets (IceCubesActionExtension, IceCubesShareExtension,
IceCubesNotifications, IceCubesAppWidgetsExtension) that import Env and
therefore transitively use UserDefaults may need their own copies of this
manifest in their respective Resources folders — out of scope for this PR.

Discovered with PrivacyLint (https://github.com/Neelagiri65/privacylint).
The scanner also reported 14 'creationDate' findings that are SwiftData
@model property names rather than file-timestamp Required Reason API
calls — those are false positives in v1 (no semantic type resolution)
and not addressed here.
Neelagiri65 added a commit to Neelagiri65/privacylint that referenced this pull request Jun 8, 2026
Pushed the add-privacy-manifest branch to the fork, opened the PR
against Dimillian/IceCubesApp main. PR is live, open, +24/-0 across
1 file (IceCubesApp/PrivacyInfo.xcprivacy).

- HANDOFF now reflects PR-open state (was: 'drafted locally').
- Reddit + Swift Forums drafts updated to link the PR directly,
  replacing the 'will open a PR' placeholder. The Reddit headline
  now reads: 'I checked — no PrivacyInfo.xcprivacy anywhere in the
  IceCubesApp repo. Real finding. PR open here.' That's the strongest
  credibility hook the post can lead with.
- 'Push the PR' removed from NEXT; replaced with 'monitor PR response,
  respond to Dimillian's review promptly'. If merged, it becomes the
  Show HN post's proof point.

Tree clean; tests still 119 pass; tap install still working.
Neelagiri65 added a commit to Neelagiri65/privacylint that referenced this pull request Jun 8, 2026
…ion debt

End-of-session sweep. Engine + tap + IceCubesApp PR (Dimillian/IceCubesApp#2471)
are verified live. Blog post / Reddit / Swift Forums / LinkedIn were reported
as 'done' by the user but the URLs were never pasted back, so Claude did
not verify them. HANDOFF flags this honestly:

- New 'Session end snapshot' block at the top separates 'verified live'
  from 'user reported done, not verified by Claude' so the next session
  knows exactly what to confirm before counting them as launched.
- 'How to resume' now has URL-verification as step 3, before any new
  work.
- NEXT list: 'verify the four launch URLs' is now the first remaining
  action; 'monitor PR #2471' is second; v0.2.0 SwiftData false-positive
  reduction stays as the next code milestone.

Disk state recorded: fork clone at /tmp/pl-pr/IceCubesApp survives reboot
but not a /tmp clean; reclone instructions in place. Helper script
/tmp/publish-launch.sh is ephemeral but regeneratable from git show
or from the dist/launch/ markdowns.

Final verification:
  swift build ✅
  swift test ✅ 119 tests in 9 suites
  git status clean, origin/master in sync
  gh pr view 2471 → state: OPEN, 0 comments

Tree clean; pushed. Session safely handoverable.
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