expo / expo/eas-cli

iOS capability sync strips Push Notifications "Broadcast" sub-feature (PUSH_NOTIFICATION_FEATURE_BROADCAST) every build

Open
#3,815 4 comments 2 reactions 1 assignee Claimed by @gwdp View on GitHub
documentation
Dominant language
TypeScript
Stars
1.4k
Forks
236
Avg merge
3d 8h
Merged PRs (30d)
98

Description

### Build/Submit summary

iOS capability syncing **disables the "Broadcast" sub-feature of Push Notifications on every `eas build`**, because Broadcast is not a standalone capability — it's a setting nested under `PUSH_NOTIFICATIONS` (`PUSH_NOTIFICATION_FEATURES → PUSH_NOTIFICATION_FEATURE_BROADCAST`). The syncer's push patch only sends `{ capabilityType: 'PUSH_NOTIFICATIONS', option: 'ON' }` (no sub-options), so it re-creates the Push Notifications capability at its defaults and drops the Broadcast flag.

Broadcast push is required for **iOS 18 Live Activity broadcast push channels**. After every build, creating a channel via the APNs channel-management API returns `400 BroadcastFeatureNotEnabled`, until you re-enable Broadcast by hand in the Developer portal — only for the next build to strip it again.

### Environment

- eas-cli 19.1.0 (darwin-arm64, node 24.15.0)
- iOS, EAS-managed credentials

### Steps to reproduce

1. In the Apple Developer portal, enable **Broadcast** under Push Notifications for your App ID (Identifiers → App ID → Push Notifications → Broadcast → Save). Required for iOS 18 broadcast push / Live Activity channels.
2. Run `eas build -p ios` with managed credentials.
3. Capability sync re-patches Push Notifications, dropping the Broadcast sub-feature.
4. A backend `POST /1/apps/{bundleId}/channels` to APNs now returns `400 {"reason":"BroadcastFeatureNotEnabled"}`.

### Evidence (`EAS_DEBUG=1`)

The remote capability shows Broadcast as a nested **setting** under Push Notifications:

```json
{
"id": "_PUSH_NOTIFICATIONS",
"attributes": {
"settings": [
{
"key": "PUSH_NOTIFICATION_FEATURES",
"options": [
{ "key": "PUSH_NOTIFICATION_FEATURE_BROADCAST", "properties": [null] }
]
}
],
"enabled": true
}
}
```

The sync decision + patch:

```
Will enable remote capability: aps-environment (Push Notifications).
Will skip remote capability: com.apple.security.application-groups (App Groups).
Will skip remote capability: com.apple.developer.associated-domains (Associated Domains).
Existing to disable: [ '_IN_APP_PURCHASE', '_PUSH_NOTIFICATIONS' ]
Patch Request: [ { capabilityType: 'PUSH_NOTIFICATIONS', option: 'ON' } ]
```

The patch re-enables Push Notifications with **default** settings, so the `PUSH_NOTIFICATION_FEATURE_BROADCAST` sub-setting is lost. (It also lists `IN_APP_PURCHASE` to disable — the same reconciliation can strip other portal-only capabilities the syncer doesn't model.)

### Why there is no config-level workaround

- **Broadcast has no entitlement**, so it can't be declared in `ios.entitlements` for the syncer to preserve.
- The capability is an App-ID-level setting in App Store Connect, **not part of the build**, so a config plugin can't set it.
- The push patch has no field for sub-options.

### Current workaround

`EXPO_NO_CAPABILITY_SYNC=1` disables capability syncing entirely, preserving the manually-enabled Broadcast setting (and other portal-only capabilities like In-App Purchase).

**It must be set in the shell environment** — e.g. `EXPO_NO_CAPABILITY_SYNC=1 eas build …`, or exported in your shell. Setting it under a build profile's `env` in `eas.json` does **not** work: capability sync runs locally during credential resolution, before/outside the build-profile env (and `eas credentials` uses no build profile at all), so it never sees the eas.json value.

### Request

One of:

- Preserve capability **sub-settings** (e.g. `PUSH_NOTIFICATION_FEATURE_BROADCAST`) during sync rather than resetting Push Notifications to defaults; or
- Don't disable/reset capabilities (or sub-features) the syncer doesn't model — treat them like the already-skipped App Groups / Associated Domains; or
- At minimum, document that enabling Broadcast push requires `EXPO_NO_CAPABILITY_SYNC=1` (or local credentials).

Contributor guide

Open the contributing guide

Assessment

This issue has not been assessed yet.

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.