Come sapere in fase di esecuzione se un'app iOS è in esecuzione tramite un'installazione Beta di TestFlight


123

È possibile rilevare in fase di esecuzione che un'applicazione è stata installata tramite TestFlight Beta (inviato tramite iTunes Connect) rispetto all'App Store? Puoi inviare un singolo app bundle e renderlo disponibile tramite entrambi. Esiste un'API in grado di rilevare in che modo è stata installata? Oppure la ricevuta contiene informazioni che consentono di determinarlo?


4
Giusto per essere chiari, stai parlando del nuovo beta testing di TestFlight tramite iTunes Connect? O stai parlando di quando hai caricato direttamente su TestFlight?
keji

La nuova beta di TestFlight, chiarirà
combinatoria il

1
Sembra che - [NSString containsString:] sia un'aggiunta ios8. Se il test automatico dell'App Store tenta di eseguirlo su ios7, non andare. ([receiverURLString rangeOfString: @ "sandboxReceipt"]. location! = NSNotFound) dovrebbe fare il trucco.
rgeorge

@rgeorge, grazie, è stato un errore stupido!
combinatoria

2
Stavo per chiedere informazioni sul rilevamento su iOS 6 che non ha appStoreReceiptURL, ma sembra che l'app TestFlight sia solo iOS 8; quindi - [NSString containsString] potrebbe andare bene dopo tutto. Ho sospeso il beta test dell'app store per questo motivo, ma immagino che alcune persone potrebbero utilizzare una strategia di test ibrida, con Ad-Hoc per i test legacy e AppStore beta per la beta pubblica, quindi rangeOfString vince ancora.
Gordon Dove il

Risposte:


118

Per un'applicazione installata tramite TestFlight Beta il file della ricevuta è denominato StoreKit\sandboxReceiptrispetto al solito StoreKit\receipt. Usando [NSBundle appStoreReceiptURL]puoi cercare sandboxReceipt alla fine dell'URL.

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

Si noti che sandboxReceiptè anche il nome del file di ricevuta quando si eseguono build in locale e per le build eseguite nel simulatore.


7
Come notato, funziona per i test locali sul dispositivo, ma non sul simulatore. Ho aggiunto qualcosa come #if TARGET_IPHONE_SIMULATOR isRunningInTestMode = YES; #endif Ovviamente, questo richiede #import <TargetConditionals.h>
Gordon Dove

13
Versione compatta: [[[[NSBundle mainBundle] appStoreReceiptURL] lastPathComponent] isEqualToString:@"sandboxReceipt"](vero se si esegue il binario distribuito TestFlight) tramite Supertop / Haddad
Nick

2
Questo metodo non può essere utilizzato nei pacchetti di estensioni poiché la ricevuta esiste solo per il pacchetto host.
jeeeyul

2
I risultati dei miei test su iOS 8 StoreKit/sandboxReceiptvengono installati come build di debug tramite Xcode sul dispositivo o sul simulatore. Quindi questo potrebbe non distinguere accuratamente le build di testflight da tutte le altre build.
pkamb

3
Sembra anche restituire SÌ quando si installa una build con distribuzione Ad Hoc.
Keller

75

Sulla base della risposta combinatoria ho creato la seguente classe helper SWIFT. Con questa classe puoi determinare se si tratta di una build di debug, testflight o 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
    }
  }
}

Usiamo questi metodi nel nostro progetto per fornire diversi ID di tracciamento o stringhe di connessione per ambiente:

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

O:

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

AGGIORNAMENTO 05-02-2016: un prerequisito per utilizzare una macro del preprocessore come #if DEBUG è impostare alcuni flag personalizzati di Swift Compiler. Maggiori informazioni in questa risposta: https://stackoverflow.com/a/24112024/639227


1
@Urkman Assicurati di impostare la -D DEBUGbandiera. Ulteriori informazioni possono essere trovate qui .
Caleb

Grazie a @Caleb, ho aggiunto ulteriori spiegazioni sui prerequisiti alla risposta.
LorenzoValentijn

1
Grazie per la tua risposta, l'ho trovata molto utile! Buono anche a sapersi, utilizzando #if targetEnvironment(simulator)si determina se si sta eseguendo in un simulatore. Quindi ho le opzioni Simulator / TestFlight / AppStore (che nel mio caso è la preferita rispetto a Debug) :-)
JeroenJK

39

Versione moderna di Swift, che tiene conto dei simulatori (in base alla risposta accettata):

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

È bello includere il simulatore, ma potresti voler cambiare il nome della funzione poiché non è più vero per tutti i casi.
dbn

2
WOW! Funziona! Eccezionale! Restituisce TRUE per TestFlight e FALSE per AppStore per la stessa build (una build creata in uno schema con un provisioning). Perfetto! Grazie!
Argus

@dbn puoi spiegare perché questo non è più vero per tutti i casi?
Ethan

1
@Ethan questa risposta è stata modificata dopo che ho fatto il mio commento; il nome del metodo eraisTestFlight()
dbn


2

Uso l'estensione Bundle+isProductionsu 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
    }
}

Poi:

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

-3

C'è un modo in cui lo uso per i miei progetti. Ecco i passaggi.

In Xcode, vai alle impostazioni del progetto (progetto, non target) e aggiungi la configurazione "beta" all'elenco:

inserisci qui la descrizione dell'immagine



Quindi è necessario creare un nuovo schema che eseguirà il progetto in configurazione "beta". Per creare lo schema vai qui:

inserisci qui la descrizione dell'immagine



Dai un nome a questo schema come preferisci. È necessario modificare le impostazioni per questo schema. Per fare ciò, tocca qui:

inserisci qui la descrizione dell'immagine



Seleziona la scheda Archivio dove puoi selezionare Build configuration

inserisci qui la descrizione dell'immagine



Quindi è necessario aggiungere una chiave Configcon valore $(CONFIGURATION)nell'elenco delle proprietà delle informazioni sui progetti in questo modo:

inserisci qui la descrizione dell'immagine



Quindi è solo la questione di ciò di cui hai bisogno nel codice per fare qualcosa di specifico per la build 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
Sebbene questa sia una tecnica utile, non risponde alla domanda. Un singolo file binario viene inviato all'App Store e può essere eseguito dal download tramite TestFlight o successivamente dopo l'esecuzione approvata dopo il download dall'App Store. La domanda riguarda il rilevamento della versione in esecuzione.
combinatoria

C'è un'opzione per creare 2 archivi in ​​primo luogo. uno per testflight uno per l'app store.
Klemen

È possibile, ma devono avere numeri di build diversi. E significa gestire due build invece di una.
combinatoria

ok, secondo me ne vale la pena. Soprattutto se utilizzi strumenti di integrazione continua.
Klemen

@KlemenZagar, il tuo approccio è noto e buono ma non risponde alla domanda.
Stanislav Pankevich
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.