如何在运行时判断iOS应用是否通过TestFlight Beta安装运行


123

是否可以在运行时检测通过TestFlight Beta(通过iTunes Connect提交)还是通过App Store安装了应用程序?您可以提交一个应用程序捆绑包,并同时通过两个应用程序捆绑包使用。是否有可以检测安装方式的API?还是收据中包含可以确定这一点的信息?


4
为了清楚起见,您正在谈论通过iTunes Connect进行的新TestFlight beta测试?还是在谈论直接上传到TestFlight的时间?
keji 2014年

新的TestFlight测试版将予以澄清
组合

1
看起来-[NSString containsString:]是ios8的补充。如果App Store自动测试试图在ios7上运行它,那就不行了。([receiptURLString rangeOfString:@“ sandboxReceipt”]。location!= NSNotFound)应该可以解决问题。
rgeorge 2014年

@rgeorge谢谢,那是一个愚蠢的错误!
2014年

2
我本来要问有关在没有appStoreReceiptURL的iOS 6上进行检测的,但看来TestFlight应用程序仅是iOS 8。所以-[NSString containsString]毕竟可以。因此,我暂停了应用商店的Beta测试,但我想有些人可能正在使用混合测试策略,其中Ad-Hoc用于传统测试,AppStore beta用于公共Beta,因此rangeOfString仍然是成功的。
Gordon Dove

Answers:


117

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

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

请注意,sandboxReceipt当在本地运行内部版本以及在模拟器中运行内部版本时,这也是收据文件的名称。


7
如前所述,这适用于在设备上进行本地测试,但不适用于模拟器。我添加了类似#if的内容:TARGET_IPHONE_SIMULATOR isRunningInTestMode = YES; #endif显然,这需要#import <TargetConditionals.h>
Gordon Dove 2014年

13
精简版:([[[[NSBundle mainBundle] appStoreReceiptURL] lastPathComponent] isEqualToString:@"sandboxReceipt"]如果运行TestFlight分布式二进制,则为
Nick

2
由于收据仅存在于主机捆绑包中,因此无法在扩展捆绑包中使用此方法。
jeeeyul 2015年

2
StoreKit/sandboxReceipt通过设备或模拟器上的Xcode作为调试版本安装时,我的iOS 8测试结果。因此,这可能无法准确区分testflight版本和所有其他版本。
pkamb

3
在使用Ad Hoc发行版安装构建时,似乎也返回YES。
凯勒2015年

75

根据组合答案,我创建了以下SWIFT帮助器类。使用此类,您可以确定它是调试,testflight还是appstore构建。

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 : //stackoverflow.com/a/24112024/639227


1
@Urkman确保您正在设置-D DEBUG标志。可以在此处找到更多信息。
迦勒

Thnx @Caleb,我在答案的前提条件中添加了更多说明。
LorenzoValentijn

1
感谢您的回答,我发现它非常有帮助!还很高兴知道,#if targetEnvironment(simulator)您可以使用它来确定您是否正在模拟器中运行。所以我有Simulator / TestFlight / AppStore选项(在我的情况下,它比首选Debug):-)
JeroenJK

39

现代Swift版本,说明了模拟器(基于公认的答案):

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

很高兴包含模拟器,但是您可能想更改函数名称,因为它在所有情况下都不再适用。
dbn

2
哇!有用!太棒了!对于同一构建,TestFlight返回TRUE,AppStore返回FALSE(对于一个构建,在一个方案中构建一个构建,并提供一个配置)。完善!谢谢!
阿格斯

@dbn您能解释为什么在所有情况下都不再适用吗?
伊桑(Ethan)

1
@Ethan我发表评论后编辑了此答案;方法名称曾经是isTestFlight()
dbn19年


2

Bundle+isProduction在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
    }
}

然后:

if Bundle.main.isProduction {
    // do something
}

-3

我将其用于项目的一种方式。步骤如下。

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

在此处输入图片说明



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

在此处输入图片说明



可以根据需要命名此方案。您应该编辑此方案的设置。为此,请点击此处:

在此处输入图片说明



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

在此处输入图片说明



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

在此处输入图片说明



然后,这就是您需要在代码中执行特定于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
}

6
虽然这是一种有用的技术,但不能回答问题。单个二进制文件将提交到App Store,并且可以通过通过TestFlight下载进行运行,也可以在从App Store下载后经过批准的运行之后再运行。问题是关于检测哪个版本正在运行。
组合

是否可以选择首先制作2个档案。一个用于testflight,一个用于应用商店。
Klemen

有可能,但是它们必须具有不同的内部版本号。这意味着要管理两个构建而不是一个。
组合

好的,我认为这是值得的。特别是如果您使用持续集成工具。
Klemen

@KlemenZagar,您的方法是一种众所周知的好方法,但它不能回答问题。
Stanislav Pankevich '17
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.