AppForShare

ExportOptions.plist in 2026: release-testing, ad-hoc and fastlane gym

· 6 min read

ExportOptions.plist in 2026: release-testing, ad-hoc and fastlane gym

Every command line iOS build ends with the same step: xcodebuild -exportArchive turns an .xcarchive into an .ipa, and it needs an ExportOptions.plist to know how. The file is short, but one wrong value gives you an App Store build when you wanted one testers can install from a link, or an export that fails with a signing error at the end of a long CI run.

This guide covers the keys that matter, the method names Apple changed in Xcode 15.3, and how fastlane gym fits in. Facts were checked on November 9, 2026 against the xcodebuild -help text and the fastlane gym docs.

The method names changed in Xcode 15.3

Starting with Xcode 15.3, Apple renamed three of the export methods. Enterprise kept its name.

Before Xcode 15.3Xcode 15.3 and laterWhat you get
ad-hocrelease-testingAn Ad Hoc signed IPA that installs on registered devices from a link
app-storeapp-store-connectA build for TestFlight and the App Store; it does not install from a link
developmentdebuggingA Development signed IPA for registered devices, debugger allowed
enterpriseenterpriseIn house distribution for Apple Developer Enterprise Program members

The xcodebuild -help text lists the old names as deprecated, with the new name to use, rather than removed. So older plists with ad-hoc generally still export, with a deprecation message in the log, but a deprecated value can be removed in a later Xcode. For a new file, use the new names. Some CI steps that validate the value themselves were slow to accept the new names, so if a CI tool rejects release-testing, that check is in the tool, not in Xcode.

The keys you will actually use

KeyWhat it does
methodHow the archive is signed and packaged. See the table above.
teamIDYour 10 character Apple team ID. Needed when your Mac or CI signs for more than one team.
signingStyleautomatic lets Xcode pick or create the certificate and profile; manual uses exactly what you list.
provisioningProfilesManual signing only: a dictionary of bundle ID to profile name or UUID. One entry per target, including every app extension.
signingCertificateManual signing: a certificate name such as Apple Distribution or Apple Development, or its SHA-1.
destinationexport writes the IPA to disk; upload sends it straight to App Store Connect.
uploadSymbolsApp Store Connect builds: include dSYM symbols so Apple can symbolicate crash reports.
manageAppVersionAndBuildNumberApp Store Connect builds: lets Xcode change the build number on upload. Set it to false if your pipeline sets build numbers.
testFlightInternalTestingOnlyMarks the upload for internal TestFlight testers only.
stripSwiftSymbolsRemoves Swift symbols from the binary to make it smaller.

Older guides also mention compileBitcode and uploadBitcode. Apple deprecated bitcode in Xcode 14, so you can leave both out. Run xcodebuild -help on your own Mac for the full list your Xcode supports.

Example: a build testers install from a link

This is the file to use when the IPA goes to testers through an install link. It signs manually with an Ad Hoc profile for the app and one for its widget extension.

<?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>teamID</key>
  <string>ABCDE12345</string>
  <key>signingStyle</key>
  <string>manual</string>
  <key>signingCertificate</key>
  <string>Apple Distribution</string>
  <key>provisioningProfiles</key>
  <dict>
    <key>com.example.myapp</key>
    <string>MyApp Ad Hoc</string>
    <key>com.example.myapp.widget</key>
    <string>MyApp Widget Ad Hoc</string>
  </dict>
  <key>stripSwiftSymbols</key>
  <true/>
</dict>
</plist>

The profile names must match profiles installed on the build machine, and each must include the devices of your testers. If you prefer automatic signing, drop signingCertificate and provisioningProfiles, set signingStyle to automatic, and pass -allowProvisioningUpdates to xcodebuild so it can create or update profiles.

Example: upload to App Store Connect

<?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>app-store-connect</string>
  <key>teamID</key>
  <string>ABCDE12345</string>
  <key>signingStyle</key>
  <string>automatic</string>
  <key>destination</key>
  <string>upload</string>
  <key>uploadSymbols</key>
  <true/>
  <key>manageAppVersionAndBuildNumber</key>
  <false/>
  <key>testFlightInternalTestingOnly</key>
  <false/>
</dict>
</plist>

With destination set to upload, the export sends the build to App Store Connect instead of writing it out. On CI, pass an App Store Connect API key with -authenticationKeyPath, -authenticationKeyID and -authenticationKeyIssuerID. Remember that this build cannot be installed from a link. Our walkthrough on building iOS on GitHub Actions for TestFlight shows the full job.

The two xcodebuild commands

xcodebuild -workspace MyApp.xcworkspace -scheme MyApp -configuration Release \
  -destination 'generic/platform=iOS' -archivePath build/MyApp.xcarchive archive

xcodebuild -exportArchive -archivePath build/MyApp.xcarchive \
  -exportOptionsPlist ExportOptions.plist -exportPath build/export \
  -allowProvisioningUpdates

Leave out -allowProvisioningUpdates with manual signing if you do not want Xcode to touch your profiles.

What fastlane gym expects

fastlane's gym action writes the export options for you. Its export_method parameter documents the classic names: app-store, validation, ad-hoc, package, enterprise, development, developer-id and mac-application. So in a Fastfile you still write ad-hoc:

gym(
  scheme: "MyApp",
  export_method: "ad-hoc",
  export_options: {
    provisioningProfiles: {
      "com.example.myapp" => "MyApp Ad Hoc",
      "com.example.myapp.widget" => "MyApp Widget Ad Hoc"
    }
  }
)

export_options also takes a path to a plist, if you want one file for both fastlane and plain xcodebuild. In that case keep export_method in the Fastfile and the profile map in the plist.

Common export errors

  • "No profiles for 'com.example.myapp' were found". The bundle ID in provisioningProfiles does not match the target, or the profile is not installed on the machine. Check extensions too.
  • "Provisioning profile doesn't include signing certificate". The profile was made for a different certificate than the one in the keychain. Regenerate the profile or import the right certificate.
  • The IPA exports, but testers cannot install it. Usually the method was app-store-connect, or the tester's device is not in the Ad Hoc profile. Our post on iOS code signing errors covers each case.

Generate the file instead of typing it

Our free ExportOptions.plist generator builds the file from a short form: method (new names, or the pre Xcode 15.3 names), team ID, automatic or manual signing with a profile per bundle ID and a certificate, the App Store Connect options and Swift symbol stripping. It shows the matching xcodebuild archive and export lines and the fastlane gym call with the classic method name, and you can download or copy the result. It runs in your browser and nothing is sent. More free tools are on the free build tools page.

Common questions

Should I use release-testing or ad-hoc?

For plain xcodebuild on Xcode 15.3 or later, use release-testing. For fastlane gym's export_method, use ad-hoc, the name its docs list. Both produce an Ad Hoc signed IPA.

Do I need a separate profile for each app extension?

With manual signing, yes. Every target with its own bundle ID needs an entry in provisioningProfiles.

Why is my exported IPA not installable from a link?

Check the method first. An app-store-connect export only installs through TestFlight or the App Store. Use release-testing or debugging with the tester's device in the profile.

Is the generator's output sent anywhere?

No. The plist is built in your browser.

Once your export produces an Ad Hoc IPA, you can share it with testers in a minute. Create a free account to get an install link and a QR code for it.

Keep reading

App Distribution Platform · · 5 min read

AppForShare File Size and Upload Limits

AppForShare limits the size of each build you upload by plan: 50 MB without an account, 150 MB on the Free plan, 2 GB on Pro and 4 GB on Team. Accounts also have a storage total, and every plan...