关于ios:检测是否正在为Swift中的设备或模拟器构建应用程序

Detect if app is being built for device or simulator in Swift

在Objective-C中,我们可以知道是否正在为使用宏的设备或模拟器构建应用程序:

1
2
3
4
5
#if TARGET_IPHONE_SIMULATOR
    // Simulator
#else
    // Device
#endif

这些是编译时宏,在运行时不可用。

我怎样才能在斯威夫特做到这一点?我搜索了堆栈溢出,查看了文档,但找不到它。


更新30/01/19

虽然这个答案可能有效,但静态检查的建议解决方案(如几个苹果工程师所阐明的)是定义一个针对iOS模拟器的自定义编译器标志。有关如何处理的详细说明,请参阅@mbelsky的答案。

原始答案

如果需要静态检查(例如,不是运行时if/else),则无法直接检测模拟器,但可以在桌面架构上检测iOS,如下所示

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

在Swift 4.1版本之后

Latest use, now directly for all in one condition for all types of simulators need to apply only one condition -

1
2
3
4
5
#if targetEnvironment(simulator)
  // your simulator code
#else
  // your real device code
#endif

如需更多说明,您可以查看SWIFT建议书SE-0190。

For older version -

显然,这在设备上是错误的,但对于iOS模拟器,它返回true,如文档中指定的那样:

The arch(i386) build configuration returns true when the code is compiled for the 32–bit iOS simulator.

如果您是为除iOS之外的模拟器开发的,您可以简单地改变os参数:例如

检测watchos模拟器

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

检测TVOS模拟器

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

或者,甚至,检测任何模拟器

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

如果运行时检查没有问题,可以检查iOS 8及以下版本中的TARGET_OS_SIMULATOR变量(或TARGET_IPHONE_SIMULATOR),这在模拟器上是真实的。

请注意,这与使用预处理器标志相比有所不同,而且有一些限制。例如,如果if/else在语法上无效(例如,在函数范围之外),您将无法使用它。

例如,假设您希望在设备和模拟器上有不同的导入。这对于动态检查是不可能的,而对于静态检查则是微不足道的。

1
2
3
4
5
#if (arch(i386) || arch(x86_64)) && os(iOS)
  import Foo
#else
  import Bar
#endif

另外,由于Swift预处理器将标志替换为01,如果直接在if/else表达式中使用它,编译器将对无法访问的代码发出警告。

要解决此警告,请参阅其他答案之一。


已过时的Swift 4.1。用#if targetEnvironment(simulator)代替。来源

要在swift中检测模拟器,可以使用构建配置:

  • 在swift编译器-自定义标志>其他swift标志中定义此配置-d ios_模拟器
  • 在此下拉菜单Drop down list中选择任何iOS模拟器SDK

现在您可以使用此语句检测模拟器:

1
2
3
4
5
#if IOS_SIMULATOR
    print("It's an iOS Simulator")
#else
    print("It's a device")
#endif

还可以扩展uidevice类:

1
2
3
4
5
6
7
8
9
10
extension UIDevice {
    var isSimulator: Bool {
        #if IOS_SIMULATOR
            return true
        #else
            return false
        #endif
    }
}
// Example of usage: UIDevice.current.isSimulator


更新信息截止2018年2月20日

看起来@russbishop有一个权威的答案,使得这个答案"不正确"——尽管它看起来工作了很长时间。

在swift中检测是否正在为设备或模拟器构建应用程序

先前的答案

基于@wzw的回答和@pang的评论,我创建了一个简单的实用工具结构。此解决方案可避免@wzw的回答产生警告。

1
2
3
4
5
6
7
8
9
import Foundation

struct Platform {

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

}

示例用法:

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


从XCODE 9.3

1
#if targetEnvironment(simulator)

Swift supports a new platform condition targetEnvironment with a
single valid argument simulator. Conditional compilation of the form
'#if targetEnvironment(simulator)' can now be used to detect when the build target is a simulator. The Swift compiler will attempt to
detect, warn, and suggest the use of targetEnvironment(simulator) when
evaluating platform conditions that appear to be testing for simulator
environments indirectly, via the existing os() and arch() platform
conditions. (SE-0190)

IOS 9 +:

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

斯威夫特3:

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

在iOS 9之前:

1
2
3
4
5
extension UIDevice {
    static var isSimulator: Bool {
        return UIDevice.currentDevice().model =="iPhone Simulator"
    }
}

ObjultC:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
@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


斯威夫特4

你现在可以用targetEnvironment(simulator)作为论据。

1
2
3
4
5
#if targetEnvironment(simulator)
    // Simulator
#else
    // Device
#endif

更新Xcode 9.3


让我在这里澄清一些事情:

  • 在许多情况下,Swift代码中没有设置TARGET_OS_SIMULATOR;您可能由于桥接头而意外地将其导入,但这是脆弱的,不受支持。在框架中,这甚至是不可能的。这就是为什么有些人对这是否能迅速奏效感到困惑的原因。
  • 我强烈建议不要用体系结构代替模拟器。
  • 要执行动态检查:

    检查ProcessInfo.processInfo.environment["SIMULATOR_DEVICE_NAME"] != nil是完全正确的。

    您还可以通过检查SIMULATOR_MODEL_IDENTIFIER来模拟底层模型,它将返回类似iPhone10,3的字符串。

    要执行静态检查:

    xcode 9.2&early:定义自己的swift编译标志(如其他答案所示)。

    xcode 9.3+使用新的targetEnvironment条件:

    1
    2
    3
    4
    5
    #if targetEnvironment(simulator)
        // for sim only
    #else
        // for device
    #endif


    自Swift 1.0以来,我的工作是检查除ARM之外的架构:

    1
    2
    3
    4
    5
    6
    7
    #if arch(i386) || arch(x86_64)

         //simulator
    #else
         //device

    #endif

    运行时,但比其他大多数解决方案更简单:

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

    或者,您可以调用一个Objective-C助手函数,该函数返回一个使用预处理器宏的布尔值(尤其是在项目中已经混合的情况下)。

    编辑:不是最好的解决方案,特别是Xcode9.3。看霍贾德的回答


    在现代系统中:

    1
    2
    3
    4
    5
    #if targetEnvironment(simulator)
        // sim
    #else
        // device
    #endif

    这很容易。


    iOS 9中不推荐使用TARGET_IPHONE_SIMULATORTARGET_OS_SIMULATOR是替代品。也可提供TARGET_OS_EMBEDDED

    从targetconditionals.h:

    1
    2
    3
    4
    5
    6
    #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 */


    在Xcode7.2中(以及更早的版本,但我还没有测试多久),您可以为"任何iOS模拟器"设置特定于平台的构建标志"-d target_iphone_模拟器"。

    在"Swift编译器-客户标志"下查看项目构建设置,然后在"其他Swift标志"中设置标志。当您将鼠标悬停在构建配置上时,可以通过单击"加号"图标来设置特定于平台的标志。

    这样做有两个好处:1)你可以在你的Swift和Objective-C代码中使用相同的条件测试("如果目标是iPhone模拟器")。2)您可以编译只适用于每个构建的变量。

    xcode生成设置屏幕截图


    这里描述的都是达尔文。目标条件:http://GITHUB.COM/Apple / SWIFT CORILBS基金会/ BLB/MUST/CoreFoundation /BASE.子PROJ/SWIFTUNUNTIME/TAIN条件:H

    TARGET_OS_SIMULATOR - Generated code will run under a simulator


    我希望这个分机很方便

    1
    2
    3
    4
    5
    6
    7
    8
    9
    extension UIDevice {
        static var isSimulator: Bool = {
            var isSimulator = false
            #if targetEnvironment(simulator)
            isSimulator = true
            #endif
            return isSimulator
        }()
    }

    用途:

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

    斯威夫特4:

    目前,我更喜欢使用processinfo类来了解设备是否是模拟器,以及使用的是哪种设备:

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

    但是,正如你所知,simModelCode不是一个很好的代码,无法立即理解启动了哪种模拟器,因此,如果你需要的话,你可以尝试看到另一个这样的答案,以确定当前的iPhone/设备型号,并拥有一个更具人类可读性的字符串。


    我在swift 3中使用了下面的代码

    1
    2
    3
    4
    5
    if TARGET_IPHONE_SIMULATOR == 1 {
        //simulator
    } else {
        //device
    }


    使用以下代码:

    1
    2
    3
    4
    5
    #if targetEnvironment(simulator)
       // Simulator
    #else
       // Device
    #endif

    Swift 4Xcode 9.4.1工作