Appearance
Apple Developer Program enrolment — the revert checklist
Why this page exists
Several things in this repo are deliberately degraded to work around the free Apple Personal Team. Each one is correct today and wrong the moment the $99/yr membership is active. Written as one checklist, at the owner's request, because the alternative is a set of comments spread across plugin headers that nobody re-reads at the moment of enrolment — which is exactly how a temporary accommodation becomes the shipped configuration.
Work top to bottom on the day the membership activates. Every item has a verification command; none of them is "just delete the file".
What the paid membership actually changes
| Free Personal Team (today) | Paid Apple Developer Program | |
|---|---|---|
| Provisioning-profile validity | 7 days, then the app refuses to launch | 1 year |
| Apps per device per 7 days | 3 | unlimited |
| Sign in with Apple capability | ❌ cannot be signed | ✅ |
Push notifications (aps-environment) | ❌ cannot be signed | ✅ |
| Associated Domains (universal links) | ❌ | ✅ |
| TestFlight / App Store submission | ❌ | ✅ |
| Profile creation from the CLI | needs an Xcode pass (QRS-673) | still needs -allowProvisioningUpdates |
R1 — Sign in with Apple: flip the opt-in ⚠ REQUIRED BEFORE ANY SUBMISSION
Status today: the com.apple.developer.applesignin entitlement is stripped by default by apps/mobile/plugins/withoutAppleSignInEntitlement.js (QRS-674), because a Personal Team cannot sign it and its presence makes Xcode refuse to create any profile — which surfaces only as the misleading No profiles for 'in.digious.qrsetu' were found.
Nothing is deleted and no code was removed. expo-apple-authentication is still a dependency, signInWithAppleNative is still implemented, and it degrades to { kind: 'error', error: 'provider_unavailable' } rather than crashing. Only the entitlement is withheld.
Revert — set one environment variable:
bash
cd apps/mobile
EXPO_APPLE_SIGNIN=1 npx expo prebuild -p ios --cleanVerify before building (this is the only check that can see an entitlement — jest, Playwright and the Android build all structurally cannot):
bash
cd apps/mobile
EXPO_APPLE_SIGNIN=1 npx expo config --type introspect | grep -c applesignin # expect 2
npx expo config --type introspect | grep -c applesignin # expect 0 (default unchanged)Then make it permanent for real builds — pick one, and prefer the first:
- Export it in the build environment (CI secret / shell profile on the Mac). Keeps the default safe for anyone still on a free team, including a future second machine.
- Retire the plugin entirely once no free-team signing is ever used again: remove
'./plugins/withoutAppleSignInEntitlement'fromapp.json'splugins, delete the plugin file, and delete theiOS Sign-in-with-Apple entitlement is stripped for free-team signingdescribe block inapps/mobile/src/app/__tests__/notifications-build-config.test.ts. Do not delete the plugin while leaving it registered —expo prebuildrequire()s it by path and the build will fail.
⚠ Apple Guideline 4.8 requires Sign in with Apple because the app offers Google sign-in. A submission build with the entitlement stripped is a rejection, not a degraded feature. This item is not optional.
⚠ Sign in with Apple also needs Supabase's Apple provider configured (Service ID, Team ID, Key ID, .p8) — all four are paid-account artifacts and none exists yet. Budget that as part of this item, not as a follow-up: the entitlement alone gets you a button that fails at the provider.
R1 — the 7-day expiry and the CLI profile trap: no revert needed, but re-read it
The 7-day re-signing loop simply stops being a problem (profiles become 1-year). But QRS-673 does not go away — expo run:ios still omits -allowProvisioningUpdates once DEVELOPMENT_TEAM is in the .pbxproj, so a missing profile still cannot be minted by the CLI. Keep the Xcode-pass escape hatch documented in iOS Build & Device Testing.
NOT a revert item — push notifications stay stripped
apps/mobile/plugins/withoutPushEntitlement.js strips aps-environment (QRS-234). Do not re-enable it with the paid account. The reason it is stripped is not the free team — it is that R1 has no remote push at all: nothing calls getExpoPushTokenAsync/getDevicePushTokenAsync, and app.json sets enableBackgroundRemoteNotifications: false. The entitlement declares a capability we do not use, and declaring it invites App Review to ask about a push backend that does not exist.
It reverts when remote push is actually implemented, which is its own project (a push backend, token storage, and the iOS scheduled-notification budget in ADR-0016) — not a checkbox on enrolment day.
The distinction to hold on to: "a free team cannot sign it" and "we do not use it" are different reasons that happen to produce the same edit. Only the first is reverted by buying an account.
Enrolment-day sequence
- Enrol and wait. Apple's organisation review is 1–4 weeks and needs a D-U-N-S number; an individual enrolment is faster. Nothing below can start until the membership is active.
- Add the account in Xcode → Settings → Accounts. The Personal Team stays listed; the new team appears alongside it. Select the paid team in
Signing & Capabilitiesfor theSetutarget. - Sign in with Apple — the section above, including the Supabase provider setup.
- Re-check the version/build integrity gate:
npm run check:version. Every store resubmission needs a new build number, and Android/iOS legitimately diverge (see the release process page). - Rebuild both natives and re-run the parity checklist. This changes signing, entitlements and the generated
ios/project, so it is a full three-surface pass — not a config tweak. - Update
documentation/portal/releases/production-state.mdonce a build is actually submitted.
What to check the claims on this page against
Everything here is verifiable, and per the repo's third rule it should be re-verified rather than trusted:
bash
# which entitlements the prebuild would write, in both directions
cd apps/mobile
npx expo config --type introspect | grep -E "applesignin|aps-environment"
# the plugins that produce that result, in the order that matters
node -e "console.log(require('./app.json').expo.plugins)"
# the contracts asserted in code
npm run -w @qrsetu/mobile test -- src/app/__tests__/notifications-build-config.test.ts⚠ Ordering in app.json's plugins array is load-bearing and reverse to intuition — Expo's withMod runs the last-registered mod first. A stripper must be registered before whatever adds the key. Both strippers sit before expo-notifications for that reason, and the test asserts it because nothing else in the repo can observe an entitlement.