実行時にiOSアプリがTestFlight Betaインストールを介して実行されているかどうかを確認する方法


122

App StoreとTestFlight Beta(iTunes Connect経由で送信)でアプリケーションがインストールされたことを実行時に検出できますか?単一のApp Bundleを送信して、両方から利用できるようにすることができます。インストールされた方法を検出できるAPIはありますか?または、レシートにこれを判断できる情報が含まれていますか?


4
明確にするために、iTunes Connectを介した新しいTestFlightベータテストについて話しているのですか?または、TestFlightに直接アップロードしたときのことですか?
keji 2014

新しいTestFlightベータが明確になる
コンビナトリアル

1
-[NSString containsString:]はios8の追加です。App Storeの自動テストがios7で実行しようとすると、失敗します。([receiptURLString rangeOfString:@ "sandboxReceipt"]。location!= NSNotFound)がうまくいくはずです。
rgeorge 2014

@rgeorgeありがとう、それはばかげた間違いでした!
コンビナトリアル

2
appStoreReceiptURLがないiOS 6での検出について質問しましたが、TestFlightアプリはiOS 8のみのようです。つまり、-[NSString containsString]は結局は問題ないかもしれません。このため、App Storeのベータテストを保留にしましたが、レガシーテストにはアドホック、パブリックベータにはAppStoreベータを使用するハイブリッドテスト戦略を使用している人もいるので、rangeOfStringが引き続き有効です。
Gordon Dove

回答:


117

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ビルドをローカルで実行するとき、およびビルドをシミュレーターで実行するときのレシートファイルの名前にも注意してください。


7
前述のように、これはデバイスのローカルテストでは機能しますが、シミュレータでは機能しません。#if TARGET_IPHONE_SIMULATOR isRunningInTestMode = YES;のようなものを追加しました。#endif明らかに、これには#import <TargetConditionals.h>が必要です
Gordon Dove

13
コンパクトバージョン:[[[[NSBundle mainBundle] appStoreReceiptURL] lastPathComponent] isEqualToString:@"sandboxReceipt"](TestFlight分散バイナリを実行している場合は真)Supertop / Haddad
Nick

2
受信はホストバンドルに対してのみ存在するため、このメソッドは拡張バンドルでは使用できません。
jeeeyul 2015

2
iOS 8テストではStoreKit/sandboxReceipt、デバイスまたはシミュレーターのXcodeを介してデバッグビルドとしてインストールすると、したがって、これはtestflightビルドを他のすべてのビルドと正確に区別しない場合があります。
pkamb 2015

3
アドホックディストリビューションでビルドをインストールするときにもYESを返すようです。
ケラー

74

Combinatorial の回答に基づいて、次の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
    }
  }

UPDATE 05-02-2016: #if DEBUGのようなプリプロセッサマクロを使用するための前提条件は、Swiftコンパイラのカスタムフラグを設定することです。この回答の詳細:https : //stackoverflow.com/a/24112024/639227


1
@Urkman -D DEBUGフラグを設定していることを確認してください。詳細については、こちらをご覧ください
カレブ2016

Thnx @Caleb、私は答えの前提条件についてさらに説明を追加しました。
LorenzoValentijn 2016

1
回答ありがとうございます。とても役に立ちました。また、使用#if targetEnvironment(simulator)すると、シミュレータで実行しているかどうかがわかります。だから私はオプションSimulator / TestFlight / AppStoreを持っています(私の場合はそれよりも優先されますDebug):-)
JeroenJK

38

シミュレータを説明する最新のSwiftバージョン(承認された回答に基づく):

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

シミュレーターを含めるのはいいですが、関数名はすべての場合に当てはまらないため、変更することをお勧めします。
DBN

2
うわー!できます!驚くばかり!同じビルド(1つのプロビジョニングで1つのスキームでビルドされた1つのビルド)のTestFlightに対してTRUEを、AppStoreに対してFALSEを返します。パーフェクト!ありがとうございました!
アーガス

@dbnこれがすべてのケースに当てはまらない理由を詳しく説明できますか?
イーサン、

1
@Ethanこの回答は私がコメントした後に編集されました。以前のメソッド名isTestFlight()
dbn


2

私はBundle+isProductionSwift 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

私のプロジェクトで使用する方法が1つあります。手順は次のとおりです。

Xcodeで、プロジェクト設定(プロジェクトではなくプロジェクト)に移動し、リストに「ベータ」構成を追加します。

ここに画像の説明を入力してください



次に、「ベータ」構成でプロジェクトを実行する新しいスキームを作成する必要があります。スキームを作成するにはここに行きます:

ここに画像の説明を入力してください



このスキームに任意の名前を付けます。このスキームの設定を編集する必要があります。これを行うには、ここをタップします:

ここに画像の説明を入力してください



選択できる[アーカイブ]タブを選択します Build configuration

ここに画像の説明を入力してください



次に、次のようにプロジェクト情報プロパティリストのConfig値を含むキーを追加する必要があります$(CONFIGURATION)

ここに画像の説明を入力してください



次に、ベータビルドに固有の何かを行うためにコードで何が必要かということです。

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つのアーカイブを作成するオプションはありますか?1つはアプリストア用のtestflight用です。
クレメン

可能ですが、ビルド番号が異なる必要があります。そして、1つではなく2つのビルドを管理することを意味します。
コンビナトリアル

わかりました、私の意見ではそれは価値があります。特に、継続的インテグレーションツールを使用する場合。
クレメン

@KlemenZagar、あなたのアプローチはよく知られていて良いものですが、質問には答えません。
スタニスラフパンケビッチ
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.