So erkennen Sie, ob in Swift eine App für ein Gerät oder einen Simulator erstellt wird

277

In Objective-C können wir mithilfe von Makros feststellen, ob eine App für ein Gerät oder einen Simulator erstellt wird:

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

Dies sind Kompilierungszeitmakros, die zur Laufzeit nicht verfügbar sind.

Wie kann ich dasselbe in Swift erreichen?

RaffAl
quelle
2
Auf diese Weise wird der Simulator oder ein reales Gerät zur Laufzeit in Objective-C nicht erkannt. Dies sind Compiler-Direktiven, die je nach Build zu unterschiedlichem Code führen.
rmaddy
Vielen Dank. Ich habe meine Frage bearbeitet.
RaffAl
9
Die am höchsten abgestimmten Antworten sind nicht der beste Weg, um dieses Problem zu lösen! Die Antwort von mbelsky (derzeit sehr weit unten) ist die einzige Lösung, die ohne Fallstricke auskommt. Sogar Greg Parker von Apple schlug vor, dies so zu tun: lists.swift.org/pipermail/swift-evolution/Week-of-Mon-20160125/…
jan.vogt
1
AUCH IN GROSSBUCHSTABEN IST ES NAIV ZU EMPFEHLEN, DASS MIT EINEM RUNTIME CHECK ALLES FALSCH IST. Vorschläge von Apple-Ingenieuren sind oft schlecht durchdachter Müll oder gelten nur in bestimmten Situationen, sodass initself weniger als nichts bedeutet.
Fattie
1
@Fattie: Es wäre interessant zu wissen, warum keine der gegebenen Antworten Ihren Bedürfnissen entspricht und worauf Sie genau hoffen, indem Sie das Kopfgeld anbieten.
Martin R

Antworten:

363

Update 30/01/19

Während diese Antwort möglicherweise funktioniert, besteht die empfohlene Lösung für eine statische Überprüfung (wie von mehreren Apple-Ingenieuren geklärt) darin, ein benutzerdefiniertes Compiler-Flag für iOS-Simulatoren zu definieren. Ausführliche Anweisungen dazu finden Sie in der Antwort von @ mbelsky .

Ursprüngliche Antwort

Wenn Sie eine statische Überprüfung benötigen (z. B. keine Laufzeit, wenn / sonst), können Sie den Simulator nicht direkt erkennen, aber Sie können iOS auf einer Desktop-Architektur wie folgt erkennen

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

Nach Swift 4.1 Version

Die letzte Verwendung, jetzt direkt für alle in einer Bedingung für alle Arten von Simulatoren, muss nur eine Bedingung anwenden -

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

Weitere Informationen finden Sie im Swift- Vorschlag SE-0190


Für ältere Version -

Dies ist auf einem Gerät eindeutig falsch, gibt jedoch für den iOS-Simulator den Wert true zurück, wie in der Dokumentation angegeben :

Die arch (i386) Build-Konfiguration gibt true zurück, wenn der Code für den 32-Bit-iOS-Simulator kompiliert wird.

Wenn Sie für einen anderen Simulator als iOS entwickeln, können Sie einfach den osParameter variieren : z

Erkennen Sie den watchOS- Simulator

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

Erkennen Sie den tvOS- Simulator

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

Oder erkennen Sie sogar einen Simulator

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

Wenn Sie stattdessen mit einer Laufzeitprüfung einverstanden sind, können Sie die TARGET_OS_SIMULATORVariable (oder TARGET_IPHONE_SIMULATORin iOS 8 und niedriger) überprüfen, was auf einem Simulator der Fall ist.

Bitte beachten Sie, dass dies anders und etwas eingeschränkter ist als die Verwendung eines Präprozessor-Flags. Zum Beispiel können Sie es nicht an Orten verwenden, an denen a if/elsesyntaktisch ungültig ist (z. B. außerhalb von Funktionsbereichen).

Angenommen, Sie möchten unterschiedliche Importe auf dem Gerät und im Simulator durchführen. Dies ist bei einer dynamischen Prüfung unmöglich, während es bei einer statischen Prüfung trivial ist.

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

Da das Flag durch den schnellen Präprozessor durch ein 0oder ein ersetzt 1wird, gibt if/elseder Compiler eine Warnung vor nicht erreichbarem Code aus , wenn Sie es direkt in einem Ausdruck verwenden.

Um diese Warnung zu umgehen, lesen Sie eine der anderen Antworten.

Gabriele Petronella
quelle
1
Lesen Sie hier mehr . Und um noch restriktiver zu sein, könnten Sie verwenden arch(i386) && os(iOS).
Ahruss
1
Das hat bei mir nicht funktioniert. Ich musste sowohl nach i386 als auch nach x86_64
suchen
3
Diese Antwort ist nicht der beste Weg, um dieses Problem zu lösen! Die Antwort von mbelsky (derzeit sehr weit unten) ist die einzige Lösung, die ohne Fallstricke auskommt. Sogar Greg Parker von Apple schlug vor, dies so zu tun: lists.swift.org/pipermail/swift-evolution/Week-of-Mon-20160125/…
jan.vogt
2
@russbishop Dies erwies sich als hilfreicher Rat für Hunderte von Menschen, der eine fehlende API kompensierte. Anstatt die Antwort zu entführen, indem Sie oben einen Kommentar unterschreiben, kommunizieren Sie einfach. Ich habe die Antwort aktualisiert, um zu verdeutlichen, dass dies keine aktuelle Lösung mehr ist, und ich habe einen Link zu der Lösung bereitgestellt, die korrekter aussieht.
Gabriele Petronella
9
In Swift 4.1 können Sie sagen #if targetEnvironment(simulator):) ( github.com/apple/swift-evolution/blob/master/proposals/… )
Hamish
172

FÜR SWIFT veraltet 4.1. Verwenden Sie #if targetEnvironment(simulator)stattdessen. Quelle

Um den Simulator in Swift zu erkennen, können Sie die Build-Konfiguration verwenden:

  • Definieren Sie diese Konfiguration -D IOS_SIMULATOR in Swift Compiler - Benutzerdefinierte Flags> Andere Swift Flags
  • Wählen Sie in dieser Dropdown- Liste ein beliebiges iOS Simulator SDK ausDropdown-Liste

Jetzt können Sie diese Anweisung verwenden, um den Simulator zu erkennen:

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

Sie können auch die UIDevice-Klasse erweitern:

extension UIDevice {
    var isSimulator: Bool {
        #if IOS_SIMULATOR
            return true
        #else
            return false
        #endif
    }
}
// Example of usage: UIDevice.current.isSimulator
mbelsky
quelle
8
Dies sollte die beste Antwort sein! Sogar Greg Parker von Apple schlug dies vor: lists.swift.org/pipermail/swift-evolution/Week-of-Mon-20160125/…
jan.vogt
1
Nutzungsupdate für Swift 3: UIDevice.current.isSimulator
Tylernol
1
Darf ich fragen, warum dies nicht funktioniert, wenn ich dies unter Release hinzufüge ?
William Hu
3
Dies ist die einzig richtige Antwort. Sie können dies auch in xcconfigDateien einrichten, indem Sie OTHER_SWIFT_FLAGS = TARGET_OS_EMBEDDEDund OTHER_SWIFT_FLAGS[sdk=embeddedsimulator*] = TARGET_OS_SIMULATORfür den Simulator überschreiben.
Russbischof
1
Unter Xcode 9.2 konnte diese Antwort manchmal nicht kompiliert werden. Das Entfernen des "-" vor dem "D" löste das Problem für mich.
Blake
160

Die Informationen wurden am 20. Februar 2018 aktualisiert

Es sieht so aus, als hätte @russbishop eine maßgebliche Antwort, die diese Antwort "falsch" macht - obwohl sie lange Zeit zu funktionieren schien.

Ermitteln Sie, ob in Swift eine App für ein Gerät oder einen Simulator erstellt wird

Vorherige Antwort

Basierend auf der Antwort von @ WZW und den Kommentaren von @ Pang habe ich eine einfache Dienstprogrammstruktur erstellt. Diese Lösung vermeidet Warnungen, die durch die Antwort von @ WZW erzeugt werden.

import Foundation

struct Platform {

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

}

Anwendungsbeispiel:

if Platform.isSimulator {
    print("Running on Simulator")
}
Daniel
quelle
10
Viel bessere Lösung als akzeptiert. In der Tat, wenn Apple eines Tages (obwohl es sehr unwahrscheinlich ist) beschließt, i386 oder x85_64 auf iOS-Geräten zu verwenden, wird die akzeptierte Antwort nicht funktionieren ... oder selbst wenn Desktop-Computer einen neuen Prozess erhalten!
Frizlab
2
Bestätigt, dass dies unter Xcode 7 perfekt funktioniert: public let IS_SIMULATOR = (TARGET_OS_SIMULATOR != 0)... dasselbe, vereinfacht. +1 danke
Dan Rosenstark
1
@ Daniel Das funktioniert gut und ist eigentlich einfacher als meine Lösung. Es ist jedoch erwähnenswert, dass es begrenzter ist als ein tatsächlicher Präprozessorschritt. Wenn ein Teil des Codes nicht im Ziel enthalten sein soll (z. B. wenn Sie zur Kompilierungszeit zwischen zwei Importen wählen möchten), müssen Sie eine statische Prüfung durchführen. Ich habe meine Antwort bearbeitet, um diesen Unterschied hervorzuheben.
Gabriele Petronella
Diese Antwort ist nicht der beste Weg, um dieses Problem zu lösen! Die Antwort von mbelsky (derzeit sehr weit unten) ist die einzige Lösung, die ohne Fallstricke auskommt. Sogar Greg Parker von Apple schlug vor, dies so zu tun: lists.swift.org/pipermail/swift-evolution/Week-of-Mon-20160125/…
jan.vogt
2
@ Fattie TARGET_OS_SIMULATOR != 0ist schon in der Antwort . Es ist die Lösung von Daniel. Es ist nicht erforderlich, es erneut in eine kostenlose Variable einzufügen, es ist bereits vorhanden. Wenn Sie der Meinung sind, dass es schlecht ist, es in einer Struktur zu haben und es in einer freien Variablen zu haben, ist es besser, einen Kommentar dazu zu schreiben oder Ihre eigene Antwort zu geben. Vielen Dank.
Eric Aya
69

Ab Xcode 9.3

#if targetEnvironment(simulator)

Swift unterstützt eine neue Plattformbedingung targetEnvironment mit einem einzigen gültigen Argument-Simulator. Die bedingte Kompilierung des Formulars '#if targetEnvironment (simulator)' kann jetzt verwendet werden, um zu erkennen, wann das Build-Ziel ein Simulator ist. Der Swift-Compiler versucht, die Verwendung von targetEnvironment (Simulator) zu erkennen, zu warnen und vorzuschlagen, wenn Plattformbedingungen, die scheinbar indirekt auf Simulatorumgebungen getestet werden, über die vorhandenen Plattformbedingungen os () und arch () ausgewertet werden. (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
    }
}

Vor iOS 9:

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

Ziel 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
HotJard
quelle
2
Das Vergleichen von Zeichenfolgen ist fragiler als das Verwenden definierter Konstanten.
Michael Peterson
@ P1X3L5 du hast recht! Aber ich gehe davon aus, dass diese Methode im Debug-Modus aufgerufen wird - sie könnte nicht so solide sein, aber schnell zu einem Projekt hinzugefügt werden
hinzugefügt werden HotJard
1
@GantMan danke für die Antwort. Ich habe den Code
behoben
@HotJard schön, dieser erzeugt keine will never be executedWarnung
Dannie P.
59

Swift 4

Sie können jetzt targetEnvironment(simulator)als Argument verwenden.

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

Aktualisiert für Xcode 9.3

Matt Swift
quelle
8
Dies sollte nun die akzeptierte Antwort sein. Ich wünschte, es gäbe eine Möglichkeit für SO, eine neue vorgeschlagene Antwort vorzuschlagen, die auf Aktualisierungen der Betriebssystem- / Programmiersprachen basiert.
quemeful
4
Es ist ein großartiger Punkt @quemeful - es ist einer der wenigen grundlegenden Fehler von SO. Da sich Computersysteme so schnell ändern, wird mit der Zeit fast jede Antwort auf SO falsch .
Fattie
40

Lassen Sie mich hier einige Dinge klarstellen:

  1. TARGET_OS_SIMULATORwird in vielen Fällen nicht im Swift-Code festgelegt; Möglicherweise wird es aufgrund eines Bridging-Headers versehentlich importiert, dies ist jedoch spröde und wird nicht unterstützt. Es ist auch nicht einmal in Frameworks möglich. Aus diesem Grund sind einige Leute verwirrt darüber, ob dies in Swift funktioniert.
  2. Ich rate dringend davon ab, Architektur als Ersatz für Simulator zu verwenden.

So führen Sie dynamische Überprüfungen durch:

Die Überprüfung ProcessInfo.processInfo.environment["SIMULATOR_DEVICE_NAME"] != nilist vollkommen in Ordnung.

Sie können das zugrunde liegende Modell auch simulieren lassen, indem Sie überprüfen, SIMULATOR_MODEL_IDENTIFIERwelche Zeichenfolgen wie zurückgegeben werden iPhone10,3.

So führen Sie statische Überprüfungen durch:

Xcode 9.2 und früher: Definieren Sie Ihr eigenes Swift-Kompilierungsflag (wie in anderen Antworten gezeigt).

Xcode 9.3+ verwendet die neue targetEnvironment-Bedingung:

#if targetEnvironment(simulator)
    // for sim only
#else
    // for device
#endif
russbishop
quelle
1
Es sieht so aus, als hätten Sie hier einige neue Insider-Informationen. Sehr hilfreich! Hinweis TARGET_OS_SIMULATOR funktionierte einige Zeit sowohl im App- als auch im Framework-Code. und es funktioniert auch in Xcode 9.3 b3. Aber ich denke, das ist "zufällig". Eine Art Mist; denn dies scheint der am wenigsten hackige Weg zu sein. Als Anbieter von Framework-Code, der entweder in Xcode 9.3 oder früher kompiliert werden könnte, müssen wir #if targetEnvironment ... anscheinend in ein # if-Swift-Makro (> = 4.1) einschließen, um Compilerfehler zu vermeiden. Oder ich denke, benutze .... Umgebung ["SIMULATOR_DEVICE_NAME"]! = Null. Diese Überprüfung scheint hackiger, IMO.
Daniel
Wenn der Fehler "Unerwarteter Plattformzustand (erwartetes 'os', 'arch' oder 'schnell') mit targetEnvironment (Simulator) aufgetreten ist
Zaporozhchenko Oleksandr
@Aleksandr targetEnvironmentlandete in Xcode 9.3. Sie benötigen eine neuere Version von Xcode.
Russbischof
@russbishop gute Arbeit, um dies für die neueste neue Ära aufzuklären - danke!
Fattie
Ich habe ein Kopfgeld von 250 geschickt, da diese Antwort die meisten und neuesten Informationen hinzuzufügen scheint - Prost
Fattie
15

Was für mich seit Swift 1.0 funktioniert, ist die Suche nach einer anderen Architektur als arm:

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

     //simulator
#else 
     //device

#endif
Akaru
quelle
14

Laufzeit, aber einfacher als die meisten anderen Lösungen hier:

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

Alternativ können Sie einfach eine Objective-C-Hilfsfunktion aufrufen, die einen Booleschen Wert zurückgibt, der das Präprozessor-Makro verwendet (insbesondere, wenn Sie Ihr Projekt bereits einmischen).

Bearbeiten: Nicht die beste Lösung, insbesondere ab Xcode 9.3. Siehe die Antwort von HotJard

Shim
quelle
3
Ich mache das, bekomme aber Warnungen in der else-Klausel, weil sie "niemals ausgeführt werden". Wir haben eine Null-Warnregel, also :-(
EricS
Es wird eine Warnung angezeigt, aber es ist sinnvoll, je nachdem, ob Sie einen Simulator oder ein Gerät zum
Erstellen
1
Warnungen werden nur angezeigt, wenn ich sie == 0anstelle von verwende != 0. Die Verwendung wie oben beschrieben, auch mit einem elseBlock danach, erzeugt keine Warnungen in Swift 4 Xcode Version 9.2 (9C40b)
Shim
Außerdem habe ich es auf einem Simulatorziel sowie auf einem physischen Gerät getestet. Scheint auch in Swift 3.2 (gleiche Xcode-Version) gleich zu sein.
Shim
In Xcode 9.3 + Swift 4.1 habe ich gerade bemerkt, dass es die Warnung auch mit! = 0 gibt. Meine Güte.
Shim
10

In modernen Systemen:

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

Es ist einfach.

Fattie
quelle
1
Ich bin mir nicht sicher, warum der erste "korrekter" sein sollte als Daniels Antwort . - Beachten Sie, dass der zweite ist eine Kompilierung zu überprüfen. Frohes neues Jahr!
Martin R
5

TARGET_IPHONE_SIMULATORist in iOS 9 veraltet. TARGET_OS_SIMULATORist der Ersatz. EbenfallsTARGET_OS_EMBEDDED verfügbar.

Von 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 */ 
Kleiber
quelle
1
Ich habe TARGET_OS_SIMULATOR ausprobiert, aber es funktioniert nicht oder wird vom Xcode nicht erkannt, während TARGET_IPHONE_SIMULATOR dies tut. Ich baue für iOS 8.0 oben.
CodeOverRide
Ich schaue auf die iOS 9-Header. Ich werde meine Antwort aktualisieren.
Kleiber
5

Ich hoffe, diese Erweiterung ist praktisch.

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

Verwendungszweck:

if UIDevice.isSimulator {
    print("running on simulator")
}
Lucas Chwe
quelle
@ChetanKoli, ich wollte den Code sehr klar und nicht kurz machen, damit er für jeden leicht zu verstehen ist. Ich bin mir nicht sicher, wie ich deine Bearbeitung finde.
Lucas Chwe
3

In Xcode 7.2 (und früher, aber ich habe noch nicht getestet, wie viel früher) können Sie ein plattformspezifisches Build-Flag "-D TARGET_IPHONE_SIMULATOR" für "Any iOS Simulator" setzen.

Schauen Sie in den Projekterstellungseinstellungen unter "Swift Compiler - Kundenflags" nach und setzen Sie dann das Flag unter "Andere Swift Flags". Sie können ein plattformspezifisches Flag setzen, indem Sie auf das Pluszeichen klicken, wenn Sie den Mauszeiger über eine Build-Konfiguration bewegen.

Dies hat einige Vorteile: 1) Sie können denselben bedingten Test ("#if TARGET_IPHONE_SIMULATOR") in Ihrem Swift- und Objective-C-Code verwenden. 2) Sie können Variablen kompilieren, die nur für jeden Build gelten.

Screenshot der Xcode-Build-Einstellungen

xgerrit
quelle
1

Ich habe diesen Code in Swift 3 verwendet

if TARGET_IPHONE_SIMULATOR == 1 {
    //simulator
} else {
    //device
}
ak_ninan
quelle
1
Ich mache das, bekomme aber Warnungen in der else-Klausel, weil sie "niemals ausgeführt werden". Wir haben eine Null-Warnregel, also grrrr ....
EricS
Es wird eine Warnung angezeigt, wenn Sie versuchen, mit einem Gerät zu arbeiten. Wenn Sie als Simulator zum Ausführen ausgewählt sind, wird die Warnung nicht angezeigt.
ak_ninan
1
es ist veraltet
rcmstark
1

Swift 4:

Derzeit bevorzuge ich die ProcessInfo- Klasse, um festzustellen , ob das Gerät ein Simulator ist und welche Art von Gerät verwendet wird:

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

Wie Sie wissen, simModelCodeist es jedoch kein bequemer Code, sofort zu verstehen, welche Art von Simulator gestartet wurde. Wenn Sie dies benötigen, können Sie versuchen, diese andere SO- Antwort anzuzeigen , um das aktuelle iPhone- / Gerätemodell zu ermitteln und einen menschlicheren Charakter zu haben lesbare Zeichenfolge.

Alessandro Ornano
quelle
1

Hier ist ein Xcode 11 Swift Beispiel basiert weg HotJard die ehrfürchtige Antwort oben , das macht auch eine isDeviceBool und verwendet SIMULATOR_UDIDstatt Namen. Variablenzuweisungen werden in jeder Zeile vorgenommen, damit Sie sie im Debugger leichter überprüfen können, wenn Sie dies wünschen.

import Foundation

// Extensions to UIDevice based on ProcessInfo.processInfo.environment keys
// to determine if the app is running on an actual device or the Simulator.

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

    static var isDevice: Bool {
        let environment = ProcessInfo.processInfo.environment
        let isDevice = environment["SIMULATOR_UDID"] == nil
        return isDevice
    }
}

Es gibt auch den Wörterbucheintrag, der DTPlatformNameenthalten sollte simulator.

Alex Zavatone
quelle
0

Verwenden Sie den folgenden Code:

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

Funktioniert für Swift 4undXcode 9.4.1

Haroldo Gondim
quelle
0

Xcode 11, Swift 5

    #if !targetEnvironment(macCatalyst)
    #if targetEnvironment(simulator)
        true
    #else
        false        
    #endif
    #endif
UnchartedWorks
quelle
0

Neben anderen Antworten.

Stellen Sie in Objective-c einfach sicher, dass Sie TargetConditionals eingeschlossen haben .

#include <TargetConditionals.h>

vor dem Gebrauch TARGET_OS_SIMULATOR.

M. Ali
quelle