Detect TestFlight vs App Store Builds in Swift
Runtime detection to identify if your app is running from TestFlight, App Store, Debug, or Simulator.
Detect TestFlight vs App Store Builds in Swift
Why Detect Build Environment?
Knowing where your app is running helps you:
- Enable beta features only for TestFlight users
- Add debug logging in development builds
- Show different onboarding for beta testers
- Track analytics separately for each environment
The Solution: Runtime Detection
TestFlight builds have a sandbox receipt, while App Store releases have a production receipt. We can check this at runtime to determine the build environment.
Complete Implementation
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
import Foundation
enum AppEnvironment: String {
case debug
case simulator
case testFlight
case appStore
}
enum AppEnv {
static var current: AppEnvironment {
// Simulator first (useful even if Release simulator)
#if targetEnvironment(simulator)
return .simulator
#endif
// Debug builds from Xcode
#if DEBUG
return .debug
#endif
// Runtime check: TestFlight builds have a sandbox receipt
if hasSandboxReceipt {
return .testFlight
}
// Otherwise it's a production App Store install
return .appStore
}
static var isTestFlight: Bool { current == .testFlight }
static var isAppStore: Bool { current == .appStore }
// MARK: - Helpers
private static var hasSandboxReceipt: Bool {
guard let url = Bundle.main.appStoreReceiptURL else { return false }
// TestFlight and StoreKit sandbox receipts end with "sandboxReceipt"
return url.lastPathComponent == "sandboxReceipt" ||
url.path.contains("sandboxReceipt")
}
}
Usage Examples
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
// Check current environment
switch AppEnv.current {
case .debug:
print("Running Debug from Xcode")
enableDebugMenu()
case .simulator:
print("Running in Simulator")
loadMockData()
case .testFlight:
print("Running TestFlight Beta")
showBetaFeatures()
enableCrashReporting()
case .appStore:
print("Running App Store Release")
// Production behavior
}
// Quick checks for specific environments
if AppEnv.isTestFlight {
// Show beta feedback button
feedbackButton.isHidden = false
}
if AppEnv.isAppStore {
// Disable all debug features
debugMode = false
}
How It Works
- Simulator Detection: Uses
#if targetEnvironment(simulator)compiler directive - Debug Detection: Uses
#if DEBUGcompiler flag set by Xcode - TestFlight Detection: Checks if the app receipt URL contains “sandboxReceipt”
- App Store: Default case when none of the above conditions are met
TestFlight builds are re-signed by Apple with a sandbox receipt, while App Store releases have a production receipt. This makes the detection reliable.
Alternative: Build-Time Configuration
If you prefer compile-time detection without runtime checks:
- Create a “Beta” build configuration for TestFlight uploads
- Add Swift Compiler Flag:
-DTESTFLIGHTto that configuration - Use conditional compilation:
1
2
3
4
#if TESTFLIGHT
// TestFlight-only code
showBetaWelcomeScreen()
#endif
Important Notes
- Don’t use
embedded.mobileprovisionfor detection—it’s unreliable - The sandbox receipt check works for both TestFlight and StoreKit testing
- Cache the environment check result to avoid repeated file system access
- This detection works on iOS 8.0+
☕ Support My Work
If you found this post helpful and want to support more content like this, you can buy me a coffee!
Your support helps me continue creating useful articles and tips for fellow developers. Thank you! 🙏
This post is licensed under CC BY 4.0 by the author.