检测是否在Swift中为设备或模拟器构build应用程序
在Objective-C中,我们可以知道是否正在使用macros为设备或模拟器构build应用程序:
#if TARGET_IPHONE_SIMULATOR // Simulator #else // Device #endif
这些是编译时macros,在运行时不可用。
我怎样才能在Swift中实现相同? 我search堆栈溢出,看了看文档,不能弄清楚。
更新14/09/17
虽然这个答案可能会起作用,但推荐的静态检查解决scheme(由多名苹果工程师澄清)是定义一个针对iOS模拟器的自定义编译器标志。 有关如何做的详细说明,请参阅@ mbelsky的答案 。
原始答案
如果你需要一个静态检查(例如,不是一个运行时if / else),你不能直接检测到模拟器,但你可以在桌面架构上检测iOS,如下所示
#if (arch(i386) || arch(x86_64)) && os(iOS) ... #endif
显然这在设备上是错误的,但是对于iOS模拟器来说它是正确的,正如文档中所指定的那样:
在为32位iOS模拟器编译代码时,arch(i386)构buildconfiguration返回true。
如果您正在开发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
variables(或iOS 8及以下版本中的TARGET_IPHONE_SIMULATOR
)。
请注意,这与使用预处理器标志有所不同,稍有限制。 例如,你将无法在if/else
语法无效的地方使用它(例如,在函数范围之外)。
比方说,你想在设备和模拟器上有不同的input。 dynamic检查是不可能的,而对于静态检查来说这是微不足道的。
#if (arch(i386) || arch(x86_64)) && os(iOS) import Foo #else import Bar #endif
另外,由于swift预处理器将标志replace为0
或1
,所以如果直接在if/else
expression式中使用它,编译器将会提示有关不可达代码的警告。
为了解决此警告,请参阅其他答案之一。
基于@WZW的回答和@庞的评论,我创build了一个简单的实用程序结构。 这个解决scheme避免了@ WZW的回答产生的警告。
import Foundation struct Platform { static var isSimulator: Bool { return TARGET_OS_SIMULATOR != 0 } }
用法示例:
if Platform.isSimulator { print("Running on Simulator") }
要在Swift中检测模拟器,可以使用构buildconfiguration:
- 在Swift编译器中定义这个configuration-D IOS_SIMULATOR – 自定义标志>其他Swift标志
- 在此下拉列表中select任何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
这个怎么样?
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 } }
在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
自从Swift 1.0开始检查arm之外的其他架构之后,
#if arch(i386) || arch(x86_64) //simulator #else //device #endif
运行时,但比大多数其他解决scheme更简单:
if TARGET_OS_SIMULATOR != 0 { // target is current running in the simulator }
或者,您可以调用一个Objective-C帮助器函数,该函数返回一个使用预处理器macros的布尔值(特别是如果您已经在项目中混合)。
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 7.2(及更早的版本,但是我还没有testing过多less),你可以为“Any iOS Simulator”设置一个特定平台的构build标志“-D TARGET_IPHONE_SIMULATOR”。
查看“Swift编译器 – 客户标志”下的项目构build设置,然后在“其他Swift标志”中设置标志。 将鼠标hover在构buildconfiguration上时,可以通过单击“加号”图标来设置特定于平台的标志。
这样做有两个好处:1)您可以在Swift和Objective-C代码中使用相同的条件testing(“#if TARGET_IPHONE_SIMULATOR”)。 2)你可以编译出只适用于每个版本的variables。
Xcode构build设置截图
我在Swift 3中使用了下面的代码
if TARGET_IPHONE_SIMULATOR == 1 { //simulator } else { //device }
所有在这里描述的Darwin.TargetConditionals : https : //github.com/apple/swift-corelibs-foundation/blob/master/CoreFoundation/Base.subproj/SwiftRuntime/TargetConditionals.h
TARGET_OS_SIMULATOR - Generated code will run under a simulator
你也可以得到UIDevice.current.name
,当它是iPhone Simulator
时它将返回iPhone Simulator
。 当然,有人可以命名他们的设备“iPhone模拟器”…