Part of: Online IPA Installer
ExportOptions.plist Generator for xcodebuild -exportArchive
Pick a distribution method and get the plist, the xcodebuild command and the fastlane call that go with it. Built in your browser, with the method values current Xcode accepts rather than the ones it now refuses.
Optional: drop a .mobileprovision to fill in the team ID, profile name and bundle ID. It is parsed in this browser tab.
Builds for devices registered to your team. Replaces the old ad-hoc value.
The ten-character Apple Developer team identifier.
Manual signing needs a provisioningProfiles entry for every bundle identifier in the archive.
Used in the commands below, not in the plist.
The key in the provisioningProfiles map.
The profile name as it appears in the Apple Developer portal, or its UUID.
Left out when empty, which lets Xcode pick from the keychain.
A single device model identifier also works here.
export writes the build to disk; upload hands it to App Store Connect instead.
ExportOptions.plist
<?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd"> <plist version="1.0"> <dict> <key>method</key> <string>release-testing</string> <key>signingStyle</key> <string>manual</string> <key>provisioningProfiles</key> <dict> <key>com.example.app</key> <string>MyApp Release Testing</string> </dict> <key>destination</key> <string>export</string> <key>thinning</key> <string><none></string> <key>stripSwiftSymbols</key> <true/> <key>uploadSymbols</key> <true/> </dict> </plist>
xcodebuild -exportArchive
xcodebuild -exportArchive \ -archivePath build/MyApp.xcarchive \ -exportPath build/export \ -exportOptionsPlist ExportOptions.plist
fastlane build_app
# Fastfile: pass the options as a hash, or point at the generated file
# with export_options: "ExportOptions.plist".
build_app(
scheme: "MyApp",
export_options: {
method: "release-testing",
signingStyle: "manual",
provisioningProfiles: { "com.example.app" => "MyApp Release Testing" },
destination: "export",
thinning: "<none>",
stripSwiftSymbols: true,
uploadSymbols: true
}
)Save the plist next to your project, or write it in the CI job before the export step. The key list differs between Xcode versions, so check the output of xcodebuild -help on the machine that runs your build before you rely on a key you have not used before.
What ExportOptions.plist is for
An archive is not a distributable app. xcodebuild archive produces an .xcarchive, and a second step signs the contents for one specific audience and writes out the .ipa. ExportOptions.plist is how you tell that second step what the audience is. Apple documents the invocation as xcodebuild -exportArchive -archivePath <xcarchivepath> -exportPath <destinationpath> -exportOptionsPlist <path> in Technical Note TN2339.
In Xcode the same choice is a click in the Distribute App sheet. On a build machine there is no sheet, so every decision the sheet would have made has to be written down: which method, which team, whether Xcode may resolve signing itself, and which profile belongs to which bundle identifier. That is the whole file.
| Key | What it decides |
|---|---|
| method | Which kind of distribution this export is for. The value that changed most recently, and the one that breaks builds. |
| teamID | The ten-character Apple Developer team identifier. TN2339 documents it as the team to use for the export. |
| signingStyle | automatic or manual. Manual means you name the profiles yourself, which is what a build machine normally does. |
| provisioningProfiles | A dictionary mapping each bundle identifier in the archive to a profile name or UUID. Only read under manual signing. |
| signingCertificate | Which certificate to sign with. Leave it out and Xcode picks a matching identity from the keychain. |
| destination | export writes the signed build to your export path. upload hands it to App Store Connect instead of writing a file. |
| thinning | Whether to emit one universal app, every thinned variant, or the variant for a single device model. |
| stripSwiftSymbols | Strips symbols from the Swift standard libraries embedded in the IPA, which makes the build smaller. Defaults to YES. |
| uploadSymbols | For App Store exports only: whether the package includes symbols, so crash reports come back symbolicated. It does nothing on a release-testing or enterprise export. Defaults to YES. |
This is not the complete set. TN2339 tells you to run xcodebuild -help to get all available keys, and the keys differ between Xcode versions, so the list printed by the Xcode your build machine uses is the authoritative one.
The rename in Xcode 15.4, and why Xcode 26 fails your build
Three command line names are recorded as deprecated, and the old ones are still accepted with a warning. xcodebuild -help on Xcode 26.6 (build 17F113) puts it as: additional options include app-store (deprecated: use app-store-connect), ad-hoc (deprecated: use release-testing), and development (deprecated: use debugging). The warning Xcode 15.4 emits for the ad-hoc pair reads:
IDEDistribution: Command line name "ad-hoc" is deprecated. Use "release-testing" instead.
The App Store value carries the matching message telling you to use app-store-connect in place of app-store, and development carries one telling you to use debugging. The sources below record the ad-hoc warning under Xcode 15.4; they do not pin the app-store rename to a specific release, so this page does not either. Xcode 26 stopped warning. The export now fails with an exportArchive exportOptionsPlist error for the method key, and the build stops there.
The reason this shows up as a sudden failure rather than a migration is CI. A workflow pinned to a runner image label follows that image to the newest Xcode without anyone editing the workflow, so a lane that has not changed in a year starts failing at the export step on a Tuesday. The fix is one string.
fastlane is not exempt. gym copies export_method straight into the same plist under method, so a Fastfile carrying the old value hits the identical failure, which is what fastlane issue 22028 reports. Its documentation still lists the old values as valid, so if a fastlane version refuses release-testing as an export_method, pass the method inside export_options instead. The generator above emits that form.
method: app-store-connect
For TestFlight and App Store submissions. Apple's preconfigured option for this is named TestFlight & App Store, and its custom distribution list names the method App Store Connect. This is the value that replaced app-store, which xcodebuild -help now records as deprecated; the sources below do not pin that rename to a specific Xcode release, so this page does not either. Pair it with destination set to upload when you want xcodebuild to send the build to App Store Connect instead of writing an .ipa to disk.
method: release-testing
For builds you hand to testers on devices your team has registered. Apple describes the Release Testing option as default settings to distribute a version to test before release, exported to install on devices your team registers with App Store Connect, and notes that it is not available for Mac apps. This is the export that produces the .ipa most beta distribution runs on, and it is the value that used to be called ad-hoc. Every device has to be in the profile before you export, because the export step is where the profile is embedded and the app re-signed. That is what signingStyle, signingCertificate and provisioningProfiles are for: they govern the signing style used when re-signing the app for distribution.
If a tester cannot install the result, the profile is where to look first. The provisioning profile decoder shows the device list and the expiry date without a Mac.
method: enterprise
For in-house distribution, and Apple lists Enterprise among its distribution options. The profile behind this method carries ProvisionsAllDevices rather than a device list, so the build installs on any device inside the organization without registering UDIDs. It belongs to the Apple Developer Enterprise Program, which is a separate membership: a team on the ordinary Apple Developer Program exporting a test build wants release-testing.
method: debugging
For a build you install and debug on registered devices. Apple describes the Debugging option as default settings to distribute a version for debugging, exported to install and debug on devices your team registers with App Store Connect, and notes that it enables sandbox testing environments for some capabilities. The older command line name for this method is development. The practical difference from release-testing is the get-task-allow entitlement, which is what lets a debugger attach: present on a debugging build, absent on one you hand to someone else.
method: developer-id
For a macOS app distributed outside the Mac App Store. Apple names this option Direct Distribution and describes it as notarizing a Developer ID app for direct distribution, available only for apps built for Mac. developer-id is macOS only. The iOS equivalents are enterprise in-house distribution, and, in the EU, Web Distribution and alternative marketplaces: Apple describes Web Distribution as letting authorized developers distribute their iOS and iPadOS apps to users in the European Union directly from a website owned by the developer, on devices running a minimum of iOS 17.5 or iPadOS 18 (checked on 2026-09-03). None of those is what a team on the ordinary Developer Program uses for a test build.
signingStyle and provisioningProfiles
signingStyle takes automatic or manual. With automatic signing, Xcode resolves the identity and the profiles itself, and the plist stays short. With manual signing it resolves nothing, so the plist has to name a profile for every bundle identifier in the archive:
<key>signingStyle</key> <string>manual</string> <key>provisioningProfiles</key> <dict> <key>com.example.app</key> <string>MyApp Release Testing</string> </dict>
The value is the profile name as it appears in the Apple Developer portal, or the profile UUID. A UUID is unambiguous and survives someone renaming a profile in the portal; a name is readable in a diff. Either is accepted.
Every embedded target counts. An app with a widget, a notification service extension and a watch app has four bundle identifiers, and manual signing needs an entry for each one. A missing entry surfaces as an export failure naming the bundle identifier it could not match, which is a more useful error than most in this area. Note that a wildcard App ID cannot be a key here: the dictionary is keyed on the real bundle identifier of the target.
thinning and destination
xcodebuild -help (Xcode 26.6, 17F113) documents thinning as a String for non-App Store exports with three shapes: <none> for a single non-thinned universal app, <thin-for-all-variants> for the universal app plus every thinned variant, or a model identifier for a specific device, written plainly rather than in angle brackets, for example iPhone7,1. TN2339 lists the key and its type but elides the description, which is why it tells you to run -help. For a build you are about to share with testers, the universal app is the right default, because a thinned variant only installs on the model it was built for.
destination decides where the export goes: export writes the signed build to the path you passed as -exportPath, and upload sends it to App Store Connect directly. A lane that exports and then uploads with a separate step wants export, since upload leaves no file behind to archive as a build artifact.
What was checked, and when
Option names on this page were checked on 2026-09-03 against the sources below, each of which was read rather than recalled. The key list, the method deprecations and the thinning value shapes were checked against xcodebuild -help on Xcode 26.6 (build 17F113) on 2026-09-03. Run it against your own Xcode, as Apple advises, because the key set differs between versions.
The current distribution options are named TestFlight & App Store, TestFlight Internal Only, Release Testing, Enterprise, Direct Distribution and Debugging, with a Custom list that still shows Ad Hoc, Developer ID, Development and Copy App.
Checked 2026-09-03: Apple, Distributing your app for beta testing and releases
The export invocation is xcodebuild -exportArchive -archivePath <xcarchivepath> -exportPath <destinationpath> -exportOptionsPlist <path>, and the instruction to run xcodebuild -help for the full key list. Listing 22 prints key names and types only, with every description elided, so the technote is not the source for what any key's values mean.
Checked 2026-09-03: Apple, Technical Note TN2339: Building from the Command Line with Xcode
The full key list, verbatim: method "Available options: app-store-connect, release-testing, enterprise, debugging, developer-id, mac-application, and validation ... Additional options include app-store (deprecated: use app-store-connect), ad-hoc (deprecated: use release-testing), and development (deprecated: use debugging)"; thinning "Available options: <none> ..., <thin-for-all-variants> ..., or a model identifier for a specific device (e.g. \"iPhone7,1\")"; uploadSymbols "For App Store exports, should the package include symbols?"; stripSwiftSymbols "Should symbols be stripped from Swift libraries in your IPA?".
Checked 2026-09-03: xcodebuild -help, Xcode 26.6 (build 17F113), run on the build machine on 2026-09-03
Web Distribution lets authorized developers distribute their iOS and iPadOS apps to users in the European Union directly from a website owned by the developer, available to EU users on devices running a minimum of iOS 17.5 or iPadOS 18.
Checked 2026-09-03: Apple, Web Distribution in the EU
Apple guidance on where the option list is authoritative: the man page for xcodebuild is not the source of truth, running xcodebuild with the -help option is.
Checked 2026-09-03: Apple Developer Forums, DTS reply on thread 731625
Xcode 15.4 emits IDEDistribution: Command line name ad-hoc is deprecated. Use release-testing instead, and gym copies export_method into the same plist key, so a fastlane lane is not exempt.
Checked 2026-09-03: fastlane issue 22028
Xcode 26 stopped warning and started failing: exportArchive exportOptionsPlist error for key method, with release-testing named as the value to use.
Checked 2026-09-03: expo/eas-cli issue 4040
The same rename hit the App Store value: Command line name app-store is deprecated. Use app-store-connect instead.
Checked 2026-09-03: flutter issue 149369, quoting the xcodebuild output
signingStyle, signingCertificate, provisioningProfiles, destination and stripSwiftSymbols appear as ExportOptions keys in a worked example on the Apple Developer Forums, where provisioningProfiles is a dictionary of bundle identifiers to profile UUIDs and the reported failure is xcodebuild finding no matching profile for a target's bundle identifier.
Checked 2026-09-03: Apple Developer Forums, thread 114448
fastlane still documents export_method as accepting app-store, validation, ad-hoc, package, enterprise, development, developer-id and mac-application, and documents export_options as a plist path or a hash.
Checked 2026-09-03: fastlane documentation for gym
Frequently Asked Questions
What is ExportOptions.plist used for?
It is the property list you pass to xcodebuild -exportArchive with the -exportOptionsPlist flag, and it tells Xcode how to turn an .xcarchive into a distributable app. Apple documents the invocation as xcodebuild -exportArchive -archivePath <xcarchivepath> -exportPath <destinationpath> -exportOptionsPlist <path> in Technical Note TN2339. The plist carries the distribution method, the team identifier, whether signing is automatic or manual, which provisioning profile to use for each bundle identifier, and the thinning and destination choices.
Why does Xcode 26 reject the ad-hoc export method?
Apple renamed the command line name in Xcode 15.4, which warned rather than failed: IDEDistribution: Command line name ad-hoc is deprecated. Use release-testing instead. The same rename hit app-store, which now warns Use app-store-connect instead. Xcode 26 stopped warning and started refusing, reporting exportArchive exportOptionsPlist error for key method. Use release-testing. This bites hardest in CI, because the macos-latest runner image moves to the newest Xcode on its own and a workflow that has not changed suddenly fails at the export step.
Does fastlane escape the ad-hoc rename?
No. gym copies export_method straight into the same plist under the method key, so a lane using ad-hoc hits the identical deprecation and the identical failure, which is what fastlane issue 22028 reports. The fastlane documentation still lists app-store, validation, ad-hoc, package, enterprise, development, developer-id and mac-application as the valid values for export_method, so the documented value and the value Xcode accepts have drifted apart. The safe form is to pass the method inside export_options, or to hand gym the generated plist with export_options set to its path.
When do I need the provisioningProfiles dictionary?
Only when signingStyle is manual. With manual signing, Xcode does not pick a profile for you, so the plist has to map each target's bundle identifier to a profile name or UUID, and the export fails when a bundle identifier in the archive has no entry. With signingStyle set to automatic, Xcode resolves profiles itself and the dictionary is left out. Manual signing is the usual choice on a build machine, because the profiles are checked in or installed by the job rather than fetched by a signed-in Xcode.
What does the thinning key do?
It decides whether the export is one universal app or a set of per-device variants. The values are none for a single non-thinned app, thin-for-all-variants for the universal app plus every thinned variant, and a specific device model identifier for one variant, the first two written inside angle brackets, the model identifier written plainly, for example iPhone7,1. xcodebuild -help documents thinning as a String key for non-App Store exports. For an over-the-air test build, the non-thinned universal app is the one you want, because it installs on every device you hand it to.
Where is the full list of ExportOptions.plist keys documented?
In xcodebuild itself. TN2339 tells you to run xcodebuild -help to get all available keys for -exportOptionsPlist, and an Apple DTS engineer put it plainly on the developer forums: the man page for xcodebuild is not really the source of truth, run xcodebuild with the -help option. The set differs between Xcode versions, so the list printed by the Xcode your build machine actually uses is the only authoritative one. This page and this generator cover the keys developers reach for most, not every key Apple accepts.
After the export succeeds
A correct plist gets you a signed .ipa on disk, which is still a file on a build machine rather than an app on a phone. Upload the IPA and testers install it over the air from a link, with the itms-services manifest generated for you. The Android side of the same job is uploading the APK. Signing stays on your machine either way: we never ask for a certificate or a key.
Related tools: provisioning profile decoder, IPA inspector, and the fastlane integration if the export is part of a lane.