Как программно определить, запущено ли мое приложение в симуляторе iphone?

270

Как говорится в вопросе, я бы хотел узнать, работает ли мой код в симуляторе, но также хотел бы знать, какая конкретная версия iphone запущена или имитируется.

РЕДАКТИРОВАТЬ: я добавил слово «программно» к названию вопроса. Суть моего вопроса в том, чтобы иметь возможность динамически включать / исключать код в зависимости от того, какая версия / симулятор запущена, поэтому я действительно искал бы что-то вроде директивы препроцессора, которая может предоставить мне эту информацию.

Джеффри Мейер
источник
Я не уверен, что директива препроцессора является динамической (хотя в любом случае это может быть то, что вы искали). Директива означает, что вы на самом деле знали, когда строили его, куда он собирался бежать.
WiseOldDuck

Ответы:

356

Уже спросил, но с совершенно другим названием.

Что #defines устанавливает Xcode при компиляции для iPhone

Я повторю свой ответ оттуда:

Он находится в документации SDK в разделе «Условная компиляция исходного кода»

Соответствующее определение - TARGET_OS_SIMULATOR, которое определено в /usr/include/TargetConditionals.h в рамках iOS. В более ранних версиях цепочки инструментов вы должны были написать:

#include "TargetConditionals.h"

но это больше не требуется в текущей (Xcode 6 / iOS8) цепочке инструментов.

Так, например, если вы хотите проверить, что вы работаете на устройстве, вы должны сделать

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

в зависимости от того, который подходит для вашего варианта использования.

Airsource Ltd
источник
1
Спасибо. Я согласен с вами, это более конкретная версия вашего первоначального вопроса. Если бы вы нашли меня в моем первоначальном поиске, мне бы даже не пришлось спрашивать.
Джеффри Мейер
5
Будьте осторожны с этими определениями. Когда вы компилируете код с помощью пункта меню «Проект> Установить активный SDK> Симулятор…», в ​​качестве переменных TARGET_IPHONE_SIMULATOR и TARGET_OS_IPHONE определены обе переменные! Итак, единственный правильный способ отделить логику указан ниже Питом (спасибо, чувак).
Вадим
5
Посмотрите разницу #if и #ifdef. Для меня это стало причиной некорректного поведения.
Антон
7
Возможно, необходимость включать TargetConditionals была устранена с тех пор, как это было написано, но просто хотел заметить, что #if TARGET_IPHONE_SIMULATOR работает без включения TargetConditionals.h сейчас.
dmur
1
@Dimitris Это хорошая практика. Вы не знаете, как был определен TARGET_OS_SIMULATOR, поэтому! (TARGET_OS_SIMULATOR) может не совпадать с! TARGET_OS_SIMULATOR
Airsource Ltd
106

Обновленный код:

Это предполагается работать официально.

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

Исходное сообщение (поскольку устарело)

Этот код скажет вам, если вы работаете в симуляторе.

#ifdef __i386__
NSLog(@"Running in the simulator");
#else
NSLog(@"Running on a device");
#endif
Пит
источник
7
Начиная с iOS 8 и Xcode 6.1.1 TARGET_OS_IPHONE имеет значение true на симуляторе.
Малхал
3
это больше не сказывается на новых версиях XCode
Фабио Наподано
1
Если вы не в 2016 году и не запускаете 64-битный симулятор. Или в 2019 году и запустите свой код на iPhone с процессором Intel.
gnasher729
61

Не директива препроцессора, но именно это я и искал, когда пришел к этому вопросу;

NSString *model = [[UIDevice currentDevice] model];
if ([model isEqualToString:@"iPhone Simulator"]) {
    //device is simulator
}
Даниэль Магнуссон
источник
9
[model compare:iPhoneSimulator] == NSOrderedSameдолжно быть записано как[model isEqualToString:iPhoneSimulator]
user102008
18
Или [model hasSuffix:@"Simulator"]если вы заботитесь только о «симуляторе» вообще, а не iPhone или iPad в частности. Этот ответ не будет работать для симулятора iPad :)
Поползень
Утверждено, потому что комментарий Поползневого делает это лучшим ответом в целом.
Le Mot Juiced
12
В iOS9 nameвместо устройства проверьтеmodel
n.Drake
1
Код не будет работать, если пользователь добавит Simulatorслово в название своего устройства
mbelsky
55

Лучший способ сделать это:

#if TARGET_IPHONE_SIMULATOR

и нет

#ifdef TARGET_IPHONE_SIMULATOR

так как его всегда определяется: 0 или 1

Taranfx
источник
39

ЛУЧШЕ ПУТЬ СЕЙЧАС!

Начиная с Xcode 9.3 beta 4 вы можете использовать #if targetEnvironment(simulator)для проверки.

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

ОБНОВЛЕНИЕ
Xcode 10 и iOS 12 SDK также поддерживают это.

Стефан Васильевич
источник
1
Это единственное, что работает для меня, остальные решения не сработали.
Врутин Ратод
Примечание Это только в быстром.
Мэтт С.
35

В случае Swift мы можем реализовать следующее

Мы можем создать структуру, которая позволяет создавать структурированные данные

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

Тогда Если мы хотим определить, создается ли приложение для устройства или симулятора в Swift, тогда.

if Platform.isSimulator {
    // Do one thing
} else {
    // Do the other
}
Нишаль Хада
источник
Самая чистая реализация на мой взгляд, и она учитывает архитектуры x86_64 и i386. Помог мне преодолеть странную ошибку устройства против симулятора в Core Data. Ты мужчина!
Железный Джон Бонни
5
На игровой площадке вы получите предупреждение: «Код после возврата никогда не будет выполнен». Так что думаю #if #else #endifбудет лучше.
DawnSong
12

Работает для Swift 5иXcode 11.3.1

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

#if targetEnvironment(simulator)
   // Simulator
#else
   // Device
#endif
Гарольдо Гондим
источник
9

Все эти ответы хороши, но это как-то сбивает с толку новичка, такого как я, поскольку не проясняет проверку компиляции и проверки во время выполнения. Препроцессор находится перед временем компиляции, но мы должны сделать его более понятным

В этой статье блога показано, как обнаружить симулятор iPhone? явно

время выполнения

Прежде всего, давайте кратко обсудим. UIDevice уже предоставляет вам информацию об устройстве

[[UIDevice currentDevice] model]

вернет вам «iPhone Simulator» или «iPhone» в зависимости от того, где запущено приложение.

Время компиляции

Однако вы хотите использовать определения времени компиляции. Зачем? Потому что вы компилируете свое приложение строго для запуска внутри Симулятора или на устройстве. Apple делает определение под названием TARGET_IPHONE_SIMULATOR. Итак, давайте посмотрим на код:

#if TARGET_IPHONE_SIMULATOR

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

#endif
onmyway133
источник
1
Как это улучшает другие ответы?
мммммм
@Mark Это немного проясняет
onmyway133
5
В настоящее время, в Xcode 7, iOS 9 Simulator [[UIDevice currentDevice] model]возвращается iPhoneтакже вместо iPhone Simulator. Так что я думаю, что это не лучший подход.
eMdOS
6

Предыдущие ответы немного устарели. Я обнаружил, что все, что вам нужно сделать, это запросить TARGET_IPHONE_SIMULATORмакрос ( нет необходимости включать какие-либо другие заголовочные файлы [предполагается, что вы кодируете для iOS]).

Я попытался, TARGET_OS_IPHONEно он вернул то же значение (1) при работе на реальном устройстве и симуляторе, поэтому я рекомендую использовать TARGET_IPHONE_SIMULATORвместо этого.

привлекательный
источник
TARGET_OS_IPHONE предназначен для кода, который может работать на iOS или MacOS X. Очевидно, вы захотите, чтобы этот код вел себя как «iPhone» на симуляторе.
gnasher729
6

В скором времени:

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

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

CedricSoubrie
источник
Чтобы различать приложения для Mac: #if (arch (i386) || arch (x86_64)) &&! Os (OSX) // мы находимся на симуляторе, работающем на mac, а не на mac-приложении. (Для кроссплатформенного кода, включенного в цели Mac)
Bobjt
4

У меня была одна и та же проблема, обе TARGET_IPHONE_SIMULATORи TARGET_OS_IPHONEвсегда определены, и установлены на 1. Решение Пита, конечно, работает, но если вам когда-нибудь удастся использовать что-то отличное от Intel (маловероятно, но кто знает), вот что-то безопасное, как до тех пор, пока аппаратное обеспечение iphone не изменится (т. е. ваш код всегда будет работать для iphone, который в данный момент существует):

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

Поместите это где-нибудь удобно, а затем сделайте вид, что TARGET_*константы были определены правильно.


источник
4

Кто-нибудь рассматривал ответ, представленный здесь ?

Я полагаю, что объективный эквивалент эквивалентен

+ (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

Для Swift 4.2 / xCode 10

Я создал расширение на UIDevice, поэтому я могу легко спросить, работает ли симулятор.

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

Например, в моем AppDelegate я использую этот метод, чтобы решить, нужна ли регистрация для удаленного уведомления, что невозможно для симулятора.

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

    // REGISTER FOR SILENT REMOTE NOTIFICATION
    application.registerForRemoteNotifications()
}
LukeSideWalker
источник
1

Включить все виды «симуляторов»

NSString *model = [[UIDevice currentDevice] model];
if([model rangeOfString:@"Simulator" options:NSCaseInsensitiveSearch].location !=NSNotFound)
{
    // we are running in a simulator
}
jeffr
источник
4
Это не имеет ничего общего с Xcode 7. Если вы запустите iOS Simulator с iOS8 (из Xcode 7), то это будет работать. Это не будет работать для iOS9, где [[UIDevice currentDevice] модель] возвращает только «iPhone», если приложение было запущено с симулятора iOS
Стефан
почему нет -[NSString containsString]?
Гоб
1

С Swift 4.2 (Xcode 10) мы можем сделать это

#if targetEnvironment(simulator)
  //simulator code
#else 
  #warning("Not compiling for simulator")
#endif
IHS
источник
1
Просто еще одна копия пасты
Дж. Доу
0

Мой ответ основан на ответе @Daniel Magnusson и комментариях @Nuthatch и @ n.Drake. и я пишу это, чтобы сэкономить время для быстрых пользователей, работающих на iOS9 и далее.

Вот что сработало для меня:

if UIDevice.currentDevice().name.hasSuffix("Simulator"){
    //Code executing on Simulator
} else{
    //Code executing on Device
}
euthimis87
источник
1
Код не будет работать, если пользователь добавит Simulatorслово в название своего устройства
mbelsky
К сожалению, в XCode 8 UIDevice.current.nameсообщается имя машины, на которой запущен симулятор (обычно что-то вроде «Simon's MacBook Pro»), поэтому тест стал ненадежным. Я все еще ищу чистый способ исправить это.
Майкл
0

/// Возвращает true, если его симулятор, а не устройство

public static var isSimulator: Bool {
    #if (arch(i386) || arch(x86_64)) && os(iOS)
        return true
    #else
        return false
    #endif
}
Пратюш Пратик
источник
0

Apple добавила поддержку проверки приложения, предназначенного для симулятора, со следующим:

#if targetEnvironment(simulator)
let DEVICE_IS_SIMULATOR = true
#else
let DEVICE_IS_SIMULATOR = false
#endif
Дэвид Корбин
источник
0

если ничего не получилось, попробуйте это

public struct Platform {

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

}
Аклеш Ратхаур
источник
-4

На мой взгляд, ответ (представлен выше и повторен ниже):

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

это лучший ответ, потому что он явно выполняется в RUNTIME, а не является COMPILE DIRECTIVE.

user1686700
источник
11
Я не согласен. Этот код попадает в ваш продукт, тогда как директива компилятора исключает излишнюю рутину на устройстве.
девять камней
1
Директивы компилятора работают, потому что устройство и симуляторы являются совершенно разными целями компиляции - то есть вы не будете использовать один и тот же двоичный файл на обоих. Он должен быть скомпилирован на другом оборудовании, поэтому в этом случае имеет смысл.
Брэд Паркс
Быть казненным в RUNTIME делает его худшим из возможных ответов.
gnasher729
-4

Это сработало для меня лучше всего

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


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

}
Mani
источник
2
На Xcode 7.3 iPhone 6 Plus Simulator возвращается "iPhone".
Эрик