Обнаруживать, создается ли приложение для устройства или симулятора в Swift

В Objective-C мы можем знать, создается ли приложение для устройства или симулятора с использованием макросов:

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

Это макросы времени компиляции и недоступны во время выполнения.

Как добиться того же результата в Swift? Я искал переполнение стека, просмотрел документы и не могу понять.

Ответ 1

Обновление 30/01/19

Несмотря на то, что этот ответ может сработать, рекомендуемое решение для статической проверки (как пояснили несколько инженеров Apple) заключается в определении настраиваемого флага компилятора, предназначенного для симуляторов iOS. Подробные инструкции о том, как это сделать, см. В ответе @mbelsky.

Оригинальный ответ

Если вам нужна статическая проверка (например, не во время выполнения, если/иначе), вы не можете обнаружить симулятор напрямую, но вы можете обнаружить iOS на настольной архитектуре, как показано ниже

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

После версии Swift 4.1

Последнее использование, теперь непосредственно для всех в одном условии, для всех типов тренажеров необходимо применять только одно условие -

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

Для получения дополнительной информации вы можете проверить предложение Swift SE-0190


Для старой версии -

Ясно, что это ложно на устройстве, но оно возвращает истину для симулятора iOS, как указано в документации:

Конфигурация сборки arch (i386) возвращает true, когда код компилируется для 32-битного симулятора iOS.

Если вы разрабатываете для симулятора, отличного от iOS, вы можете просто изменить параметр os: например

Определить симулятор watchOS

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

Определить симулятор tvOS

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

Или даже обнаружить любой симулятор

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

Если вместо этого вы согласны с проверкой во время выполнения, вы можете проверить переменную TARGET_OS_SIMULATOR (или TARGET_IPHONE_SIMULATOR в iOS 8 и ниже), что верно на симуляторе.

Обратите внимание, что это отличается и немного более ограничено, чем использование флага препроцессора. Например, вы не сможете использовать его там, где синтаксически недопустимо if/else (например, за пределами области действия функций).

Скажем, например, что вы хотите иметь разные импорты на устройстве и на симуляторе. Это невозможно при динамической проверке, тогда как при статической проверке это тривиально.

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

Кроме того, поскольку флаг заменяется на 0 или 1 препроцессором swift, если вы напрямую используете его в выражении if/else компилятор выдаст предупреждение о недоступном коде.

Чтобы обойти это предупреждение, см. Один из других ответов.

Ответ 2

УСТАРЕЛО ДЛЯ SWIFT 4.1. #if targetEnvironment(simulator) используйте #if targetEnvironment(simulator). Источник

Для обнаружения симулятора в Swift вы можете использовать конфигурацию сборки:

  • Определите эту конфигурацию -D IOS_SIMULATOR в Swift Compiler - Пользовательские флаги> Другие флаги Swift
  • Выберите любой iOS Simulator SDK в этом выпадающем меню Drop down list

Теперь вы можете использовать это утверждение для обнаружения симулятора:

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

Также вы можете расширить класс UIDevice:

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

Ответ 3

Обновленная информация от 20 февраля 2018 г.

Похоже, у @russbishop есть авторитетный ответ, который делает этот ответ "неправильным", хотя он, казалось, работал долгое время.

Определить, создается ли приложение для устройства или симулятора в Swift

Предыдущий ответ

На основе ответа @WZW и комментариев @Pang я создал простую структуру утилит. Это решение позволяет избежать предупреждения, создаваемого ответом @WZW.

import Foundation

struct Platform {

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

}

Пример использования:

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

Ответ 4

От Xcode 9.3

#if targetEnvironment(simulator)

Swift поддерживает новое условие платформы targetEnvironment с одним действительным имитатором аргументов. Условная компиляция формы "#if targetEnvironment (simulator)" теперь может использоваться для определения, когда целью сборки является симулятор. Компилятор Swift попытается обнаружить, предупредить и предложить использовать targetEnvironment (симулятор) при оценке условий платформы, которые, по-видимому, косвенно тестируют среды симулятора через существующие условия платформы os() и arch(). (SE-0190)

iOS 9+:

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

Свифт 3:

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

До iOS 9:

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

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

Ответ 5

Swift 4

Теперь вы можете использовать targetEnvironment(simulator) в качестве аргумента.

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

Обновлено для Xcode 9.3

Ответ 6

Позвольте мне уточнить некоторые вещи здесь:

  1. TARGET_OS_SIMULATOR не установлен в коде Swift во многих случаях; Вы можете случайно импортировать его из-за связующего заголовка, но это хрупко и не поддерживается. Это также невозможно даже в рамках. Вот почему некоторые люди не понимают, работает ли это в Swift.
  2. Я настоятельно рекомендую не использовать архитектуру вместо симулятора.

Для выполнения динамических проверок:

Проверка ProcessInfo.processInfo.environment["SIMULATOR_DEVICE_NAME"] != nil - это прекрасно.

Вы также можете получить базовую модель для моделирования, проверив SIMULATOR_MODEL_IDENTIFIER которая будет возвращать строки, такие как iPhone10,3.

Для выполнения статических проверок:

Xcode 9.2 и ранее: определите свой собственный флаг компиляции Swift (как показано в других ответах).

Xcode 9. 3+ использует новое условие targetEnvironment:

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

Ответ 7

Что работает для меня, так как Swift 1.0 проверяет архитектуру, отличную от руки:

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

     //simulator
#else 
     //device

#endif

Ответ 8

Время выполнения, но проще, чем большинство других решений здесь:

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

Кроме того, вы можете просто вызвать вспомогательную функцию Objective C, которая возвращает логическое значение, использующее макрос препроцессора (особенно, если вы уже микшируете в своем проекте).

Изменение: не лучшее решение, особенно в Xcode 9.3. Посмотреть ответ HotJard

Ответ 9

В современных системах:

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

Это легко.

Ответ 10

TARGET_IPHONE_SIMULATOR устарел в iOS 9. TARGET_OS_SIMULATOR является заменой. Также доступен TARGET_OS_EMBEDDED.

От 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 */ 

Ответ 11

В Xcode 7.2 (и ранее, но я еще не тестировал, насколько раньше), вы можете установить флаг сборки конкретной платформы "-D TARGET_IPHONE_SIMULATOR" для "Any iOS Simulator".

Посмотрите в настройках сборки проекта в разделе "Swift Compiler - Customer Flags", а затем установите флаг в "Other Swift Flags". Вы можете установить флаг конкретной платформы, щелкнув значок "плюс", когда наводите курсор на конфигурацию сборки.

Есть несколько преимуществ сделать это следующим образом: 1) Вы можете использовать тот же условный тест ( "#if TARGET_IPHONE_SIMULATOR" ) в вашем коде Swift и Objective-C. 2) Вы можете скомпилировать переменные, которые применяются только к каждой сборке.

Снимок экрана настроек Xcode

Ответ 13

Я использовал этот ниже код в Swift 3

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

Ответ 14

Свифт 4:

В настоящее время я предпочитаю использовать класс ProcessInfo, чтобы узнать, является ли устройство симулятором и какое устройство используется:

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

Но, как вы знаете, simModelCode не очень удобен для немедленного понимания, какой тип симулятора был запущен, поэтому, если вам нужно, вы можете попытаться увидеть этот другой SO- ответ, чтобы определить текущую модель iPhone/устройства и иметь более человекочитаемая строка.

Ответ 15

Используйте этот код ниже:

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

Работает для Swift 4 и Xcode 9.4.1

Ответ 16

Я надеюсь, что это расширение пригодится

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

Использование:

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

Ответ 17

Вы также можете получить UIDevice.current.name, который возвращает iPhone Simulator, когда он симулятор. Конечно, кто-то может назвать свое устройство "iPhone Simulator"...