Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to tell at runtime whether an iOS app is running through a TestFlight Beta install

Tags:

ios

testflight

People also ask

How long does it take to get an app on TestFlight?

App Store Connect adds your app to the review queue and changes its status to Waiting for Review. To get approval, your build must comply with the App Store Review Guidelines. Approval usually takes no more than 48 hours.

How do you test iOS beta apps?

Open your email invitation or tap the public link on your iOS or iPadOS device. Tap View in TestFlight or Start Testing; or tap Install or Update for the app you want to test. If you're testing an iOS app that includes an iMessage app, launch the beta app from the Home Screen.

How do I test TestFlight app?

Download TestFlight app and start testing Invited testers will receive an email with a link to test your app. To be able to test, they will first have to download the TestFlight app. After accepting the invitation, they will be directed to the TestFlight app and they will be prompted to install to app to be tested.

Do TestFlight apps expire?

Every new release in TestFlight expires after 90 days (3 months). This will never be a problem as we're committed to pushing regular updates - generally every month.


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.


Based 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


Modern Swift version, which accounts for Simulators (based on accepted answer):

private func isSimulatorOrTestFlight() -> Bool {
    guard let path = Bundle.main.appStoreReceiptURL?.path else {
        return false
    }
    return path.contains("CoreSimulator") || path.contains("sandboxReceipt")
}

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
}

Update

This does not work any more. Use other method.

Original answer

This also works:

if NSBundle.mainBundle().pathForResource("embedded", ofType: "mobileprovision") != nil {
    // TestFlight
} else {
    // App Store (and Apple reviewers too)
}

Found in Detect if iOS App is Downloaded from Apple's Testflight