Jak mogę programowo ustalić, czy moja aplikacja działa w symulatorze iPhone'a?

270

Jak mówi pytanie, chciałbym głównie wiedzieć, czy mój kod działa w symulatorze, ale chciałbym również wiedzieć, która konkretna wersja iPhone'a jest uruchomiona lub symulowana.

EDYCJA: Dodałem słowo „programowo” do nazwy pytania. Chodzi mi o to, aby móc dynamicznie dołączać / wykluczać kod w zależności od uruchomionej wersji / symulatora, więc naprawdę szukałbym czegoś w rodzaju dyrektywy przedprocesorowej, która mogłaby dostarczyć mi tych informacji.

Jeffrey Meyer
źródło
Nie jestem pewien, czy dyrektywa przedprocesowa jest dynamiczna (choć i tak może być tym, czego szukałeś). Dyrektywa oznacza, że ​​tak naprawdę wiedziałeś, kiedy ją zbudowałeś, dokąd skończy.
WiseOldDuck

Odpowiedzi:

356

Już zadałem, ale z zupełnie innym tytułem.

Co #defines są konfigurowane przez Xcode podczas kompilacji na iPhone'a

Stamtąd powtórzę moją odpowiedź:

Jest w dokumentacji SDK w sekcji „Warunkowe kompilowanie kodu źródłowego”

Odpowiednia definicja to TARGET_OS_SIMULATOR, który jest zdefiniowany w /usr/include/TargetConditionals.h w ramach systemu iOS. We wcześniejszych wersjach zestawu narzędzi trzeba było napisać:

#include "TargetConditionals.h"

ale nie jest to już konieczne w bieżącym łańcuchu narzędzi (Xcode 6 / iOS8).

Na przykład, jeśli chcesz sprawdzić, czy działasz na urządzeniu, powinieneś to zrobić

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

w zależności od tego, który jest odpowiedni dla twojego przypadku użycia.

Airsource Ltd
źródło
1
Dzięki. Zgadzam się z tobą, to jest bardziej szczegółowa wersja twojego pierwotnego pytania. Gdyby twoje pojawiło się w moich pierwotnych poszukiwaniach, nie musiałbym nawet pytać.
Jeffrey Meyer,
5
Uważaj na te definicje. Kiedy kompilujesz kod z pozycją menu „Projekt> Ustaw aktywny SDK> Symulator…”, jako TARGET_IPHONE_SIMULATOR jako zmienne TARGET_OS_IPHONE są zdefiniowane! Więc jedyny właściwy sposób na oddzielenie logiki jest wskazany poniżej przez Pete'a (Dzięki stary).
Vadim
5
Zobacz różnicę #if i #ifdef. Dla mnie była to przyczyna nieprawidłowego zachowania.
Anton
7
Być może potrzeba uwzględnienia TargetConditionals została wyeliminowana, odkąd to zostało napisane, ale chcę tylko zauważyć, że #if TARGET_IPHONE_SIMULATOR działa teraz bez uwzględniania TargetConditionals.h.
dmur
1
@Dimitris To dobra praktyka. Nie wiesz, jak zdefiniowano TARGET_OS_SIMULATOR, więc! (TARGET_OS_SIMULATOR) może nie być identyczny z! TARGET_OS_SIMULATOR
Airsource Ltd
106

Zaktualizowany kod:

Podobno działa to oficjalnie.

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

Oryginalny post (od wycofania)

Ten kod powie ci, czy korzystasz z symulatora.

#ifdef __i386__
NSLog(@"Running in the simulator");
#else
NSLog(@"Running on a device");
#endif
Pete
źródło
7
Począwszy od iOS 8 i Xcode 6.1.1, TARGET_OS_IPHONE jest prawdziwy na symulatorze.
malhal
3
to już nie działa w nowszych wersjach XCode
Fabio Napodano,
1
Chyba że jesteś w 2016 roku i nie uruchomisz 64-bitowego symulatora. Lub w 2019 roku i uruchom kod na iPhonie z procesorem Intel.
gnasher729,
61

Nie była to dyrektywa przedprocesowa, ale tego właśnie szukałam, kiedy doszłam do tego pytania;

NSString *model = [[UIDevice currentDevice] model];
if ([model isEqualToString:@"iPhone Simulator"]) {
    //device is simulator
}
Daniel Magnusson
źródło
9
[model compare:iPhoneSimulator] == NSOrderedSamenależy zapisać jako[model isEqualToString:iPhoneSimulator]
użytkownik102008,
18
Lub [model hasSuffix:@"Simulator"]jeśli zależy Ci tylko na „symulatorze”, a nie na iPhonie ani iPadzie . Ta odpowiedź nie działa na symulatorze iPada :)
Nuthatch
Głosowano, ponieważ komentarz Nuthatch sprawia, że ​​jest to najlepsza odpowiedź w całości.
Le Mot Juiced
12
W iOS9 sprawdź urządzenie namezamiastmodel
n.Drake
1
Kod nie będzie działał, jeśli użytkownik doda Simulatorsłowo w nazwie swojego urządzenia
mbelsky
55

Najlepszym sposobem na to jest:

#if TARGET_IPHONE_SIMULATOR

i nie

#ifdef TARGET_IPHONE_SIMULATOR

ponieważ zawsze jest zdefiniowany: 0 lub 1

Taranfx
źródło
39

TERAZ JEST LEPSZY SPOSÓB!

Począwszy od wersji Xcode 9.3 beta 4 można #if targetEnvironment(simulator)to sprawdzić.

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

AKTUALIZACJA
Xcode 10 i iOS 12 SDK również to obsługuje.

Stefan Vasiljevic
źródło
1
To jedyne, które działa dla mnie, reszta rozwiązań nie działała.
Vrutin Rathod
Uwaga Jest to tylko szybkie.
Matt S.
35

W przypadku Swift możemy wprowadzić następujące

Możemy stworzyć strukturę, która pozwala tworzyć dane strukturalne

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

Następnie, jeśli chcemy wykryć, czy w Swift jest budowana aplikacja na urządzenie lub symulator.

if Platform.isSimulator {
    // Do one thing
} else {
    // Do the other
}
Nischal Hada
źródło
Moim zdaniem najczystsza implementacja, która odpowiada architekturze x86_64 i i386. Pomógł mi pokonać dziwny błąd urządzenia vs. symulator w Core Data. Jesteś mężczyzną!
Iron John Bonney
5
Na placu zabaw pojawi się ostrzeżenie: „Kod po„ powrocie ”nigdy nie zostanie wykonany”. Więc myślę, że #if #else #endifbędzie lepiej.
DawnSong
12

Działa dla Swift 5iXcode 11.3.1

Użyj tego kodu:

#if targetEnvironment(simulator)
   // Simulator
#else
   // Device
#endif
Haroldo Gondim
źródło
9

Wszystkie te odpowiedzi są dobre, ale jakoś wprawia w zakłopotanie początkującego, jak ja, ponieważ nie wyjaśnia kontroli kompilacji i kontroli środowiska uruchomieniowego. Preprocesor jest przed czasem kompilacji, ale powinniśmy to wyjaśnić

W tym artykule na blogu pokazano, jak wykryć symulator iPhone'a? Wyraźnie

Środowisko wykonawcze

Po pierwsze, omówmy krótko. UIDevice zapewnia już informacje o urządzeniu

[[UIDevice currentDevice] model]

zwróci Ci „iPhone Simulator” lub „iPhone” w zależności od tego, gdzie aplikacja jest uruchomiona.

Czas kompilacji

Jednak chcesz zdefiniować czas kompilacji. Czemu? Ponieważ twoją aplikację kompilujesz wyłącznie w celu uruchomienia jej w symulatorze lub na urządzeniu. Apple tworzy definicję o nazwie TARGET_IPHONE_SIMULATOR. Spójrzmy więc na kod:

#if TARGET_IPHONE_SIMULATOR

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

#endif
onmyway133
źródło
1
Jak to się poprawia w przypadku innych odpowiedzi?
user151019,
@ Mark To trochę wyjaśnia
onmyway133
5
Obecnie w Xcode 7 [[UIDevice currentDevice] model]powraca iPhonerównież symulator iOS 9 iPhone Simulator. Myślę więc, że to nie jest najlepsze podejście.
eMdOS,
6

Poprzednie odpowiedzi są trochę przestarzałe. Odkryłem, że wszystko, co musisz zrobić, to wysłać zapytanie do TARGET_IPHONE_SIMULATORmakra ( nie musisz dołączać żadnych innych plików nagłówkowych [zakładając, że kodujesz dla iOS]).

Próbowałem, TARGET_OS_IPHONEale zwróciło tę samą wartość (1) podczas działania na rzeczywistym urządzeniu i symulatorze, dlatego zalecam użycie TARGET_IPHONE_SIMULATORzamiast tego.

Szlagier
źródło
TARGET_OS_IPHONE dotyczy kodu, który może działać na iOS lub MacOS X. Oczywiście chciałbyś, aby ten kod zachowywał się tak jak iPhone na symulatorze.
gnasher729,
6

W krótkim:

#if (arch(i386) || arch(x86_64))
...            
#endif

Od Wykryj, czy aplikacja Swift buduje aplikację na urządzenie lub symulator

CedricSoubrie
źródło
Aby odróżnić aplikacje dla komputerów Mac: #if (arch (i386) || arch (x86_64)) &&! Os (OSX) // korzystamy z symulatora działającego na komputerze Mac, a nie aplikacji Mac. (W przypadku kodu między platformami zawartego w celach Mac)
Bobjt
4

Miałem ten sam problem, jak TARGET_IPHONE_SIMULATORi TARGET_OS_IPHONEzawsze są zdefiniowane i są ustawione na 1. prac rozwiązań Pete'a, oczywiście, ale jeśli kiedykolwiek zdarzy się budować na czymś innym niż Intel (mało prawdopodobne, ale kto wie), tutaj jest coś, co jest bezpieczne, jak tak długo, jak sprzęt iPhone'a się nie zmieni (więc Twój kod zawsze będzie działał dla obecnie dostępnych telefonów):

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

Umieść to w dogodnym miejscu, a następnie udawaj, że TARGET_*stałe zostały poprawnie zdefiniowane.


źródło
4

Czy ktoś rozważał udzieloną tutaj odpowiedź ?

Przypuszczam, że byłby to odpowiednik celu-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);
    }
}
Vijay Sharma
źródło
4

Dla Swift 4.2 / xCode 10

Utworzyłem rozszerzenie na UIDevice, dzięki czemu mogę łatwo zapytać, czy symulator jest uruchomiony.

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

Na przykład w aplikacji AppDelegate korzystam z tej metody, aby zdecydować, czy konieczna jest rejestracja w celu zdalnego powiadomienia, co nie jest możliwe w symulatorze.

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

    // REGISTER FOR SILENT REMOTE NOTIFICATION
    application.registerForRemoteNotifications()
}
LukeSideWalker
źródło
1

Aby uwzględnić wszystkie typy „symulatorów”

NSString *model = [[UIDevice currentDevice] model];
if([model rangeOfString:@"Simulator" options:NSCaseInsensitiveSearch].location !=NSNotFound)
{
    // we are running in a simulator
}
jeffr
źródło
4
Nie ma to nic wspólnego z Xcode 7. Jeśli uruchomisz iOS Simulator z iOS8 (od Xcode 7), to zadziała. Nie będzie działać na iOS9, gdzie [model [UIDevice currentDevice]] zwraca tylko „iPhone”, jeśli aplikacja została uruchomiona z iOS Simulator
tesla
dlaczego nie -[NSString containsString]?
Gobe
1

Dzięki Swift 4.2 (Xcode 10) możemy to zrobić

#if targetEnvironment(simulator)
  //simulator code
#else 
  #warning("Not compiling for simulator")
#endif
iHS
źródło
1
Po prostu kolejna kopia
J. Doe
0

Moja odpowiedź oparta jest na odpowiedzi @Daniel Magnusson i komentarzach @Nuthatch i @ n.Drake. i piszę to, aby zaoszczędzić trochę czasu szybkim użytkownikom pracującym na iOS9 i nowszych.

Oto, co zadziałało dla mnie:

if UIDevice.currentDevice().name.hasSuffix("Simulator"){
    //Code executing on Simulator
} else{
    //Code executing on Device
}
euthimis87
źródło
1
Kod nie będzie działał, jeśli użytkownik doda Simulatorsłowo w nazwie swojego urządzenia
mbelsky
Niestety z XCode 8 UIDevice.current.namezgłasza nazwę maszyny, na której działa Symulator (zwykle coś takiego jak „MacBook Pro Simona”), więc test stał się niewiarygodny. Nadal szukam czystego sposobu, aby to naprawić.
Michael
0

/// Zwraca wartość true, jeśli jest to symulator, a nie urządzenie

public static var isSimulator: Bool {
    #if (arch(i386) || arch(x86_64)) && os(iOS)
        return true
    #else
        return false
    #endif
}
Pratyush Pratik
źródło
0

Firma Apple dodała obsługę sprawdzania, czy aplikacja jest przeznaczona dla symulatora, w następujący sposób:

#if targetEnvironment(simulator)
let DEVICE_IS_SIMULATOR = true
#else
let DEVICE_IS_SIMULATOR = false
#endif
David Corbin
źródło
0

jeśli nic nie działało, spróbuj tego

public struct Platform {

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

}
Aklesh Rathaur
źródło
-4

Moim zdaniem odpowiedź (przedstawiona powyżej i powtórzona poniżej):

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

jest najlepszą odpowiedzią, ponieważ jest oczywiście wykonywana w RUNTIME w porównaniu do DYREKTYWY KOMPILACYJNEJ.

użytkownik1686700
źródło
11
Nie zgadzam się. Ten kod kończy się w twoim produkcie, podczas gdy dyrektywa kompilatora uniemożliwia rutynową pracę urządzenia.
dziewięć kamieni
1
Dyrektywy kompilatora działają, ponieważ urządzenie i symulatory są całkowicie różnymi celami kompilacji - tzn. Nie użyłbyś tego samego pliku binarnego na obu. To musi być skompilowany na inny sprzęt, więc ma to sens w tym przypadku.
Brad Parks
Wykonanie w RUNTIME sprawia, że ​​jest to najgorsza możliwa odpowiedź.
gnasher729,
-4

To działało dla mnie najlepiej

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


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

}
Mani
źródło
2
Na Xcode 7.3 powraca symulator iPhone'a 6 Plus "iPhone".
Eric