Cómo saber en tiempo de ejecución si una aplicación de iOS se está ejecutando a través de una instalación de TestFlight Beta

123

¿Es posible detectar en tiempo de ejecución que se ha instalado una aplicación a través de TestFlight Beta (enviada a través de iTunes Connect) frente a la App Store? Puede enviar un solo paquete de aplicaciones y tenerlo disponible a través de ambos. ¿Existe una API que pueda detectar de qué manera se instaló? ¿O el recibo contiene información que permita determinarlo?

combinacional
fuente
4
Para que quede claro, ¿está hablando de la nueva prueba beta de TestFlight a través de iTunes Connect? ¿O estás hablando de cuando has subido a TestFlight directamente?
keji
La nueva beta de TestFlight, aclarará
combinatoria
1
Parece que - [NSString containsString:] es una adición de ios8. Si la prueba automática de la App Store intenta ejecutarlo en ios7, no lo haga. ([ReceiveURLString rangeOfString: @ "sandboxReceipt"]. location! = NSNotFound) debería funcionar.
rgeorge
@rgeorge gracias, ¡fue un error tonto!
combinatoria
2
Iba a preguntar sobre la detección en iOS 6 que no tiene appStoreReceiptURL, pero parece que la aplicación TestFlight es solo para iOS 8; entonces - [NSString containsString] podría estar bien después de todo. Puse la prueba beta de la tienda de aplicaciones en espera debido a esto, pero supongo que algunas personas podrían estar usando una estrategia de prueba híbrida, con Ad-Hoc para pruebas heredadas y AppStore beta para beta pública, por lo que rangeOfString aún gana.
Gordon Dove

Respuestas:

118

Para una aplicación instalada a través de TestFlight Beta, el archivo de recibo se nombra StoreKit\sandboxReceiptfrente al habitual StoreKit\receipt. Usando [NSBundle appStoreReceiptURL]puede buscar sandboxReceipt al final de la URL.

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

Tenga en cuenta que sandboxReceipttambién es el nombre del archivo de recibo cuando se ejecutan compilaciones localmente y cuando las compilaciones se ejecutan en el simulador.

combinacional
fuente
7
Como se señaló, esto funciona para pruebas locales en el dispositivo, pero no en el simulador. Agregué algo como #if TARGET_IPHONE_SIMULATOR isRunningInTestMode = YES; #endif Obviamente, esto necesita #import <TargetConditionals.h>
Gordon Dove
13
Versión compacta: [[[[NSBundle mainBundle] appStoreReceiptURL] lastPathComponent] isEqualToString:@"sandboxReceipt"](Verdadero si se ejecuta TestFlight binario distribuido) a través de Supertop / Haddad
Nick
2
Este método no se puede utilizar en paquetes de extensión, ya que el recibo existe solo para el paquete de host.
jeeeyul
2
Mis resultados de prueba de iOS 8 StoreKit/sandboxReceiptcuando se instalan como una compilación de depuración a través de Xcode en el dispositivo o simulador. Por lo tanto, esto puede no distinguir con precisión las compilaciones de testflight de todas las demás compilaciones.
pkamb
3
También parece devolver SÍ al instalar una compilación con distribución Ad Hoc.
Keller
75

Basándome en la respuesta de combinatoria, creé la siguiente clase auxiliar de SWIFT. Con esta clase puedes determinar si se trata de una compilación de depuración, prueba de vuelo o tienda de aplicaciones.

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

Usamos estos métodos en nuestro proyecto para proporcionar diferentes ID de seguimiento o cadenas de conexión por entorno:

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

ACTUALIZACIÓN 05-02-2016: Un requisito previo para usar una macro de preprocesador como #if DEBUG es establecer algunos indicadores personalizados del compilador Swift. Más información en esta respuesta: https://stackoverflow.com/a/24112024/639227

LorenzoValentijn
fuente
1
@Urkman Asegúrese de que está colocando la -D DEBUGbandera. Puede encontrar más información aquí .
Caleb
Thnx @Caleb, agregué más explicación sobre los requisitos previos a la respuesta.
LorenzoValentijn
1
Gracias por tu respuesta, ¡lo encontré muy útil! También es bueno saber que el uso de #if targetEnvironment(simulator)usted determina si está ejecutando en un simulador. Entonces tengo las opciones Simulator / TestFlight / AppStore (que en mi caso es preferible Debug) :-)
JeroenJK
39

Versión moderna de Swift, que da cuenta de los simuladores (según la respuesta aceptada):

private func isSimulatorOrTestFlight() -> Bool {
    guard let path = Bundle.main.appStoreReceiptURL?.path else {
        return false
    }
    return path.contains("CoreSimulator") || path.contains("sandboxReceipt")
}
Serhii Yakovenko
fuente
Es bueno incluir el simulador, pero es posible que desee cambiar el nombre de la función, ya que ya no es cierto para todos los casos.
dbn
2
¡GUAUU! ¡Funciona! ¡Increíble! Devuelve TRUE para TestFlight y FALSE para AppStore para la misma compilación (una compilación compilada en un esquema con un aprovisionamiento). ¡Perfecto! ¡Gracias!
Argus
@dbn, ¿puede explicar por qué esto ya no es cierto para todos los casos?
Ethan
1
@Ethan esta respuesta fue editada después de que hice mi comentario; el nombre del método solía serisTestFlight()
dbn
2

Uso la extensión Bundle+isProductionen 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
    }
}

Luego:

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

Hay una forma en que lo uso para mis proyectos. Estos son los pasos.

En Xcode, vaya a la configuración del proyecto (proyecto, no objetivo) y agregue la configuración "beta" a la lista:

ingrese la descripción de la imagen aquí



Luego, debe crear un nuevo esquema que ejecutará el proyecto en la configuración "beta". Para crear un esquema, vaya aquí:

ingrese la descripción de la imagen aquí



Nombra este esquema como quieras. Debe editar la configuración de este esquema. Para hacer esto, toque aquí:

ingrese la descripción de la imagen aquí



Seleccione la pestaña Archivo donde puede seleccionar Build configuration

ingrese la descripción de la imagen aquí



Luego, debe agregar una clave Configcon el valor de $(CONFIGURATION)la lista de propiedades de información de proyectos como esta:

ingrese la descripción de la imagen aquí



Entonces es solo la cuestión de lo que necesita en el código para hacer algo específico para la compilación 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
}
Klemen
fuente
6
Si bien esta es una técnica útil, no responde a la pregunta. Se envía un único binario a la App Store y se puede ejecutar desde la descarga a través de TestFlight o más tarde después de la ejecución aprobada después de la descarga de la App Store. La pregunta es sobre detectar qué versión se está ejecutando.
combinatoria
¿Existe una opción para hacer 2 archivos en primer lugar? uno para testflight uno para la tienda de aplicaciones.
Klemen
Es posible, pero deben tener diferentes números de compilación. Y significa administrar dos compilaciones en lugar de una.
combinatoria
ok, en mi opinión merece la pena. Especialmente si utiliza herramientas de integración continua.
Klemen
@KlemenZagar, su enfoque es bien conocido y bueno, pero no responde a la pregunta.
Stanislav Pankevich