Come posso determinare a livello di codice se la mia app è in esecuzione nel simulatore di iPhone?


270

Come afferma la domanda, vorrei principalmente sapere se il mio codice è in esecuzione nel simulatore, ma sarei anche interessato a conoscere la versione specifica di iPhone in esecuzione o in fase di simulazione.

EDIT: ho aggiunto la parola "a livello di codice" al nome della domanda. Il punto della mia domanda è essere in grado di includere / escludere dinamicamente il codice a seconda della versione / simulatore in esecuzione, quindi sarei davvero alla ricerca di qualcosa come una direttiva pre-processore che possa fornirmi queste informazioni.


Non sono sicuro che una direttiva pre-processore sia dinamica (anche se potrebbe comunque essere quello che stavi cercando). La direttiva significa che in realtà, quando l'hai costruita, sapevi dove sarebbe finita la corsa.
WiseOldDuck,

Risposte:


356

Già chiesto, ma con un titolo molto diverso.

Quali #define sono impostati da Xcode durante la compilazione per iPhone

Ripeterò la mia risposta da lì:

È nei documenti SDK in "Compilare il codice sorgente in modo condizionale"

La definizione pertinente è TARGET_OS_SIMULATOR, che è definita in /usr/include/TargetConditionals.h all'interno del framework iOS. Nelle versioni precedenti della toolchain, dovevi scrivere:

#include "TargetConditionals.h"

ma questo non è più necessario sull'attuale toolchain (Xcode 6 / iOS8).

Quindi, ad esempio, se si desidera verificare che sia in esecuzione sul dispositivo, è necessario farlo

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

a seconda di quale è appropriato per il tuo caso d'uso.


1
Grazie. Sono d'accordo con te questa è una versione più specifica della tua domanda originale. Se il tuo fosse arrivato nella mia ricerca originale, non avrei nemmeno bisogno di chiedere.
Jeffrey Meyer,

5
Fai attenzione a queste definizioni. Quando compili il codice con la voce di menu 'Progetto> Imposta SDK attivo> Simulatore ...', vengono definite entrambe le variabili TARGET_IPHONE_SIMULATOR come TARGET_OS_IPHONE! Quindi l'unico modo giusto per separare la logica è indicato di seguito da Pete (Grazie amico).
Vadim,

5
Guarda la differenza #if e #ifdef. Per me è stata la causa di comportamenti scorretti.
Anton

7
Forse la necessità di includere TargetConditionals è stata eliminata da quando è stato scritto, ma volevo solo notare che #if TARGET_IPHONE_SIMULATOR funziona senza includere TargetConditionals.h ora.
dal

1
@Dimitris È una buona pratica. Non sai come è stato definito TARGET_OS_SIMULATOR, quindi! (TARGET_OS_SIMULATOR) potrebbe non essere identico a! TARGET_OS_SIMULATOR
Airsource Ltd

106

Codice aggiornato:

Si presume che ciò funzioni ufficialmente.

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

Posta originale (poiché obsoleta)

Questo codice ti dirà se stai eseguendo un simulatore.

#ifdef __i386__
NSLog(@"Running in the simulator");
#else
NSLog(@"Running on a device");
#endif

7
A partire da iOS 8 e Xcode 6.1.1 TARGET_OS_IPHONE è vero sul simulatore.
Malhal

3
questo non si preoccupa più delle versioni XCode più recenti
Fabio Napodano,

1
A meno che tu non sia nel 2016 ed esegua un simulatore a 64 bit. O nel 2019 ed esegui il tuo codice su un iPhone con processore Intel.
gnasher729,

61

Non direttiva pre-processore, ma questo era quello che stavo cercando quando sono arrivato a questa domanda;

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

9
[model compare:iPhoneSimulator] == NSOrderedSamedovrebbe essere scritto come[model isEqualToString:iPhoneSimulator]
user102008

18
O [model hasSuffix:@"Simulator"]se ti interessa solo il "simulatore" in generale, non iPhone o iPad in particolare. Questa risposta non funzionerà per il simulatore di iPad :)
Nuthatch,

Votato perché il commento di Nuthatch rende questa la migliore risposta in toto.
Le Mot Juiced

12
In iOS9, controlla il dispositivo nameinvece dimodel
n.Drake

1
Il codice non funzionerà se un utente aggiunge una Simulatorparola nel nome del suo dispositivo
mbelsky

55

Il modo migliore per farlo è:

#if TARGET_IPHONE_SIMULATOR

e non

#ifdef TARGET_IPHONE_SIMULATOR

poiché è sempre definito: 0 o 1


39

C'È UN MODO MIGLIORE ORA!

A partire da Xcode 9.3 beta 4 è possibile utilizzare #if targetEnvironment(simulator)per verificare.

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

UPDATE
Xcode 10 e iOS 12 SDK supporta anche questo.


1
Questo è l'unico che funziona per me, il resto delle soluzioni non ha funzionato.
Vrutin Rathod,

Nota Questo è solo rapido.
Matt S.

35

In caso di Swift possiamo implementare quanto segue

Siamo in grado di creare struct che ti consente di creare dati strutturati

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

Quindi, se volessimo rilevare se l'app è stata costruita per dispositivo o simulatore in Swift, allora.

if Platform.isSimulator {
    // Do one thing
} else {
    // Do the other
}

A mio avviso, l'implementazione più pulita rappresenta le architetture x86_64 e i386. Mi ha aiutato a superare uno strano dispositivo rispetto al bug del simulatore in Core Data. Tu sei l'uomo!
Iron John Bonney,

5
In Playground riceverai un avviso, "Il codice dopo 'return' non verrà mai eseguito". Quindi penso che #if #else #endifandrà meglio.
DawnSong,

12

Funziona per Swift 5eXcode 11.3.1

Usa questo codice:

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

9

Tutte queste risposte sono buone, ma in qualche modo confonde il neofita come me in quanto non chiarisce il controllo di compilazione e il controllo di runtime. Il preprocessore è prima del tempo di compilazione, ma dovremmo renderlo più chiaro

Questo articolo di blog mostra come rilevare il simulatore di iPhone? chiaramente

Runtime

Prima di tutto, discutiamo brevemente. UIDevice ti fornisce già informazioni sul dispositivo

[[UIDevice currentDevice] model]

ti restituirà "iPhone Simulator" o "iPhone" in base a dove è in esecuzione l'app.

Tempo di compilazione

Tuttavia, ciò che si desidera è utilizzare il tempo di compilazione definito. Perché? Perché compili la tua app rigorosamente per essere eseguita all'interno del simulatore o sul dispositivo. Apple fa una chiamata definita TARGET_IPHONE_SIMULATOR. Quindi diamo un'occhiata al codice:

#if TARGET_IPHONE_SIMULATOR

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

#endif

1
In che modo questo migliora su altre risposte?
mmmmmm,

@Mark Chiarisce un po '
onmyway133

5
Attualmente, in Xcode 7, iOS 9 Simulator [[UIDevice currentDevice] model]sta tornando iPhoneanche al posto di iPhone Simulator. Quindi, penso che questo non sia l'approccio migliore.
eMdOS,

6

Le risposte precedenti sono un po 'datate. Ho scoperto che tutto ciò che devi fare è interrogare la TARGET_IPHONE_SIMULATORmacro ( non è necessario includere altri file di intestazione [supponendo che stai codificando per iOS]).

Ho provato TARGET_OS_IPHONEma ha restituito lo stesso valore (1) quando si esegue su un dispositivo e un simulatore reali, per questo motivo si consiglia di utilizzare TARGET_IPHONE_SIMULATORinvece.


TARGET_OS_IPHONE è per il codice che potrebbe essere eseguito su iOS o su MacOS X. Ovviamente si vorrebbe che quel codice si comportasse in modo "iPhone" su un simulatore.
gnasher729,


4

Ho avuto lo stesso problema, entrambi TARGET_IPHONE_SIMULATORe TARGET_OS_IPHONEsono sempre definiti, e sono impostati su 1. La soluzione di Pete funziona, ovviamente, ma se ti capita mai di basarti su qualcosa di diverso da Intel (improbabile, ma chi lo sa), ecco qualcosa che è sicuro come purché l'hardware dell'iPhone non cambi (quindi il tuo codice funzionerà sempre per gli iPhone attualmente disponibili):

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

Mettilo in un posto comodo, quindi fingi che le TARGET_*costanti siano state definite correttamente.


4

Qualcuno ha considerato la risposta fornita qui ?

Suppongo che sarebbe equivalente all'obiettivo-c

+ (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);
    }
}

4

Per Swift 4.2 / xCode 10

Ho creato un'estensione su UIDevice, quindi posso facilmente chiedere se il simulatore è in esecuzione.

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

Nel mio AppDelegate ad esempio uso questo metodo per decidere se è necessaria la registrazione per la notifica remota, cosa impossibile per il simulatore.

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

    // REGISTER FOR SILENT REMOTE NOTIFICATION
    application.registerForRemoteNotifications()
}

1

Per includere tutti i tipi di "simulatori"

NSString *model = [[UIDevice currentDevice] model];
if([model rangeOfString:@"Simulator" options:NSCaseInsensitiveSearch].location !=NSNotFound)
{
    // we are running in a simulator
}

4
Non ha nulla a che fare con Xcode 7. Se si esegue iOS Simulator con iOS8 (da Xcode 7), funzionerà. Non funzionerà per iOS9 dove [modello [UIDevice currentDevice]] restituisce solo "iPhone" se l'app è stata lanciata da iOS Simulator
Stefan,

perchè no -[NSString containsString]?
Gobe,

1

Con Swift 4.2 (Xcode 10), possiamo farlo

#if targetEnvironment(simulator)
  //simulator code
#else 
  #warning("Not compiling for simulator")
#endif

1
Solo un'altra copia incolla
J. Doe,

0

La mia risposta si basa sulla risposta di @Daniel Magnusson e sui commenti di @Nuthatch e @ n.Drake. e lo scrivo per risparmiare un po 'di tempo per gli utenti veloci che lavorano su iOS9 e versioni successive.

Questo è ciò che ha funzionato per me:

if UIDevice.currentDevice().name.hasSuffix("Simulator"){
    //Code executing on Simulator
} else{
    //Code executing on Device
}

1
Il codice non funzionerà se un utente aggiunge una Simulatorparola nel nome del suo dispositivo
mbelsky

Sfortunatamente con XCode 8 UIDevice.current.nameriporta il nome della macchina su cui è in esecuzione il simulatore (in genere qualcosa come "Simon's MacBook Pro" ora), quindi il test è diventato inaffidabile. Sto ancora cercando un modo pulito per risolverlo.
Michael,

0

/// Restituisce vero se il suo simulatore e non un dispositivo

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

0

Apple ha aggiunto il supporto per verificare che l'app sia destinata al simulatore con quanto segue:

#if targetEnvironment(simulator)
let DEVICE_IS_SIMULATOR = true
#else
let DEVICE_IS_SIMULATOR = false
#endif

0

se nulla ha funzionato, prova questo

public struct Platform {

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

}

-4

Secondo me, la risposta (presentata sopra e ripetuta sotto):

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

è la risposta migliore perché è ovviamente eseguita in RUNTIME anziché essere una DIRETTIVA COMPILE.


11
Non sono d'accordo. Questo codice finisce nel tuo prodotto, mentre una direttiva del compilatore mantiene la routine sul dispositivo superflua.
nove pietre

1
Le direttive del compilatore funzionano perché il dispositivo e i simulatori sono target di compilazione completamente diversi, ovvero non si utilizzerà lo stesso binario su entrambi. Esso deve essere compilato hardware differente, quindi ha senso in questo caso.
Brad Parks,

Essere eseguito su RUNTIME rende la risposta peggiore possibile.
gnasher729,

-4

Questo ha funzionato meglio per me

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


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

}

2
Su Xcode 7.3, il simulatore di iPhone 6 Plus ritorna "iPhone".
Eric,
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.