So erkennen Sie zur Laufzeit, ob eine iOS-App über eine TestFlight Beta-Installation ausgeführt wird

122

Kann zur Laufzeit festgestellt werden, dass eine Anwendung über TestFlight Beta (über iTunes Connect eingereicht) im Vergleich zum App Store installiert wurde? Sie können ein einzelnes App-Bundle einreichen und über beide verfügbar machen. Gibt es eine API, die erkennen kann, auf welche Weise sie installiert wurde? Oder enthält die Quittung Informationen, anhand derer dies festgestellt werden kann?

kombinatorisch
quelle
4
Um ganz klar zu sein, sprechen Sie über den neuen TestFlight-Betatest über iTunes Connect? Oder sprechen Sie davon, wenn Sie direkt auf TestFlight hochgeladen haben?
Keji
Die neue TestFlight Beta wird klarstellen
kombinatorisch
1
Sieht aus wie - [NSString enthältString:] ist eine ios8-Ergänzung. Wenn der automatische Test im App Store versucht, ihn auf ios7 auszuführen, gehen Sie nicht. ([receiveURLString rangeOfString: @ "sandboxReceipt"]. location! = NSNotFound) sollte den Trick machen.
Rgeorge
@rgeorge danke, das war ein dummer fehler!
kombinatorisch
2
Ich wollte nach der Erkennung unter iOS 6 fragen, die nicht über appStoreReceiptURL verfügt, aber es scheint, dass die TestFlight-App nur iOS 8 ist. also - [NSString enthältString] könnte doch in Ordnung sein. Ich habe den Beta-Test im App Store aus diesem Grund ausgesetzt, aber ich denke, einige Leute verwenden möglicherweise eine Hybrid-Teststrategie mit Ad-Hoc für Legacy-Tests und AppStore Beta für die öffentliche Beta, sodass rangeOfString immer noch gewinnt.
Gordon Dove

Antworten:

117

Für eine über TestFlight Beta installierte Anwendung wird die Belegdatei im Vergleich StoreKit\sandboxReceiptzur üblichen benannt StoreKit\receipt. Mit [NSBundle appStoreReceiptURL]können Sie am Ende der URL nach sandboxReceipt suchen.

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

Beachten Sie, dass dies sandboxReceiptauch der Name der Belegdatei ist, wenn Builds lokal ausgeführt werden und wenn Builds im Simulator ausgeführt werden.

kombinatorisch
quelle
7
Wie bereits erwähnt, funktioniert dies für lokale Tests auf dem Gerät, jedoch nicht auf dem Simulator. Ich habe etwas hinzugefügt wie #if TARGET_IPHONE_SIMULATOR isRunningInTestMode = YES; #endif Offensichtlich braucht dies #import <TargetConditionals.h>
Gordon Dove
13
Kompakte Version: [[[[NSBundle mainBundle] appStoreReceiptURL] lastPathComponent] isEqualToString:@"sandboxReceipt"](True, wenn TestFlight Distributed Binary ausgeführt wird) über Supertop / Haddad
Nick
2
Diese Methode kann nicht in Erweiterungspaketen verwendet werden, da der Beleg nur für das Host-Paket vorhanden ist.
Jeeeyul
2
Meine iOS 8-Testergebnisse werden StoreKit/sandboxReceiptbei der Installation als Debug-Build über Xcode auf einem Gerät oder Simulator angezeigt. Dies unterscheidet Testflight- Builds möglicherweise nicht genau von allen anderen Builds.
Pkamb
3
Scheint auch JA zu geben, wenn ein Build mit Ad-hoc-Verteilung installiert wird.
Keller
74

Basierend auf der Antwort von combinatorial habe ich die folgende SWIFT-Hilfsklasse erstellt. Mit dieser Klasse können Sie feststellen, ob es sich um einen Debug-, Testflight- oder Appstore-Build handelt.

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

Wir verwenden diese Methoden in unserem Projekt, um pro Umgebung unterschiedliche Tracking-IDs oder Verbindungszeichenfolgen bereitzustellen :

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

ODER:

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

UPDATE 05-02-2016: Voraussetzung für die Verwendung eines Präprozessor-Makros wie #if DEBUG ist das Festlegen einiger benutzerdefinierter Swift Compiler-Flags. Weitere Informationen in dieser Antwort: https://stackoverflow.com/a/24112024/639227

LorenzoValentijn
quelle
1
@Urkman Stellen Sie sicher, dass Sie das -D DEBUGFlag setzen. Weitere Informationen finden Sie hier .
Caleb
Danke @Caleb, ich habe der Antwort weitere Erklärungen zu den Voraussetzungen hinzugefügt.
LorenzoValentijn
1
Vielen Dank für Ihre Antwort, ich fand es sehr hilfreich! Gut zu wissen, #if targetEnvironment(simulator)ob Sie in einem Simulator arbeiten. Also habe ich die Optionen Simulator / TestFlight / AppStore (was in meinem Fall vorgezogen wird Debug) :-)
JeroenJK
38

Moderne Swift-Version, die Simulatoren berücksichtigt (basierend auf der akzeptierten Antwort):

private func isSimulatorOrTestFlight() -> Bool {
    guard let path = Bundle.main.appStoreReceiptURL?.path else {
        return false
    }
    return path.contains("CoreSimulator") || path.contains("sandboxReceipt")
}
Serhii Yakovenko
quelle
Schön, den Simulator einzuschließen, aber vielleicht möchten Sie den Funktionsnamen ändern, da er nicht mehr in allen Fällen zutrifft.
dbn
2
BEEINDRUCKEND! Es klappt! Genial! Gibt TRUE für TestFlight und FALSE für AppStore für denselben Build zurück (ein Build, der in einem Schema mit einer Bereitstellung erstellt wurde). Perfekt! Danke!
Argus
@dbn können Sie erläutern, warum dies nicht mehr für alle Fälle gilt?
Ethan
1
@Ethan diese Antwort wurde bearbeitet, nachdem ich meinen Kommentar abgegeben habe; der Methodenname warisTestFlight()
dbn
6

Aktualisieren

Das funktioniert nicht mehr. Verwenden Sie eine andere Methode.

Ursprüngliche Antwort

Dies funktioniert auch:

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

Gefunden in Erkennen, wenn die iOS-App von Apples Testflight heruntergeladen wurde

Marián Černý
quelle
2

Ich benutze die Erweiterung Bundle+isProductionauf 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
    }
}

Dann:

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

Es gibt eine Möglichkeit, wie ich es für meine Projekte verwenden kann. Hier sind die Schritte.

Gehen Sie in Xcode zu den Projekteinstellungen (Projekt, nicht Ziel) und fügen Sie der Liste die Beta-Konfiguration hinzu:

Geben Sie hier die Bildbeschreibung ein



Dann müssen Sie ein neues Schema erstellen, das das Projekt in der "Beta" -Konfiguration ausführt. Um ein Schema zu erstellen, gehen Sie hier:

Geben Sie hier die Bildbeschreibung ein



Nennen Sie dieses Schema, wie Sie wollen. Sie sollten die Einstellungen für dieses Schema bearbeiten. Tippen Sie dazu hier:

Geben Sie hier die Bildbeschreibung ein



Wählen Sie die Registerkarte Archiv, auf der Sie auswählen können Build configuration

Geben Sie hier die Bildbeschreibung ein



Dann müssen Sie einen Schlüssel Configmit dem Wert $(CONFIGURATION)der Projektinfo-Eigenschaftsliste wie folgt hinzufügen :

Geben Sie hier die Bildbeschreibung ein



Dann ist es genau das, was Sie im Code benötigen, um etwas Spezielles für den Beta-Build zu tun:

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
quelle
6
Dies ist zwar eine hilfreiche Technik, beantwortet aber die Frage nicht. Eine einzelne Binärdatei wird an den App Store gesendet und kann entweder beim Herunterladen über TestFlight oder später nach dem genehmigten Ausführen nach dem Herunterladen aus dem App Store ausgeführt werden. Bei der Frage geht es darum, festzustellen, welche Version ausgeführt wird.
kombinatorisch
Gibt es eine Option, um überhaupt 2 Archive zu erstellen? eine für den Testflug eine für den App Store.
Klemen
Es ist möglich, aber sie müssen unterschiedliche Build-Nummern haben. Und es bedeutet, zwei Builds anstelle von einem zu verwalten.
kombinatorisch
ok, meiner meinung nach lohnt es sich. Vor allem, wenn Sie Tools zur kontinuierlichen Integration verwenden.
Klemen
@KlemenZagar, Ihr Ansatz ist bekannt und gut, aber er beantwortet die Frage nicht.
Stanislav Pankevich