web-dev-qa-db-fra.com

Détecter si une application est en cours de création pour un appareil ou un simulateur dans Swift

En Objective-C, nous pouvons savoir si une application est en cours de création pour un appareil ou un simulateur à l'aide de macros:

#if TARGET_IPHONE_SIMULATOR
    // Simulator
#else
    // Device
#endif

Ce sont des macros à la compilation et non disponibles au moment de l'exécution.

Comment puis-je obtenir la même chose dans Swift? J'ai cherché un débordement de pile, jeté un coup d’œil dans la documentation et je ne peux pas le comprendre.

224
RaffAl

Mise à jour 30/01/19

Bien que cette réponse puisse fonctionner, la solution recommandée pour une vérification statique (comme le précisent plusieurs ingénieurs Apple) consiste à définir un indicateur de compilateur personnalisé ciblant les simulateurs iOS. Pour des instructions détaillées sur la marche à suivre, voir Réponse de @ mbelsky .

Réponse originale

Si vous avez besoin d’une vérification statique (par exemple, pas un runtime si/else), vous ne pouvez pas détecter le simulateur directement, mais vous pouvez détecter iOS sur une architecture de bureau comme suit:

#if (Arch(i386) || Arch(x86_64)) && os(iOS)
    ...
#endif

Après la version Swift 4.1

Dernière utilisation, maintenant directement pour tous dans une condition pour tous les types de simulateurs doivent appliquer une seule condition -

#if targetEnvironment(simulator)
  // your simulator code
#else
  // your real device code
#endif

Pour plus de précisions, vous pouvez vérifier Swift proposition SE-0190 


Pour les anciennes versions -

Clairement, ceci est false sur un périphérique, mais renvoie true pour le simulateur iOS, comme spécifié dans la documentation :

La configuration de génération Arch (i386) renvoie true lorsque le code est compilé pour le simulateur iOS 32 bits.

Si vous développez pour un simulateur autre que iOS, vous pouvez simplement faire varier le paramètre os: par exemple.

Détecter le simulateur watchOS

#if (Arch(i386) || Arch(x86_64)) && os(watchOS)
...
#endif

Détecter le simulateur tvOS

#if (Arch(i386) || Arch(x86_64)) && os(tvOS)
...
#endif

Ou même détecter n'importe quel simulateur

#if (Arch(i386) || Arch(x86_64)) && (os(iOS) || os(watchOS) || os(tvOS))
...
#endif

Si, au contraire, vous êtes d'accord avec une vérification à l'exécution, vous pouvez inspecter la variable TARGET_OS_SIMULATOR (ou TARGET_IPHONE_SIMULATOR sous iOS 8 et inférieur), qui est la vérité sur un simulateur.

Veuillez noter que ceci est différent et légèrement plus limité que l’utilisation d’un indicateur de préprocesseur. Par exemple, vous ne pourrez pas l'utiliser à la place d'un if/else incorrect du point de vue de la syntaxe (par exemple, en dehors de l'étendue des fonctions).

Dites, par exemple, que vous souhaitez avoir différentes importations sur l'appareil et sur le simulateur. Ceci est impossible avec une vérification dynamique, alors que c'est trivial avec une vérification statique.

#if (Arch(i386) || Arch(x86_64)) && os(iOS)
  import Foo
#else
  import Bar
#endif

De plus, étant donné que l'indicateur est remplacé par un 0 ou un 1 par le préprocesseur Swift, si vous l'utilisez directement dans une expression if/else, le compilateur émet un avertissement concernant le code inaccessible.

Pour contourner cet avertissement, reportez-vous à l'une des autres réponses.

273
Gabriele Petronella

OUTDATED FOR Swift 4.1. Utilisez #if targetEnvironment(simulator) à la place. La source

Pour détecter le simulateur dans Swift, vous pouvez utiliser la configuration de construction:

  • Définissez cette configuration -D IOS_SIMULATOR dans Swift Compiler - Indicateurs personnalisés> Autres indicateurs Swift}
  • Sélectionnez Any iOS Simulator SDK dans ce menu déroulant  Drop down list

Maintenant, vous pouvez utiliser cette instruction pour détecter le simulateur:

#if IOS_SIMULATOR
    print("It's an iOS Simulator")
#else
    print("It's a device")
#endif

Aussi, vous pouvez étendre la classe UIDevice:

extension UIDevice {
    var isSimulator: Bool {
        #if IOS_SIMULATOR
            return true
        #else
            return false
        #endif
    }
}
// Example of usage: UIDevice.current.isSimulator
157
mbelsky

Info mise à jour au 20 février 2018

Il semble que @russbishop ait une réponse faisant autorité qui rend cette réponse "incorrecte" - même si elle a semblé fonctionner longtemps.

Détecter si une application est en cours de construction pour un appareil ou un simulateur dans Swift

Réponse précédente

Sur la base de la réponse de @ WZW et des commentaires de @ Pang, j'ai créé une structure d'utilitaire simple. Cette solution évite les avertissements produits par la réponse de @ WZW.

import Foundation

struct Platform {

    static var isSimulator: Bool {
        return TARGET_OS_SIMULATOR != 0
    }

}

Exemple d'utilisation:

if Platform.isSimulator {
    print("Running on Simulator")
}
153
Daniel

À partir de Xcode 9.3

#if targetEnvironment(simulator)

Swift prend en charge une nouvelle condition de plate-forme targetEnvironment avec un simulateur d'argument valide unique. Compilation conditionnelle du formulaire '#if targetEnvironment (simulator)' peut maintenant être utilisé pour détecter quand la cible de construction est un simulateur. Le compilateur Swift va tenter de détecter, avertir et suggérer l’utilisation de targetEnvironment (simulateur) lorsque évaluer les conditions de la plate-forme qui semblent être en train de tester le simulateur environnements indirectement, via les plates-formes os () et Arch () existantes conditions. (SE-0190)

iOS 9+:

extension UIDevice {
    static var isSimulator: Bool {
        return NSProcessInfo.processInfo().environment["SIMULATOR_DEVICE_NAME"] != nil
    }
}

Swift 3:

extension UIDevice {
    static var isSimulator: Bool {
        return ProcessInfo.processInfo.environment["SIMULATOR_DEVICE_NAME"] != nil
    }
}

Avant iOS 9:

extension UIDevice {
    static var isSimulator: Bool {
        return UIDevice.currentDevice().model == "iPhone Simulator"
    }
}

Objectif c:

@interface UIDevice (Additions)
- (BOOL)isSimulator;
@end

@implementation UIDevice (Additions)

- (BOOL)isSimulator {
    if([[NSProcessInfo processInfo] isOperatingSystemAtLeastVersion:(NSOperatingSystemVersion){9, 0, 0}]) {
        return [NSProcessInfo processInfo].environment[@"SIMULATOR_DEVICE_NAME"] != nil;
    } else {
        return [[self model] isEqualToString:@"iPhone Simulator"];
    }
}

@end
63
HotJard

Swift 4

Vous pouvez maintenant utiliser targetEnvironment(simulator) comme argument.

#if targetEnvironment(simulator)
    // Simulator
#else
    // Device
#endif

Mise à jour pour Xcode 9.3

48
Matt Swift

Permettez-moi de clarifier certaines choses ici:

  1. TARGET_OS_SIMULATOR n'est pas défini dans le code Swift dans de nombreux cas; vous pouvez être importé accidentellement à cause d'un en-tête de pontage, mais ceci est fragile et n'est pas pris en charge. Ce n'est pas même possible dans les cadres. C'est pourquoi certaines personnes ne savent pas si cela fonctionne dans Swift.
  2. Je déconseille fortement d'utiliser l'architecture comme substitut du simulateur.

Pour effectuer des contrôles dynamiques:

Vérifier ProcessInfo.processInfo.environment["SIMULATOR_DEVICE_NAME"] != nil est parfaitement correct.

Vous pouvez également simuler le modèle sous-jacent en cochant SIMULATOR_MODEL_IDENTIFIER qui renverra des chaînes comme iPhone10,3.

Pour effectuer des contrôles statiques:

Xcode 9.2 et versions antérieures: définissez votre propre drapeau de compilation Swift (comme indiqué dans les autres réponses).

Xcode 9.3+ utilise la nouvelle condition targetEnvironment:

#if targetEnvironment(simulator)
    // for sim only
#else
    // for device
#endif
26
russbishop

Ce qui fonctionne pour moi depuis que Swift 1.0 recherche une architecture autre qu'armer:

#if Arch(i386) || Arch(x86_64)

     //simulator
#else 
     //device

#endif
15
akaru

Runtime, mais plus simple que la plupart des autres solutions ici:

if TARGET_OS_SIMULATOR != 0 {
    // target is current running in the simulator
}

Sinon, vous pouvez simplement appeler une fonction d'assistance Objective-C qui renvoie un booléen utilisant la macro de préprocesseur (surtout si vous êtes déjà en train de mixer dans votre projet).

Edit: Pas la meilleure solution, surtout à partir de Xcode 9.3. Voir Réponse de HotJard

13
shim

Dans les systèmes modernes:

#if targetEnvironment(simulator)
    // sim
#else
    // device
#endif

C'est facile.

9
Fattie

TARGET_IPHONE_SIMULATOR est obsolète dans iOS 9. TARGET_OS_SIMULATOR est le remplacement. De plus, TARGET_OS_EMBEDDED est disponible.

De TargetConditionals.h :

#if defined(__GNUC__) && ( defined(__Apple_CPP__) || defined(__Apple_CC__) || defined(__MACOS_CLASSIC__) )
. . .
#define TARGET_OS_SIMULATOR         0
#define TARGET_OS_EMBEDDED          1 
#define TARGET_IPHONE_SIMULATOR     TARGET_OS_SIMULATOR /* deprecated */
#define TARGET_OS_NANO              TARGET_OS_WATCH /* deprecated */ 
5
Nuthatch

Dans Xcode 7.2 (et plus tôt, mais je n'ai pas testé beaucoup plus tôt), vous pouvez définir un indicateur de construction spécifique à la plate-forme "-D TARGET_IPHONE_SIMULATOR" pour "Any iOS Simulator".

Recherchez dans les paramètres de construction du projet sous "Compilateur Swift - Indicateurs client", puis activez le drapeau dans "Autres drapeaux Swift". Vous pouvez définir un indicateur spécifique à la plate-forme en cliquant sur l'icône "plus" lorsque vous passez la souris sur une configuration de construction.

Cela présente quelques avantages: 1) Vous pouvez utiliser le même test conditionnel ("#if TARGET_IPHONE_SIMULATOR") dans votre code Swift et Objective-C. 2) Vous pouvez compiler des variables qui ne s'appliquent qu'à chaque construction.

Capture d'écran des paramètres de construction Xcode

3
xgerrit

Toutes décrites ici Darwin.TargetConditionals: https://github.com/Apple/Swift-corelibs-foundation/blob/master/CoreFoundation/Base.subproj/SwiftRuntime/TargetConditionals.h

TARGET_OS_SIMULATOR - Generated code will run under a simulator

2
dimpiax

J'ai utilisé ce code ci-dessous dans Swift 3

if TARGET_IPHONE_SIMULATOR == 1 {
    //simulator
} else {
    //device
}
1
ak_ninan

Swift 4:

Actuellement, je préfère utiliser la classe ProcessInfo pour savoir si le périphérique est un simulateur et quel type de périphérique est utilisé:

if let simModelCode = ProcessInfo().environment["SIMULATOR_MODEL_IDENTIFIER"] {
            print("yes is a simulator :\(simModelCode)")
}

Mais, comme vous le savez, simModelCode n'est pas un code confortable pour comprendre immédiatement quel type de simulateur a été lancé. Si vous en avez besoin, vous pouvez essayer de voir cet autre SO answer pour déterminer le iPhone actuel/device et d’avoir une chaîne plus lisible par l’homme.

1
Alessandro Ornano

J'espère que cette extension sera utile

extension UIDevice {
    static var isSimulator: Bool = {
        var isSimulator = false
        #if targetEnvironment(simulator)
        isSimulator = true
        #endif
        return isSimulator
    }()
}

Usage:

if UIDevice.isSimulator {
    print("running on simulator")
}
1
Lucas Chwe

Utilisez ce code ci-dessous:

#if targetEnvironment(simulator)
   // Simulator
#else
   // Device
#endif

Fonctionne pour Swift 4 et Xcode 9.4.1

0
Haroldo Gondim