Comment savoir au moment de l'exécution si une application iOS s'exécute via une installation de TestFlight Beta

123

Est-il possible de détecter à l'exécution qu'une application a été installée via TestFlight Beta (soumis via iTunes Connect) par rapport à l'App Store? Vous pouvez soumettre un seul ensemble d'applications et le rendre disponible via les deux. Existe-t-il une API capable de détecter de quelle manière elle a été installée? Ou le reçu contient-il des informations permettant de le déterminer?

combinatoire
la source
4
Pour être clair, vous parlez du nouveau test bêta de TestFlight via iTunes Connect? Ou parlez-vous du moment où vous avez téléchargé directement sur TestFlight?
keji
La nouvelle version bêta de TestFlight, clarifiera
combinatoire du
1
On dirait que - [NSString containsString:] est un ajout ios8. Si le test automatique de l'App Store tente de l'exécuter sur ios7, pas de problème. ([receptionURLString rangeOfString: @ "sandboxReceipt"]. location! = NSNotFound) devrait faire l'affaire.
rgeorge
@rgeorge merci, c'était une erreur stupide!
combinatoire du
2
J'allais poser des questions sur la détection sur iOS 6 qui n'a pas appStoreReceiptURL, mais il semble que l'application TestFlight est uniquement iOS 8; so - [NSString containsString] pourrait être bien après tout. J'ai suspendu les tests bêta de l'App Store pour cette raison, mais je suppose que certaines personnes pourraient utiliser une stratégie de test hybride, avec Ad-Hoc pour les tests hérités et la version bêta de l'AppStore pour la version bêta publique, donc rangeOfString gagne toujours.
Gordon Dove

Réponses:

118

Pour une application installée via TestFlight Beta, le fichier de reçu est nommé par StoreKit\sandboxReceiptrapport à l'habituel StoreKit\receipt. En utilisant, [NSBundle appStoreReceiptURL]vous pouvez rechercher sandboxReceipt à la fin de l'URL.

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

Notez que sandboxReceipt c'est également le nom du fichier de reçu lors de l'exécution des builds localement et pour les builds exécutés dans le simulateur.

combinatoire
la source
7
Comme indiqué, cela fonctionne pour les tests locaux sur l'appareil, mais pas sur le simulateur. J'ai ajouté quelque chose comme #if TARGET_IPHONE_SIMULATOR isRunningInTestMode = YES; #endif Evidemment, cela nécessite #import <TargetConditionals.h>
Gordon Dove
13
Version compacte: [[[[NSBundle mainBundle] appStoreReceiptURL] lastPathComponent] isEqualToString:@"sandboxReceipt"](Vrai si vous exécutez le binaire distribué TestFlight) via Supertop / Haddad
Nick
2
Cette méthode ne peut pas être utilisée dans les bundles d'extension car le reçu n'existe que pour le bundle hôte.
jeeeyul
2
Mes tests iOS 8 se produisent StoreKit/sandboxReceiptlorsqu'ils sont installés en tant que build de débogage via Xcode sur l'appareil ou le simulateur. Cela peut donc ne pas distinguer avec précision les builds de vol de test de toutes les autres builds.
pkamb
3
Semble également retourner OUI lors de l'installation d'une version avec une distribution Ad Hoc.
Keller
75

Sur la base de la réponse combinatoire, j'ai créé la classe d'assistance SWIFT suivante. Avec cette classe, vous pouvez déterminer s'il s'agit d'une version de débogage, de vol de test ou d'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
    }
  }
}

Nous utilisons ces méthodes dans notre projet pour fournir différents identifiants de suivi ou chaîne de connexion par environnement:

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

OU:

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

MISE À JOUR 05-02-2016: Une condition préalable à l'utilisation d'une macro de préprocesseur comme #if DEBUG est de définir des indicateurs personnalisés du compilateur Swift. Plus d'informations dans cette réponse: https://stackoverflow.com/a/24112024/639227

LorenzoValentijn
la source
1
@Urkman Assurez-vous que vous définissez le -D DEBUGdrapeau. Plus d'informations peuvent être trouvées ici .
Caleb
Thnx @Caleb, j'ai ajouté plus d'explications sur les prérequis à la réponse.
LorenzoValentijn
1
Merci pour votre réponse, je l'ai trouvée très utile! Aussi bon à savoir, #if targetEnvironment(simulator)vous déterminez si vous utilisez un simulateur. J'ai donc les options Simulator / TestFlight / AppStore (qui est dans mon cas préféré à Debug) :-)
JeroenJK
39

Version Swift moderne, qui tient compte des simulateurs (en fonction de la réponse acceptée):

private func isSimulatorOrTestFlight() -> Bool {
    guard let path = Bundle.main.appStoreReceiptURL?.path else {
        return false
    }
    return path.contains("CoreSimulator") || path.contains("sandboxReceipt")
}
Serhii Yakovenko
la source
C'est bien d'inclure le simulateur, mais vous voudrez peut-être changer le nom de la fonction car ce n'est plus vrai dans tous les cas.
dbn le
2
HOU LA LA! Ça marche! Impressionnant! Renvoie TRUE pour TestFlight et FALSE pour AppStore pour la même build (une build construite dans le seul schéma avec un approvisionnement). Parfait! Merci!
Argus le
@dbn pouvez-vous expliquer pourquoi ce n'est plus vrai dans tous les cas?
Ethan
1
@Ethan cette réponse a été modifiée après avoir fait mon commentaire; le nom de la méthode étaitisTestFlight()
dbn
2

J'utilise l'extension Bundle+isProductionsur 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
    }
}

Ensuite:

if Bundle.main.isProduction {
    // do something
}
Denis Kutlubaev
la source
-3

Il y a une façon dont je l'utilise pour mes projets. Voici les étapes.

Dans Xcode, allez dans les paramètres du projet (projet, pas cible) et ajoutez la configuration "beta" à la liste:

entrez la description de l'image ici



Ensuite, vous devez créer un nouveau schéma qui exécutera le projet en configuration "bêta". Pour créer un schéma, allez ici:

entrez la description de l'image ici



Nommez ce schéma comme vous le souhaitez. Vous devez modifier les paramètres de ce schéma. Pour ce faire, appuyez ici:

entrez la description de l'image ici



Sélectionnez l'onglet Archive où vous pouvez sélectionner Build configuration

entrez la description de l'image ici



Ensuite, vous devez ajouter une clé Configavec une valeur dans $(CONFIGURATION)la liste des propriétés d'informations du projet comme ceci:

entrez la description de l'image ici



Ensuite, c'est juste ce dont vous avez besoin dans le code pour faire quelque chose de spécifique à la version bêta:

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
}
Klemen
la source
6
Bien que ce soit une technique utile, elle ne répond pas à la question. Un seul binaire est soumis à l'App Store et peut être exécuté à partir du téléchargement via TestFlight ou ultérieurement après avoir été approuvé après le téléchargement depuis l'App Store. La question est de savoir quelle version est en cours d'exécution.
combinatoire
Existe-t-il une option pour créer 2 archives en premier lieu. un pour testflight un pour l'App Store.
Klemen
C'est possible, mais ils doivent avoir des numéros de build différents. Et cela signifie gérer deux builds au lieu d'une.
combinatoire du
ok, à mon avis ça vaut le coup. Surtout si vous utilisez des outils d'intégration continue.
Klemen
@KlemenZagar, votre approche est connue et bonne mais elle ne répond pas à la question.
Stanislav Pankevich