Skip to main content
Tutorials

TestFlight Beta Testing on Windows: Add TestFlight Testers

Last updated

TestFlight beta testing is Apple’s official way to distribute beta builds. Testers install your beta build through the TestFlight app on their iPhone, and you don’t have to register their UDIDs. The flow is to build a Release .ipa with an App Store provisioning profile, upload it to App Store Connect, answer the export compliance question once processing finishes, then hand the build to internal testers (up to 100, no review) or external testers (up to 10,000, the first build needs TestFlight App Review). The steps below walk through it on Windows.

Before you start, have these ready.

  • An Apple Developer Program membership, with an app record already created in App Store Connect. If you haven’t done that yet, read the first three sections of Publish to the App Store on Windows.
  • An Apple Distribution certificate (.p12) and an App Store provisioning profile (.mobileprovision) in the keychain folder in the project root. See Certificates and signing for how to get them.
  • A project that builds with KXBuild.

What does the TestFlight beta testing flow look like?

Step Where How long
1. Build a Release .ipa VS Code or the command line A few minutes
2. Upload the .ipa Transporter (Mac) or Appuploader (Windows) A few minutes
3. Build processing Done by Apple A few minutes to tens of minutes
4. Export compliance The TestFlight tab in App Store Connect, or in Info.plist ahead of time Immediate
5. Internal testing Internal Testing on the TestFlight tab Installable once processing finishes
6. External testing External Testing on the TestFlight tab The first build waits for TestFlight App Review
7. Collect feedback The TestFlight feedback pages and kxdevice During testing

How do you build a Release .ipa for TestFlight?

TestFlight only accepts builds signed with an App Store provisioning profile. A build signed with an Ad Hoc or Development profile may upload, but testers can’t install it.

Bump the build number

Within one version number, each upload needs a higher build number (CFBundleVersion) than the last one. Increase it on the General page of the Project Settings Editor, or override it for one build with -D and leave the project file alone.

1kxbuild build .\MyApp.xcodeproj -c Release --export app-store -D CURRENT_PROJECT_VERSION=12

The -D override only works when CFBundleVersion in Info.plist is set to $(CURRENT_PROJECT_VERSION), which is the default for projects created in Xcode 13 or later. To generate build numbers automatically in CI, see Auto-increment build numbers.

Build

Use either of these.

  • VS Code extension. Run KXBuild: Build for App Store (.ipa) from the Command Palette or the project’s context menu.
  • Command line. Run this in the project folder. If the project uses CocoaPods, pass the .xcworkspace in place of the .xcodeproj.
1kxbuild build .\MyApp.xcodeproj -c Release --export app-store

The build writes build\MyApp.ipa in the project folder. --export app-store signs with the Apple Distribution certificate and App Store provisioning profile in keychain, whatever the project’s signing settings say. If it reports that no App Store profile was found, put the App Store .mobileprovision in keychain, along with the Distribution .p12 and its password in password.properties.

If the keychain folder has both an Ad Hoc and an App Store profile with the same bundle ID, keep both. --export app-store uses only the App Store profile, and --export ad-hoc uses only the Ad Hoc one, so there’s nothing else to set.

How do you upload the .ipa to App Store Connect?

On a Mac, drag the .ipa into Apple’s Transporter app. On Windows, open Appuploader, sign in with your developer account, select build\MyApp.ipa on the upload page and wait for the progress to finish. See Release and App Store submission for details.

During upload Apple checks the signature, the provisioning profile, the icons and Info.plist. A build that fails a check is rejected with the reason. These three are the most common.

Error Cause Fix
The build number has already been used CFBundleVersion isn’t higher than the last upload Increase the build number and build again
Wrong provisioning profile type Signed with an Ad Hoc or Development profile Use an App Store profile
Missing icon AppIcon has no 1024×1024 image Add it in the Asset Catalog Editor

To upload from a pipeline, use an App Store Connect API key with command-line Transporter. See Upload .ipa files with an API key.

Why answer export compliance after uploading?

After a successful upload Apple processes the build, usually for a few minutes to tens of minutes, and sends you an email when it’s done. Open the app in App Store Connect and go to the TestFlight tab. The build appears in the iOS builds list.

Answer the export compliance question

While a build shows Missing Compliance, testers can’t install it. Click Manage next to the build and answer the encryption questions. If the app only uses HTTPS and the encryption built into the system, answer that it doesn’t use non-exempt encryption, as long as that’s true. If you implemented your own encryption algorithm, or you aren’t sure whether your use is exempt, follow Apple’s overview of export compliance and upload compliance documentation if required.

Put the answer in Info.plist

Answering for every build gets tedious. Once you’ve confirmed the app doesn’t use non-exempt encryption, add a Boolean key ITSAppUsesNonExemptEncryption set to NO in Info.plist, and future builds won’t ask. See Apple’s ITSAppUsesNonExemptEncryption reference for the key.

If the project has an Info.plist file, open it in the Property List Editor, right-click the root dictionary and choose Add Row, enter ITSAppUsesNonExemptEncryption as the key, set the type to Boolean and the value to NO, then press Ctrl+S. The saved XML looks like this.

1<key>ITSAppUsesNonExemptEncryption</key>
2<false/>

Projects created in Xcode 13 or later usually have no Info.plist file. Create Config\Info.plist with just this key and point the target’s Info.plist File setting at it. The build merges it with the generated content.

1<?xml version="1.0" encoding="UTF-8"?>
2<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
3<plist version="1.0">
4<dict>
5	<key>ITSAppUsesNonExemptEncryption</key>
6	<false/>
7</dict>
8</plist>
1kxbuild project add-setting -k INFOPLIST_FILE -v Config/Info.plist -t MyApp -P .\MyApp.xcodeproj

Set this value truthfully. If the app does use non-exempt encryption and you declare NO, you’re responsible for that, and Apple may ask for more documentation.

How do you add internal TestFlight testers?

Internal testers must be members of your App Store Connect team, up to 100 per app. They can install as soon as the build is processed and compliance is answered, with no review, which suits developers and QA testing their own work.

Add testers to your team first

Invite your colleagues in Users and Access in App Store Connect. They become team members only after they accept. People who aren’t team members can’t join an internal group and have to be external testers.

Create an internal group

  1. In Apps in App Store Connect, select your app and open the TestFlight tab.
  2. In the sidebar, click + next to Internal Testing, enter a group name such as “Dev team”, and click Create.
  3. Add testers to the group by selecting team members.
  4. Add the build you just processed under the group’s Builds.

When you create the group you can turn on automatic distribution, so every new build goes to everyone in the group. Without it, you add each new build to the group by hand.

You need the Account Holder, Admin, App Manager, Developer or Marketing role for this. Apple’s Add internal testers is the reference for the exact steps.

How do you invite external testers?

External testers can be anyone, invited by email or through a public link, up to 10,000 per app. Use it for clients, early users and colleagues outside the development team.

Fill in the test information

On the TestFlight tab, open Test Information under Additional Information in the sidebar, choose a language and fill in these two fields.

  • Beta App Description, required, tells testers what the app does.
  • Feedback Email, the address testers reach you at from TestFlight, also used as the reply address for invitation emails.

If the app requires sign-in, provide a test account in the Beta App Review information. As with a regular App Store submission, the build is sent back if the reviewer can’t sign in.

Create an external group and submit for review

  1. You need at least one internal group before you can create an external group.
  2. In the sidebar, click + next to External Testing, enter a group name and click Create.
  3. Select the group, click Add Builds, choose the version and build, and describe what to test in What to Test. Testers see this text in TestFlight.
  4. Click Submit for Review.

The first build of each version must pass TestFlight App Review before testing starts. Later builds of the same version don’t always need a full review. Each version can have only one build in review at a time, and you can submit up to 6 builds in 24 hours. If a build is rejected, its status shows as rejected. Read the reason under App Review in the sidebar, fix the issue, bump the build number and upload again.

Invite testers

You can use both methods at the same time.

  • Email invitations. Select the group, click + next to Testers, and enter email addresses one by one or import a CSV file. Testers accept the invitation from the email in TestFlight.
  • Public link. On the group’s Testers tab, click Create Public Link. Open it to anyone, or set criteria by device and OS version, and optionally set a tester limit from 1 to 10,000. Copy the link and share it in a chat group or on your website.

Anyone who has a public link can forward it, and testers who join through it show up as anonymous in the list. When you don’t want new testers any more, turn the link off under Manage next to Public Link. See Apple’s Invite external testers for the full steps.

Internal or external testing: which one?

Internal testing External testing
Tester limit 100 10,000
Who can join App Store Connect team members Anyone
How to invite Add from team members Email, CSV import, public link
Review Not required The first build of each version needs TestFlight App Review
When testing starts As soon as the build is processed After review passes
Build lifetime 90 days 90 days
Good for Daily testing by developers and QA Client acceptance, recruiting public testers

A common setup is to use both. Send every build to the internal group first, and once there are no obvious problems, add the same build to the external group.

How do testers install the beta?

Send these steps to your testers.

  1. Open the App Store on the iPhone, search for TestFlight and install the free app.
  2. Open the invitation from the email or public link, and tap Accept in TestFlight.
  3. Tap Install. The app appears on the Home Screen.
  4. TestFlight notifies them when a new build is available. They tap Update.

A TestFlight beta has the same bundle ID as the App Store version, so one replaces the other. If a tester has the App Store version installed, the beta replaces it. After testing they can reinstall the App Store version.

Builds expire after 90 days

Each build can be tested for up to 90 days after upload. After that, testers can’t open it, as described in Apple’s TestFlight overview. For long test cycles, upload a new build before the old one expires. Bump the build number, build, upload and add it to the test groups, and testers update in TestFlight to keep going.

How do you collect feedback and crashes?

Tester feedback

Testers can send feedback from your app’s page in the TestFlight app, or take a screenshot in the beta and choose to share it with the developer. Feedback is collected in the Feedback section of the TestFlight tab in App Store Connect. Testers on older OS versions that can’t send in-app feedback email it to the feedback address you entered in the test information.

Crashes

The TestFlight tab shows sessions and crashes for each build. Start by checking which build the crashes concentrate on.

If the tester is nearby, connect their iPhone to your computer and pull the crash reports, which include the failing function and the call stack.

1kxdevice crash pull .\crashes MyApp*

See the kxdevice command reference for usage. For how to read crash reports and reproduce the crash on the device, see Debug iOS apps on Windows with LLDB.

When TestFlight beta testing is done, select this build on the App Store page in App Store Connect and submit it for App Review. The remaining steps are in Publish to the App Store on Windows.

FAQ

How many testers can I invite to TestFlight?

As of October 2026, each app can have up to 100 internal testers, who must be App Store Connect team members, and up to 10,000 external testers, who can be anyone. You can use both at the same time.

Why don’t I see my build in TestFlight after a successful upload?

Apple has to process the build first, which usually takes a few minutes to tens of minutes, and you get an email when it’s done. If it then shows Missing Compliance, answer the export compliance question before you can distribute it to testers.

How long does TestFlight external review take?

The first build of each version goes through TestFlight App Review. Apple doesn’t commit to a specific time, and results usually arrive within a day. Later builds of the same version don’t always need a full review, and internal testing needs no review at all.

What do I do when a TestFlight build expires?

Each build can be tested for up to 90 days, and that can’t be extended. Bump the build number, build and upload again, then add the new build to your test groups. Testers update in TestFlight to keep testing.

Can I upload an Ad Hoc build to TestFlight?

Not for testing. TestFlight requires a build signed with an App Store provisioning profile, and testers can’t install a build signed with an Ad Hoc or Development profile even if it uploads. Put the App Store profile in the keychain folder and build again with -c Release --export app-store.