Skip to content

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 current method values rather than the deprecated ones.

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>&lt;none&gt;</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 + export

# Fastfile. gym (fastlane 2.240.1 and earlier) only accepts the old method
# names, so archive with gym, then export with xcodebuild and the plist above.
# sh runs inside ./fastlane, hence the ../ paths.
build_app(
  scheme: "MyApp",
  archive_path: "build/MyApp.xcarchive",
  skip_package_ipa: true
)
sh("xcodebuild -exportArchive" \
   " -archivePath ../build/MyApp.xcarchive" \
   " -exportPath ../build/export" \
   " -exportOptionsPlist ../ExportOptions.plist")

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.

KeyWhat it decides
methodWhich kind of distribution this export is for. The value that changed most recently, and the one that breaks builds.
teamIDThe ten-character Apple Developer team identifier. TN2339 documents it as the team to use for the export.
signingStyleautomatic or manual. Manual means you name the profiles yourself, which is what a build machine normally does.
provisioningProfilesA dictionary mapping each bundle identifier in the archive to a profile name or UUID. Only read under manual signing.
signingCertificateWhich certificate to sign with. Leave it out and Xcode picks a matching identity from the keychain.
destinationexport writes the signed build to your export path. upload hands it to App Store Connect instead of writing a file.
thinningWhether to emit one universal app, every thinned variant, or the variant for a single device model.
stripSwiftSymbolsStrips symbols from the Swift standard libraries embedded in the IPA, which makes the build smaller. Defaults to YES.
uploadSymbolsFor 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

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. The old names still work with that warning: on Xcode 27.0, an export with the old ad-hoc name logs the deprecation and carries on. Use the new names in new configuration.

If an export fails with an exportOptionsPlist error for the method key that reads expected one {} but found followed by your value, look at the braces. The empty set means Xcode could not list any distribution methods for the archive, usually a signing or account problem, and renaming the method will not fix it: the same error comes back for release-testing. A value Xcode genuinely does not know fails with the accepted list printed inside the braces.

fastlane has not caught up. gym, up to 2.240.1, still validates export_method against the old names, so release-testing there stops the lane with Unsupported export_method, and a method passed inside export_options goes through the same check, which is what a comment on fastlane issue 22028 reports. In a Fastfile, either keep gym's export_method on the old ad-hoc name, which Xcode still accepts with a deprecation warning, or archive with gym and export with xcodebuild -exportArchive and the generated plist. The generator above emits the second 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. What the old names still do, and what fastlane accepts, was rechecked later, and those entries carry their own dates. 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

  • On Xcode 27.0 (build 27A266a), xcodebuild -help still lists ad-hoc (deprecated: use release-testing), and an export whose method is ad-hoc logs Command line name ad-hoc is deprecated. Use release-testing instead. and then carries on to the signing step exactly as release-testing does. An unknown value is what fails: exportOptionsPlist error for key method expected one {app-store-connect, release-testing, enterprise, debugging, validation}.

    Checked 2026-09-28: xcodebuild -help and -exportArchive, Xcode 27.0 (build 27A266a), run on the build machine on 2026-09-28

  • Xcode 15.4 emits IDEDistribution: Command line name ad-hoc is deprecated. Use release-testing instead. On the fastlane side, gym validates export_method against the old names only, so a lane using release-testing stops with Unsupported export_method 'release-testing' (reported in a comment on the issue on 2026-07-06), and a method passed inside export_options goes through the same check. Every released fastlane up to 2.240.1 behaves this way.

    Checked 2026-09-24: fastlane issue 22028

  • The error reported there, exportArchive exportOptionsPlist error for key method expected one {} but found ad-hoc, lists an empty set of accepted methods. A later comment reproduces the same empty set with app-store-connect, so the error means Xcode could not list any distribution methods for the archive, not that the old name was refused.

    Checked 2026-09-24: 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.

Is the ad-hoc export method still accepted?

Yes, with a deprecation warning. Apple renamed the command line name in Xcode 15.4, which logs IDEDistribution: Command line name ad-hoc is deprecated. Use release-testing instead. The same rename hit app-store, which warns Use app-store-connect instead. Current Xcode still exports with the old names: xcodebuild -help on Xcode 27.0 lists ad-hoc as deprecated in favour of release-testing, and an ad-hoc export logs the warning and carries on. Use release-testing in new configuration. If an export fails with exportOptionsPlist error for key method expected one {} but found ad-hoc, the empty set means Xcode could not list any distribution methods for the archive, usually a signing or account problem, and renaming the method will not fix it.

Does fastlane accept release-testing?

Not yet. Every released fastlane up to 2.240.1 validates gym's export_method against the old list (app-store, validation, ad-hoc, package, enterprise, development, developer-id and mac-application), so release-testing stops the lane with Unsupported export_method, as a comment on fastlane issue 22028 reports. A method passed inside export_options, or in a plist handed to gym, goes through the same check. With gym, either keep export_method on the old ad-hoc name, which Xcode still accepts with a deprecation warning, or archive with gym and skip_package_ipa, then export with xcodebuild -exportArchive and the generated plist. The fastlane block the generator prints does the second.

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.