Comment puis-je déterminer par programme si mon application s'exécute dans le simulateur d'iphone?

270

Comme l'indique la question, je voudrais principalement savoir si mon code fonctionne ou non dans le simulateur, mais je serais également intéressé à connaître la version spécifique de l'iPhone qui fonctionne ou est simulée.

EDIT: J'ai ajouté le mot «par programme» au nom de la question. Le point de ma question est de pouvoir inclure / exclure dynamiquement du code en fonction de la version / du simulateur en cours d'exécution, donc je chercherais vraiment quelque chose comme une directive de pré-processeur qui peut me fournir ces informations.

Jeffrey Meyer
la source
Je ne suis pas sûr qu'une directive de pré-processeur soit dynamique (bien que ce soit peut-être ce que vous cherchiez de toute façon). La directive signifie que vous saviez réellement, quand vous l'avez construite, où elle allait finir par fonctionner.
WiseOldDuck

Réponses:

356

Déjà demandé, mais avec un titre très différent.

Quelles # définitions sont configurées par Xcode lors de la compilation pour iPhone

Je vais répéter ma réponse à partir de là:

C'est dans la documentation du SDK sous "Compilation conditionnelle du code source"

La définition pertinente est TARGET_OS_SIMULATOR, qui est définie dans /usr/include/TargetConditionals.h dans le cadre iOS. Sur les versions antérieures de la chaîne d'outils, vous deviez écrire:

#include "TargetConditionals.h"

mais cela n'est plus nécessaire sur la chaîne d'outils actuelle (Xcode 6 / iOS8).

Ainsi, par exemple, si vous souhaitez vérifier que vous exécutez sur l'appareil, vous devez faire

#if TARGET_OS_SIMULATOR
    // Simulator-specific code
#else
    // Device-specific code
#endif

selon ce qui est approprié pour votre cas d'utilisation.

Airsource Ltd
la source
1
Merci. Je suis d'accord avec vous, il s'agit d'une version plus spécifique de votre question d'origine. Si le vôtre était apparu dans ma recherche initiale, je n'aurais même pas eu besoin de demander.
Jeffrey Meyer
5
Soyez prudent avec ces définitions. Lorsque vous compilez du code avec l'élément de menu «Projet> Définir le SDK actif> Simulateur…», les variables TARGET_IPHONE_SIMULATOR et TARGET_OS_IPHONE sont toutes deux définies! Donc, la seule bonne façon de séparer la logique est indiquée ci-dessous par Pete (merci mec).
Vadim
5
Regardez la différence #if et #ifdef. Pour moi, c'était la cause d'un comportement incorrect.
Anton
7
Peut-être la nécessité d'inclure TargetConditionals a été évitée depuis que cela a été écrit, mais je voulais juste noter que #if TARGET_IPHONE_SIMULATOR fonctionne sans inclure TargetConditionals.h maintenant.
dmur
1
@Dimitris C'est une bonne pratique. Vous ne savez pas comment TARGET_OS_SIMULATOR a été défini, donc! (TARGET_OS_SIMULATOR) peut ne pas être identique à! TARGET_OS_SIMULATOR
Airsource Ltd
106

Code mis à jour:

Il est censé fonctionner officiellement.

#if TARGET_IPHONE_SIMULATOR
NSString *hello = @"Hello, iPhone simulator!";
#elif TARGET_OS_IPHONE
NSString *hello = @"Hello, device!";
#else
NSString *hello = @"Hello, unknown target!";
#endif

Message d'origine (déconseillé depuis)

Ce code vous indiquera si vous utilisez un simulateur.

#ifdef __i386__
NSLog(@"Running in the simulator");
#else
NSLog(@"Running on a device");
#endif
Pete
la source
7
Depuis iOS 8 et Xcode 6.1.1, TARGET_OS_IPHONE est vrai sur le simulateur.
malhal
3
cela ne marche plus sur les nouvelles versions de XCode
Fabio Napodano
1
Sauf si vous êtes en 2016 et exécutez un simulateur 64 bits. Ou en 2019 et exécutez votre code sur un iPhone avec processeur Intel.
gnasher729
61

Pas de directive pré-processeur, mais c'est ce que je cherchais quand je suis arrivé à cette question;

NSString *model = [[UIDevice currentDevice] model];
if ([model isEqualToString:@"iPhone Simulator"]) {
    //device is simulator
}
Daniel Magnusson
la source
9
[model compare:iPhoneSimulator] == NSOrderedSamedevrait être écrit comme[model isEqualToString:iPhoneSimulator]
user102008
18
Ou [model hasSuffix:@"Simulator"]si vous ne vous souciez que du "simulateur" en général, pas de l' iPhone ou de l' iPad en particulier. Cette réponse ne fonctionnera pas pour le simulateur iPad :)
Nuthatch
Voté parce que le commentaire de Nuthatch en fait la meilleure réponse au total.
Le Mot Juiced
12
Dans iOS9, vérifiez l'appareil name au lieu demodel
n.Drake
1
Le code ne fonctionnera pas si un utilisateur ajoute un Simulatormot dans le nom de son appareil
mbelsky
55

La meilleure façon de procéder est:

#if TARGET_IPHONE_SIMULATOR

et pas

#ifdef TARGET_IPHONE_SIMULATOR

puisque son toujours défini: 0 ou 1

Taranfx
la source
39

IL Y A UNE MEILLEURE FAÇON MAINTENANT!

À partir de Xcode 9.3 beta 4, vous pouvez utiliser #if targetEnvironment(simulator)pour vérifier.

#if targetEnvironment(simulator)
//Your simulator code
#endif

UPDATE
Xcode 10 et iOS 12 SDK le prennent également en charge.

Stefan Vasiljevic
la source
1
C'est le seul qui fonctionne pour moi, le reste des solutions n'a pas fonctionné.
Vrutin Rathod
Remarque Ce n'est que rapide.
Matt S.
35

Dans le cas de Swift, nous pouvons implémenter ce qui suit

Nous pouvons créer une structure qui vous permet de créer une donnée structurée

struct Platform {
    static var isSimulator: Bool {
        #if targetEnvironment(simulator)
            // We're on the simulator
            return true
        #else
            // We're on a device
             return false
        #endif
    }
}

Ensuite, si nous voulions détecter si l'application est en cours de création pour un appareil ou un simulateur dans Swift, alors.

if Platform.isSimulator {
    // Do one thing
} else {
    // Do the other
}
Nischal Hada
la source
Implémentation la plus propre à mon avis, et elle représente les architectures x86_64 et i386. M'a aidé à surmonter un bug étrange entre l'appareil et le simulateur dans Core Data. Tu es l'homme!
Iron John Bonney
5
Dans Playground, vous obtiendrez un avertissement, "Le code après le 'retour' ne sera jamais exécuté". Je pense donc que ce #if #else #endifsera mieux.
DawnSong
12

Fonctionne pour Swift 5etXcode 11.3.1

Utilisez ce code:

#if targetEnvironment(simulator)
   // Simulator
#else
   // Device
#endif
Haroldo Gondim
la source
9

Toutes ces réponses sont bonnes, mais cela dérange en quelque sorte les débutants comme moi car cela ne clarifie pas le contrôle de compilation et le contrôle d'exécution. Le préprocesseur est avant le moment de la compilation, mais nous devons le rendre plus clair

Cet article de blog montre comment détecter le simulateur iPhone? clairement

Durée

Tout d'abord, discutons brièvement. UIDevice vous fournit déjà des informations sur l'appareil

[[UIDevice currentDevice] model]

vous renverra "iPhone Simulator" ou "iPhone" selon l'endroit où l'application est en cours d'exécution.

Compiler le temps

Cependant, ce que vous voulez, c'est utiliser le temps de compilation définit. Pourquoi? Parce que vous compilez votre application strictement pour qu'elle soit exécutée à l'intérieur du simulateur ou sur l'appareil. Apple fait un définir appelé TARGET_IPHONE_SIMULATOR. Regardons donc le code:

#if TARGET_IPHONE_SIMULATOR

NSLog(@"Running in Simulator - no app store or giro");

#endif
onmyway133
la source
1
Comment cela améliore-t-il les autres réponses?
mmmmmm
@Mark Il clarifie un peu
onmyway133
5
Actuellement, dans Xcode 7, iOS 9 Simulator [[UIDevice currentDevice] model]revient iPhoneégalement à la place de iPhone Simulator. Donc, je pense que ce n'est pas la meilleure approche.
eMdOS
6

Les réponses précédentes sont un peu datées. J'ai trouvé que tout ce que vous devez faire est d'interroger la TARGET_IPHONE_SIMULATORmacro ( pas besoin d'inclure d'autres fichiers d'en-tête [en supposant que vous codez pour iOS]).

J'ai essayé TARGET_OS_IPHONEmais il a retourné la même valeur (1) lors de l'exécution sur un appareil et un simulateur réels, c'est pourquoi je recommande d'utiliser à la TARGET_IPHONE_SIMULATORplace.

Stunner
la source
TARGET_OS_IPHONE est pour le code qui pourrait fonctionner sur iOS ou sur MacOS X. Évidemment, vous voudriez que ce code se comporte de la manière "iPhone" sur un simulateur.
gnasher729
4

J'ai eu le même problème, les deux TARGET_IPHONE_SIMULATORet TARGET_OS_IPHONEsont toujours définis, et sont définis sur 1. La solution de Pete fonctionne, bien sûr, mais s'il vous arrive de construire sur autre chose qu'Intel (peu probable, mais qui sait), voici quelque chose qui est sûr comme tant que le matériel de l'iphone ne change pas (donc votre code fonctionnera toujours pour les iphones actuellement disponibles):

#if defined __arm__ || defined __thumb__
#undef TARGET_IPHONE_SIMULATOR
#define TARGET_OS_IPHONE
#else
#define TARGET_IPHONE_SIMULATOR 1
#undef TARGET_OS_IPHONE
#endif

Mettez cela dans un endroit pratique, puis faites comme si le TARGET_* constantes étaient correctement définies.


la source
4

Quelqu'un at-il considéré la réponse fournie ici ?

Je suppose que l'équivalent objectif c serait

+ (BOOL)isSimulator {
    NSOperatingSystemVersion ios9 = {9, 0, 0};
    NSProcessInfo *processInfo = [NSProcessInfo processInfo];
    if ([processInfo isOperatingSystemAtLeastVersion:ios9]) {
        NSDictionary<NSString *, NSString *> *environment = [processInfo environment];
        NSString *simulator = [environment objectForKey:@"SIMULATOR_DEVICE_NAME"];
        return simulator != nil;
    } else {
        UIDevice *currentDevice = [UIDevice currentDevice];
        return ([currentDevice.model rangeOfString:@"Simulator"].location != NSNotFound);
    }
}
Vijay Sharma
la source
4

Pour Swift 4.2 / xCode 10

J'ai créé une extension sur UIDevice, donc je peux facilement demander si le simulateur fonctionne.

// UIDevice+CheckSimulator.swift

import UIKit

extension UIDevice {

    /// Checks if the current device that runs the app is xCode's simulator
    static func isSimulator() -> Bool {        
        #if targetEnvironment(simulator)
            return true
        #else
            return false
        #endif
    }
}

Dans mon AppDelegate par exemple, j'utilise cette méthode pour décider si l'enregistrement pour une notification à distance est nécessaire, ce qui n'est pas possible pour le simulateur.

// CHECK FOR REAL DEVICE / OR SIMULATOR
if UIDevice.isSimulator() == false {

    // REGISTER FOR SILENT REMOTE NOTIFICATION
    application.registerForRemoteNotifications()
}
LukeSideWalker
la source
1

Pour inclure tous les types de "simulateurs"

NSString *model = [[UIDevice currentDevice] model];
if([model rangeOfString:@"Simulator" options:NSCaseInsensitiveSearch].location !=NSNotFound)
{
    // we are running in a simulator
}
jeffr
la source
4
Cela n'a rien à voir avec Xcode 7. Si vous exécutez iOS Simulator avec iOS8 (à partir de Xcode 7), cela fonctionnera. Cela ne fonctionnera pas pour iOS9 où [[UIDevice currentDevice] model] ne renvoie que "iPhone" si l'application a été lancée depuis iOS Simulator
Stefan
pourquoi pas -[NSString containsString]?
Gobe
1

Avec Swift 4.2 (Xcode 10), nous pouvons le faire

#if targetEnvironment(simulator)
  //simulator code
#else 
  #warning("Not compiling for simulator")
#endif
iHS
la source
1
Juste un autre copier-coller
J. Doe
0

Ma réponse est basée sur la réponse de @Daniel Magnusson et les commentaires de @Nuthatch et @ n.Drake. et je l'écris pour gagner du temps pour les utilisateurs rapides travaillant sur iOS9 et versions ultérieures.

C'est ce qui a fonctionné pour moi:

if UIDevice.currentDevice().name.hasSuffix("Simulator"){
    //Code executing on Simulator
} else{
    //Code executing on Device
}
euthimis87
la source
1
Le code ne fonctionnera pas si un utilisateur ajoute un Simulatormot dans le nom de son appareil
mbelsky
Malheureusement, avec XCode 8 UIDevice.current.name, le nom de la machine sur laquelle le simulateur est exécuté (généralement quelque chose comme "MacBook Pro de Simon" maintenant) indique que le test n'est plus fiable. Je cherche toujours un moyen propre de le réparer.
Michael
0

/// Renvoie vrai si son simulateur et non un appareil

public static var isSimulator: Bool {
    #if (arch(i386) || arch(x86_64)) && os(iOS)
        return true
    #else
        return false
    #endif
}
Pratyush Pratik
la source
0

Apple a ajouté un support pour vérifier que l'application est ciblée pour le simulateur avec les éléments suivants:

#if targetEnvironment(simulator)
let DEVICE_IS_SIMULATOR = true
#else
let DEVICE_IS_SIMULATOR = false
#endif
David Corbin
la source
0

si rien ne fonctionne, essayez ceci

public struct Platform {

    public static var isSimulator: Bool {
        return TARGET_OS_SIMULATOR != 0 // Use this line in Xcode 7 or newer
    }

}
Aklesh Rathaur
la source
-4

À mon avis, la réponse (présentée ci-dessus et répétée ci-dessous):

NSString *model = [[UIDevice currentDevice] model];
if ([model isEqualToString:@"iPhone Simulator"]) {
    //device is simulator
}

est la meilleure réponse car elle est évidemment exécutée au RUNTIME par rapport à une DIRECTIVE DE COMPILATION.

user1686700
la source
11
Je ne suis pas d'accord. Ce code se retrouve dans votre produit, tandis qu'une directive du compilateur empêche la routine - sur le périphérique inutile -.
neuf pierres
1
Les directives du compilateur fonctionnent parce que le périphérique et les simulateurs sont des cibles de compilation complètement différentes - c'est-à-dire que vous n'utiliseriez pas le même binaire sur les deux. Il doit être compilé sur un matériel différent, il est donc logique dans ce cas.
Brad Parks
Être exécuté à RUNTIME en fait la pire réponse possible.
gnasher729
-4

Cela a fonctionné le mieux pour moi

NSString *name = [[UIDevice currentDevice] name];


if ([name isEqualToString:@"iPhone Simulator"]) {

}
Mani
la source
2
Sur Xcode 7.3, l'iPhone 6 Plus Simulator revient "iPhone".
Eric