Is it possible to detect at runtime that an application has been installed through TestFlight Beta (submitted through iTunes Connect) vs the App Store? You can submit a single app bundle and have it available through both. Is there an API that can detect which way it was installed? Or does the receipt contain information that allows this to be determined?
-
4Just to be clear you are talking about the new TestFlight beta testing through iTunes Connect? Or are you talking about when you have uploaded to TestFlight directly?keji– keji2014-09-28 04:59:33 +00:00Commented Sep 28, 2014 at 4:59
-
The new TestFlight beta, will clarifycombinatorial– combinatorial2014-09-28 15:13:47 +00:00Commented Sep 28, 2014 at 15:13
-
1Looks like -[NSString containsString:] is an ios8 addition. If the App Store auto testing tries to run it on ios7, no go. ([receiptURLString rangeOfString:@"sandboxReceipt"].location != NSNotFound) should do the trick.rgeorge– rgeorge2014-09-29 03:19:10 +00:00Commented Sep 29, 2014 at 3:19
-
2I was going to ask about detecting on iOS 6 which doesn't have appStoreReceiptURL, but it seems that the TestFlight app is iOS 8 only; so -[NSString containsString] might be fine after all. I've put app store beta testing on hold because of this, but I guess some people might be using a hybrid testing strategy, with Ad-Hoc for legacy testing and AppStore beta for public beta, so rangeOfString still wins.Gordon Dove– Gordon Dove2014-11-04 10:05:25 +00:00Commented Nov 4, 2014 at 10:05
-
In case if you are looking for a solution for macOS app you can refer this gist.github.com/lukaskubanek/cbfcab29c0c93e0e9e0a16ab09586996. This SO post appears at the top even if you google search for macOS, hence putting it hereKaunteya– Kaunteya2023-10-20 09:04:33 +00:00Commented Oct 20, 2023 at 9:04
5 Answers
For an application installed through TestFlight Beta the receipt file is named StoreKit/sandboxReceipt vs the usual StoreKit/receipt. Using [NSBundle appStoreReceiptURL] you can look for sandboxReceipt at the end of the URL.
NSURL *receiptURL = [[NSBundle mainBundle] appStoreReceiptURL];
NSString *receiptURLString = [receiptURL path];
BOOL isRunningTestFlightBeta = ([receiptURLString rangeOfString:@"sandboxReceipt"].location != NSNotFound);
Note that sandboxReceipt is also the name of the receipt file when running builds locally and for builds run in the simulator.
Swift Version:
let isTestFlight = Bundle.main.appStoreReceiptURL?.lastPathComponent == "sandboxReceipt"
Also note that this does not work for Mac catalyst app, their receipt url is /Applications/<app>/Contents/_MASReceipt/receipt even on TestFlight.
20 Comments
[[[[NSBundle mainBundle] appStoreReceiptURL] lastPathComponent] isEqualToString:@"sandboxReceipt"] (True if running TestFlight distributed binary) via Supertop/HaddadBased on combinatorial's answer I created the following SWIFT helper class. With this class you can determine if it's a debug, testflight or appstore build.
enum AppConfiguration {
case Debug
case TestFlight
case AppStore
}
struct Config {
// This is private because the use of 'appConfiguration' is preferred.
private static let isTestFlight = Bundle.main.appStoreReceiptURL?.lastPathComponent == "sandboxReceipt"
// This can be used to add debug statements.
static var isDebug: Bool {
#if DEBUG
return true
#else
return false
#endif
}
static var appConfiguration: AppConfiguration {
if isDebug {
return .Debug
} else if isTestFlight {
return .TestFlight
} else {
return .AppStore
}
}
}
We use these methods in our project to supply different tracking id's or connection string per environment:
func getURL(path: String) -> String {
switch (Config.appConfiguration) {
case .Debug:
return host + "://" + debugBaseUrl + path
default:
return host + "://" + baseUrl + path
}
}
OR:
static var trackingKey: String {
switch (Config.appConfiguration) {
case .Debug:
return debugKey
case .TestFlight:
return testflightKey
default:
return appstoreKey
}
}
UPDATE 05-02-2016: A prerequisite to use a preprocessor macro like #if DEBUG is to set some Swift Compiler Custom Flags. More information in this answer: https://stackoverflow.com/a/24112024/639227
Update 23-01-2024:
Note that this does not work for Mac catalyst app, their receipt url is /Applications/<app>/Contents/_MASReceipt/receipt even on TestFlight.
4 Comments
#if targetEnvironment(simulator) you determine whether you're running in a simulator. So I have the options Simulator/TestFlight/AppStore (which is in my case preferred over Debug) :-)Modern Swift version, which accounts for Simulators (based on accepted answer):
var isSimulatorOrTestFlight: Bool {
guard let path = Bundle.main.appStoreReceiptURL?.path else {
return false
}
return path.contains("CoreSimulator") || path.contains("sandboxReceipt")
}
Update 23-01-2024:
Note that this does not work for Mac catalyst app, their receipt url is /Applications/<app>/Contents/_MASReceipt/receipt even on TestFlight.
7 Comments
isTestFlight()I use extension Bundle+isProduction on Swift 5.2:
import Foundation
extension Bundle {
var isProduction: Bool {
#if DEBUG
return false
#else
guard let path = self.appStoreReceiptURL?.path else {
return true
}
return !path.contains("sandboxReceipt")
#endif
}
}
Then:
if Bundle.main.isProduction {
// do something
}
Comments
There is one way that I use it for my projects. Here are the steps.
In Xcode, go to the the project settings (project, not target) and add "beta" configuration to the list:
Then you need to create new scheme that will run project in "beta" configuration. To create scheme go here:
Name this scheme whatever you want. The you should edit settings for this scheme. To do this, tap here:
Select Archive tab where you can select Build configuration
Then you need to add a key Config with value $(CONFIGURATION) the projects info property list like this:
Then its just the matter what you need in code to do something specific to beta build:
let config = Bundle.main.object(forInfoDictionaryKey: "Config") as! String
if config == "Debug" {
// app running in debug configuration
}
else if config == "Release" {
// app running in release configuration
}
else if config == "Beta" {
// app running in beta configuration
}




