如何在运行时判断 iOS 应用程序是不是正在通过 TestFlight Beta 安装运行

Posted

技术标签:

【中文标题】如何在运行时判断 iOS 应用程序是不是正在通过 TestFlight Beta 安装运行【英文标题】:How to tell at runtime whether an iOS app is running through a TestFlight Beta install如何在运行时判断 iOS 应用程序是否正在通过 TestFlight Beta 安装运行 【发布时间】:2014-11-22 18:17:57 【问题描述】:

是否可以在运行时检测到应用程序已通过 TestFlight Beta(通过 iTunes Connect 提交)而不是 App Store 安装?您可以提交单个应用程序包并通过两者提供。是否有可以检测其安装方式的 API?或者收据是否包含可以确定这一点的信息?

【问题讨论】:

为了清楚起见,您是在谈论通过 iTunes Connect 进行的新 TestFlight beta 测试?还是说你是直接上传到TestFlight的时候? 新的TestFlight beta,将澄清 看起来 -[NSString containsString:] 是 ios8 添加的。如果 App Store 自动测试尝试在 ios7 上运行它,那就不行了。 ([receiptURLString rangeOfString:@"sandboxReceipt"].location != NSNotFound) 应该可以解决问题。 @rgeorge 谢谢,这是一个愚蠢的错误! 我想问一下在没有 appStoreReceiptURL 的 iOS 6 上检测,但似乎 TestFlight 应用程序仅适用于 iOS 8;所以 -[NSString containsString] 毕竟可能没问题。因此,我已暂停应用商店 beta 测试,但我猜有些人可能会使用混合测试策略,即 Ad-Hoc 用于旧版测试,而 AppStore beta 用于公共测试版,因此 rangeOfString 仍然胜出。 【参考方案1】:

对于通过 TestFlight Beta 安装的应用程序,收据文件被命名为 StoreKit\sandboxReceipt,而不是通常的 StoreKit\receipt。使用[NSBundle appStoreReceiptURL],您可以在 URL 的末尾查找 sandboxReceipt。

NSURL *receiptURL = [[NSBundle mainBundle] appStoreReceiptURL];
NSString *receiptURLString = [receiptURL path];
BOOL isRunningTestFlightBeta =  ([receiptURLString rangeOfString:@"sandboxReceipt"].location != NSNotFound);

请注意,sandboxReceipt 也是在本地运行构建和在模拟器中运行构建时收据文件的名称。

Swift 版本:

let isTestFlight = Bundle.main.appStoreReceiptURL?.lastPathComponent == "sandboxReceipt"

【讨论】:

如前所述,这适用于设备上的本地测试,但不适用于模拟器。我添加了类似#if TARGET_IPHONE_SIMULATOR isRunningInTestMode = YES; #endif 显然,这需要#import 精简版:[[[[NSBundle mainBundle] appStoreReceiptURL] lastPathComponent] isEqualToString:@"sandboxReceipt"](如果运行 TestFlight 分布式二进制文件则为真)通过Supertop/Haddad 此方法不能用于扩展包,因为收据只存在于主机包中。 在使用 Ad Hoc 分发安装构建时似乎也返回 YES。 在 6 年后的 2020 年按预期工作。【参考方案2】:

基于combinatorial's answer,我创建了以下 SWIFT 帮助程序类。使用此类,您可以确定它是调试、测试飞行还是应用商店构建。

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
    
  

我们在项目中使用这些方法为每个环境提供不同的跟踪 ID连接字符串

  func getURL(path: String) -> String     
    switch (Config.appConfiguration) 
    case .Debug:
      return host + "://" + debugBaseUrl + path
    default:
      return host + "://" + baseUrl + path
    
  

或者:

  static var trackingKey: String 
    switch (Config.appConfiguration) 
    case .Debug:
      return debugKey
    case .TestFlight:
      return testflightKey
    default:
      return appstoreKey
    
  

2016 年 5 月 2 日更新: 使用 #if DEBUG 之类的预处理器宏的先决条件是设置一些 Swift 编译器自定义标志。此答案中的更多信息:https://***.com/a/24112024/639227

【讨论】:

@Urkman 确保您设置了-D DEBUG 标志。更多信息可以在here找到。 Thnx @Caleb,我对答案的先决条件添加了更多解释。 感谢您的回答,我发现它很有帮助!也很高兴知道,使用#if targetEnvironment(simulator) 可以确定您是否在模拟器中运行。所以我有 Simulator/TestFlight/AppStore 选项(在我的情况下,这比 Debug 更受欢迎):-)【参考方案3】:

现代 Swift 版本,用于模拟器(基于接受的答案):

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

【讨论】:

很高兴包含模拟器,但您可能需要更改函数名称,因为它不再适用于所有情况。 哇!有用!惊人的!对于同一构建(在一个方案中构建的一个构建,具有一个配置),对于 TestFlight 返回 TRUE,对于 AppStore 返回 FALSE。完美的!谢谢! @dbn 你能详细说明为什么这不再适用于所有情况吗? @Ethan 这个答案是在我发表评论后编辑的;方法名称以前是isTestFlight()【参考方案4】:

我在 Swift 5.2 上使用扩展名 Bundle+isProduction

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
    

然后:

if Bundle.main.isProduction 
    // do something

【讨论】:

【参考方案5】:

更新

这不再起作用了。使用其他方法。

原答案

这也有效:

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

发现于Detect if iOS App is Downloaded from Apple's Testflight

【讨论】:

可以用这个方法查看应用是在review还是live吗?【参考方案6】:

我有一种方法可以将它用于我的项目。步骤如下。

在 Xcode 中,转到项目设置(项目,而不是目标)并将“beta”配置添加到列表中:

然后你需要创建一个新的方案,它将在“beta”配置中运行项目。要创建方案,请转到此处:

将此方案命名为您想要的任何名称。您应该编辑此方案的设置。为此,请点按此处:

选择存档选项卡,您可以在其中选择Build configuration

然后您需要在项目信息属性列表中添加一个值为$(CONFIGURATION) 的键Config,如下所示:

然后,您需要在代码中执行特定于 beta 构建的操作,这就是问题所在:

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

【讨论】:

虽然这是一种有用的技术,但它并不能回答问题。单个二进制文件被提交到 App Store,可以通过 TestFlight 下载运行,也可以在从 App Store 下载后批准运行后运行。问题是关于检测哪个版本正在运行。 是否可以选择首先制作 2 个存档。一个用于测试,一个用于应用商店。 这是可能的,但它们必须具有不同的内部版本号。这意味着管理两个构建而不是一个。 好的,在我看来这是值得的。特别是如果您使用持续集成工具。 @KlemenZagar,您的方法是众所周知的好方法,但它没有回答问题。

以上是关于如何在运行时判断 iOS 应用程序是不是正在通过 TestFlight Beta 安装运行的主要内容,如果未能解决你的问题,请参考以下文章

如何判断通过 heroku cli 运行命令是不是成功?

如何判断 CLLocationManager 是不是正在主动扫描信标?

如何判断文件是不是已通过 NSFileManager ios 上传

如何在vc++中判断计算机是不是安装c运行时库

运行时判断程序是不是在Wine下运行

如何判断ios中是不是安装了某个应用