Як сказати під час виконання програми, чи працює програма iOS через бета-версію TestFlight Beta


123

Чи можливо виявити під час виконання, що програма була встановлена ​​через тестування Beta TestFlight (подається через iTunes Connect) проти App Store? Ви можете надіслати один пакет програм і мати його доступним через обидва. Чи існує API, який може виявити, яким способом він був встановлений? Або квитанція містить інформацію, яка дозволяє це визначити?


4
Щоб зрозуміти, ви говорите про нове бета-тестування TestFlight через iTunes Connect? Або ви говорите про те, коли ви безпосередньо завантажили в TestFlight?
keji

Нова бета-версія TestFlight, уточнимо
комбінаторний

1
Схоже - [NSString міститьString:] є додатком ios8. Якщо автоматичне тестування App Store намагається запустити його на ios7, не йдіть. ([квитанціяURLString rangeOfString: @ "sandboxReceipt"]. location! = NSNotFound) повинна зробити свою справу.
rgeorge

@rgeorge спасибі, це була німа помилка!
комбінаторний

2
Я збирався запитати про виявлення на iOS 6, у якому немає appStoreReceiptURL, але здається, що додаток TestFlight - це лише iOS 8; так що - [NSString containsString] все-таки може бути добре. Через це я зупинив бета-тестування магазину додатків, але, мабуть, деякі люди можуть використовувати гібридну стратегію тестування: Ad-Hoc для попереднього тестування та AppStore beta для загальнодоступної бета-версії, тому rangeOfString все одно виграє.
Гордон Голуб

Відповіді:


117

Для програми, встановленої через TestFlight Beta, файл отримання отримав назву StoreKit\sandboxReceiptвід звичайного StoreKit\receipt. Використовуючи [NSBundle appStoreReceiptURL]ви можете шукати sandboxReceipt в кінці URL-адреси.

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

Зауважте, що sandboxReceiptце також ім'я файлу отримання при запуску збірок локально і для збірок, запущених в тренажері.


7
Як зазначалося, це працює для локального тестування на пристрої, але не на тренажері. Я додав щось на зразок #if TARGET_IPHONE_SIMULATOR isRunningInTestMode = ТАК; #endif Очевидно, що для цього потрібно #import <TargetConditionals.h>
Гордон Даув

13
Компактна версія: [[[[NSBundle mainBundle] appStoreReceiptURL] lastPathComponent] isEqualToString:@"sandboxReceipt"](Правда, якщо ви використовуєте тестовий бінарний тест TestFlight
Нік,

2
Цей метод не можна використовувати в розширеннях розширень, оскільки квитанція існує лише для хост-пакета.
jeeeyul

2
Результати тестування на моєму iOS 8 під StoreKit/sandboxReceiptчас встановлення налагодження налагодження через Xcode на пристрої чи симуляторі. Таким чином, це може не точно відрізняти збірки тестових польотів від усіх інших збірок.
pkamb

3
Також, здається, повертає ТАК при встановленні збірки з Ad Hoc дистрибутивом.
Келлер

75

На основі відповіді комбінатора я створив наступний помічник 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
    }
  }
}

Ми використовуємо ці методи в нашому проекті, щоб поставити різні ідентифікатори відстеження або рядок з'єднання для середовища:

  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
    }
  }

ОНОВЛЕННЯ 05-02-2016: Необхідною умовою використання макросу препроцесора, як #if DEBUG, є встановлення деяких спеціальних прапорів Swift Compiler. Більше інформації у цій відповіді: 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
ОЦЕ ТАК! Це працює! Дивовижно! Повертає TRUE для TestFlight та FALSE для AppStore для тієї ж збірки (одна збірка, побудована в одній схемі з одним резервуванням). Ідеально! Спасибі!
Аргус

@dbn Ви можете розширити, чому це вже не стосується всіх випадків?
Етан

1
@Ethan цю відповідь було відредаговано після того, як я зробив свій коментар; назва методуisTestFlight()
dbn

6

Оновлення

Це вже не працює. Використовуйте інший метод.

Оригінальна відповідь

Це також працює:

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

Знайдено в програмі Виявлення, якщо додаток iOS завантажено з Apple Testflight


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 перейдіть до налаштувань проекту (проект, а не ціль) та додайте до списку конфігурацію "бета":

введіть тут опис зображення



Тоді вам потрібно створити нову схему, яка запустить проект у «бета» конфігурації. Щоб створити схему, перейдіть сюди:

введіть тут опис зображення



Назвіть цю схему все, що завгодно. Вам слід відредагувати налаштування для цієї схеми. Для цього натисніть тут:

введіть тут опис зображення



Виберіть вкладку Архів, де ви можете вибрати 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 архіви. один для тестового польоту, один для магазину додатків.
Клемен

Це можливо, але вони повинні мати різні числа збірки. А це означає керувати двома побудовами замість однієї.
комбінаторний

ок, на мою думку це того варте. Особливо, якщо ви використовуєте засоби безперервної інтеграції.
Клемен

@KlemenZagar, ваш підхід відомий і хороший, але він не відповідає на питання.
Станіслав Панькевич
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.