如何检测应用程序是否正在构建的设备或模拟器在Swift

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

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

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

我如何在Swift中实现同样的目标?

119066 次浏览

更新30/01/19

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

原来的答案

如果你需要静态检查(例如,不是运行时If /else),你不能直接检测模拟器,但你可以在桌面架构上检测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 proposal SE-0190


对于旧版本 -

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

当为32位iOS模拟器编译代码时,arch(i386)构建配置返回true。

如果你正在为iOS以外的模拟器开发,你可以简单地改变os参数:

检测watchOS模拟器

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

检测tvo模拟器

#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变量(或ios8及以下的TARGET_IPHONE_SIMULATOR),这在模拟器上是正确的。

请注意,这与使用预处理器标志有所不同,并且有一些限制。例如,你不能在if/else语法无效的地方使用它(例如在函数作用域之外)。

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

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

此外,由于该标志被swift预处理器替换为01,如果你直接在if/else表达式中使用它,编译器将发出关于不可达代码的警告。

为了解决这个警告,请参阅其他答案之一。

从Swift 1.0开始,对我来说有用的是检查除了arm之外的架构:

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


//simulator
#else
//device


#endif

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

看起来@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")
}

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

来自Xcode 9.3

#if targetEnvironment(simulator)
Swift支持一个新的平台条件targetEnvironment 单个有效参数模拟器。表单的条件编译 '#if targetEnvironment(模拟器)'现在可以用来检测何时构建目标是模拟器。Swift编译器将尝试 检测,警告,并建议使用targetEnvironment(模拟器) 评估平台条件,似乎是测试模拟器 环境,通过现有的os()和arch()平台 条件。(se - 0190) < / p >

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

在Xcode 7.2(以及更早的版本,但我还没有测试过多少)中,你可以为“任何iOS模拟器”设置一个平台特定的构建标志“-D TARGET_IPHONE_SIMULATOR”。

查看“Swift Compiler - Customer Flags”下的项目构建设置,然后在“Other Swift Flags”中设置标志。当您将鼠标悬停在构建配置上时,可以通过单击“加号”图标来设置特定于平台的标志。

这样做有几个好处:1)你可以在Swift和Objective-C代码中使用相同的条件测试(“#if TARGET_IPHONE_SIMULATOR”)。2)你可以编译出只适用于每个版本的变量。

Xcode构建设置截图

swift 4.1已过时。使用#if targetEnvironment(simulator)代替。

要检测Swift中的模拟器,您可以使用构建配置:

  • Swift编译器-自定义标志>其他Swift标志中定义这个配置- d IOS_SIMULATOR
  • 在下拉菜单下拉列表中选择任何iOS模拟器SDK

现在你可以使用这个语句来检测模拟器:

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

你也可以扩展UIDevice类:

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

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

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

或者,你可以调用一个Objective-C helper函数来返回一个使用预处理器宏的布尔值(特别是如果你已经在你的项目中混合了)。

编辑:不是最好的解决方案,特别是在Xcode 9.3中。看到HotJard的回答

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

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

所有描述达尔文。TargetConditionals: https://github.com/apple/swift-corelibs-foundation/blob/master/CoreFoundation/Base.subproj/SwiftRuntime/TargetConditionals.h < / p >

TARGET_OS_SIMULATOR - Generated code will run under a simulator

在现代系统中:

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

就是这么简单。

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

  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+使用新的targetenvirenvironment条件:

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

斯威夫特4

你现在可以使用targetEnvironment(simulator)作为参数。

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

为Xcode 9.3更新

使用下面的代码:

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

适用于Swift 4Xcode 9.4.1

斯威夫特4:

目前,我更喜欢使用ProcessInfo类来知道设备是否是模拟器,以及正在使用哪种设备:

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

但是,正如你所知道的,simModelCode并不是一个容易理解的代码,因此,如果你需要,你可以尝试查看另一个so 回答来确定当前的iPhone/设备型号,并获得一个更适合人类阅读的字符串。

我希望这个扩展可以派上用场。

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

用法:

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

Xcode 11, Swift 5

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

下面是一个基于HotJard的的Xcode 11 Swift示例,它也添加了一个isDevice Bool,并使用SIMULATOR_UDID来代替name。变量赋值是在每一行上完成的,这样你就可以更容易地在调试器中检查它们。

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

还有DTPlatformName的字典条目,它应该包含simulator

除了其他答案。

在Objective-c中,只要确保你包含了TargetConditionals

#include <TargetConditionals.h>

在使用TARGET_OS_SIMULATOR之前。

< p >迅速5.2.4 Xcode 11.7 < / p >

#if targetEnvironment(simulator)


#endif


我不知道这是否对任何人有用,但至少M1 mac的当前版本似乎没有将SIMULATOR_MODEL_IDENTIFIER传递到NSProcessInfo

我使用

BOOL isMobile = [[NSProcessInfo processInfo].environment[@"USER"] isEqual:@"mobile"];

和迅捷的对等物。这可能很脆弱,但很有效。