Skip to content

FamilyActivityPicker auto-promotes individual app selections to a category — selection metadata becomes inaccurate #95

@GoatyGoatson

Description

@GoatyGoatson

Summary

When using DeviceActivitySelectionViewPersisted, iOS's native FamilyActivityPicker silently auto-promotes individual app selections into a category-level selection once enough sub-items of the same category are chosen. The onSelectionChange payload reflects this collapse, which makes any user-facing counter or limit logic based on applicationCount / categoryCount misleading or incorrect.

Environment

  • react-native-device-activity: 0.6.1
  • iOS: 26.0 (iPhone 13 mini, physical device)
  • Xcode: 26
  • Expo SDK 54, New Architecture enabled
  • React Native version: latest matching SDK 54

Reproduction

Starting from an empty FamilyActivitySelection (selectionId \"my_selection\"):

  1. Open the picker
  2. Inside category Spiele/Games, tap App A individually (e.g. Clash of Clans)
    • onSelectionChange emits: `{"applicationCount":1,"categoryCount":0,"webDomainCount":0,"includeEntireCategory":false}` ✅ as expected
  3. Tap App B individually inside the same category (e.g. Mazery)
    • onSelectionChange emits: `{"applicationCount":0,"categoryCount":1,"webDomainCount":0,"includeEntireCategory":false}` ❌

Expected for step 3: `applicationCount: 2, categoryCount: 0` (two individual apps).
Actual: iOS auto-collapsed the two app tokens into a single category token. The picker visually still shows both apps with checkmarks, but the underlying FamilyActivitySelection no longer contains the individual applicationTokens — only the parent categoryToken.

Why this matters

We use the count for a freemium quota ("block up to N individual apps for free, unlimited with premium"). With the auto-promotion behavior, a user who selects 2 apps gets reported as "1 category selected" — making it impossible to:

  • Show a correct progress counter (X / N selected)
  • Enforce a per-app limit
  • Distinguish "user explicitly chose the whole category" from "user picked some apps and iOS merged them"

The metadata struct we get back (`applicationCount`, `categoryCount`, `webDomainCount`, `includeEntireCategory`) doesn't carry enough info to differentiate user intent.

What we tried

  • Tracking the delta between consecutive onSelectionChange events and inferring auto-promotion when applicationCount drops while categoryCount rises — works during a single picker session but is lost on app restart since the persisted selection only contains the collapsed form
  • Inspecting the includeEntireCategory flag — appears unrelated; it's false in both "user picked Alle" and "iOS auto-promoted" cases on iOS 26
  • Looking at the native side (ReactNativeDeviceActivityModule.swift, Shared.swift) — the application/category/webDomain tokens are read but only counts are exposed across the bridge

Feature request / questions

  1. Is this iOS behavior documented somewhere? When does iOS promote individual selections to a category? Always when all visible apps of a category are picked? When N items are picked? Does it depend on Family Controls authorization scope?

  2. Could the library expose additional info via onSelectionChange? For example:

    • The last-tapped token type (app | category | webDomain)
    • A boolean like lastChangeWasAutoPromotion based on internal state diffing
    • Optionally the raw serialized selection token so consumers can do their own diffing across sessions
  3. Workaround suggestions? Has anyone solved counter-style UX ("X of 3 apps selected") on top of this picker?

Happy to put together a PR if there's a desired direction. Thank you for the library — it's been very helpful for our Deep Work app.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions