76 lines
6.1 KiB
Markdown
76 lines
6.1 KiB
Markdown
---
|
|
name: adhoc-distribution
|
|
description: Get a test build of a mobile app or game onto specific devices without a store review - Apple ad-hoc / release-testing (register device UDIDs, an ad-hoc provisioning profile, ExportOptions.plist, xcodebuild -exportArchive) and Google Play internal app sharing (upload an AAB or APK, get a share link). Explains when to use ad-hoc vs TestFlight vs Play internal testing vs internal app sharing, the 100 devices per year limit, and the tester setup. Use when the user wants an IPA for a few devices, asks how to register a device or UDID, needs an ExportOptions.plist, or wants a quick Play install link for a build.
|
|
---
|
|
|
|
# Ad-hoc builds and internal app sharing
|
|
|
|
CLI: `storebound` (or `bunx storebound` in a project that has it, or `bun <toolchain>/cli/index.ts`).
|
|
|
|
## Which channel
|
|
|
|
| Channel | Who installs | Review | Limits | Use it for |
|
|
|---|---|---|---|---|
|
|
| TestFlight internal (`testflight` skill) | up to 100 App Store Connect users | none | 90-day builds | the team, every build |
|
|
| TestFlight external | up to 10,000 testers, by email or public link | Beta App Review for the first build of a version | 90-day builds | wider beta |
|
|
| Apple ad-hoc / release-testing | registered devices only | none | 100 devices per product family per membership year | a client demo, a device lab, testers without App Store Connect access |
|
|
| Play internal testing track (`play-first-release` skill) | up to 100 testers by email list | none | needs a track release | the team, every build |
|
|
| Play internal app sharing | anyone allowed to use the link (default: anyone with it) | none | 100 users per link, link valid 60 days | a quick build for one person, any version code, debuggable builds |
|
|
|
|
Prefer TestFlight and Play internal testing. Ad-hoc costs device slots you only get back at the start of a
|
|
membership year; internal app sharing builds cannot be promoted to any track.
|
|
|
|
## Apple ad-hoc
|
|
|
|
Config: `apple.teamId` (Membership details), `apple.bundleId`, optional `apple.adhoc`:
|
|
`method` (`release-testing`, the name since Xcode 15.3, default; `ad-hoc` for older Xcode), `devices` (default
|
|
`.storebound/devices.csv`), `profile` (a profile name: manual signing; unset: automatic signing).
|
|
|
|
1. **The owner collects the devices.** UDIDs are personal data: they stay in `.storebound/devices.csv` (gitignored),
|
|
never in chat, issues or commits. Header line `name,udid,platform`, one device per line, platform `IOS` or `MAC_OS`.
|
|
The tester finds the UDID in Finder (iPhone connected, click the line under the device name) or Xcode Devices;
|
|
an Apple silicon Mac needs its Provisioning UDID (System Information > Hardware).
|
|
2. `storebound apple adhoc devices` (read-only): what is registered, UDIDs masked, and the slots used per device class.
|
|
3. `storebound apple adhoc register --dry`: validates the CSV (40 hex digits, or 8 hex - 16 hex), compares with the
|
|
registered devices and lists the new ones. Show it to the owner. Every new device uses one of the 100 yearly slots
|
|
of its product family, also if it is disabled later. Then `storebound apple adhoc register` asks and registers.
|
|
One device: `--name "Test iPhone" --udid <udid> --platform IOS`. It never disables, enables or deletes a device:
|
|
that is the owner's call on the Apple Developer website.
|
|
4. Signing, either:
|
|
- automatic (no `apple.adhoc.profile`): Xcode creates the profile during export (`-allowProvisioningUpdates`), or
|
|
- manual: `storebound apple adhoc profile --dry`, then `storebound apple adhoc profile`. It creates an
|
|
IOS_APP_ADHOC profile for the bundle id with every enabled iOS device and the valid Apple Distribution
|
|
certificates, and saves `build/adhoc/<name>.mobileprovision` (double-click to install). A profile cannot be
|
|
changed: when devices were added, a new profile with a dated name is created; set `apple.adhoc.profile` to it.
|
|
Deleting the old one is the owner's step on the developer website.
|
|
5. Archive in Xcode (Product > Archive) or `xcodebuild archive`.
|
|
6. `storebound apple adhoc options --dry` shows the ExportOptions.plist (method, teamID, signingStyle, thinning
|
|
`<none>`, destination export); without `--dry` it writes `build/ExportOptions.plist`.
|
|
7. `storebound apple adhoc export --archive build/App.xcarchive --dry` prints the exact `xcodebuild -exportArchive`
|
|
command; without `--dry` it runs it and lists the `.ipa` in `build/adhoc/`. With `-allowProvisioningUpdates`
|
|
Xcode may contact Apple to create or refresh signing assets.
|
|
8. Install: Apple Configurator, Finder (drag the IPA onto the device) or Xcode Devices. A device that is not in the
|
|
profile cannot install the build: re-run steps 3, 4 and 7.
|
|
|
|
## Google Play internal app sharing
|
|
|
|
1. Owner, once in Play Console > Internal app sharing (under Test and release): who may upload (the service account
|
|
needs "Release apps to testing tracks" for the app, or a place on the uploader list) and who may download
|
|
(an email list, or "Anyone you shared the link with can download", the default).
|
|
2. `storebound play share --aab app-release.aab --dry` (or `--apk`): file, size, sha256 and package; nothing sent.
|
|
3. `storebound play share --aab app-release.aab` asks, uploads and prints the share link and the certificate
|
|
fingerprint (Google re-signs the build with an internal app sharing key). `--json`:
|
|
`{ downloadUrl, certificateFingerprint, sha256 }`. Limits: app bundles up to 10 GiB, APKs up to 1 GiB.
|
|
4. Each tester, once per device: Play Store > Settings > About > tap "Play Store version" 7 times > turn on Internal
|
|
app sharing. Then open the link on the device. Testers must be able to see the app's store listing on Google Play
|
|
(country and track access). The link expires 60 days after the upload; re-upload for a new one.
|
|
|
|
## The owner decides
|
|
- Which devices to register (yearly slots) and removing or disabling any device.
|
|
- Who may download internal app sharing links, and sharing a link at all (anyone with it can install the build).
|
|
- Deleting old provisioning profiles.
|
|
|
|
## Out of scope
|
|
- Enterprise (in-house) distribution, Developer ID / notarized Mac apps, over-the-air install pages (the
|
|
ExportOptions `manifest` key).
|
|
- Firebase App Distribution and other third-party beta services.
|