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.