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

时间:2014-07-21 15:59:51

标签: ios swift

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

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

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

如何在Swift中实现相同的目标?我搜索了堆栈溢出,看了一下docs并且无法弄明白。

20 个答案:

答案 0 :(得分:291)

更新30/01/19

虽然这个答案可能有效,但建议的静态检查解决方案(由几位Apple工程师澄清)是定义一个针对iOS模拟器的自定义编译器标志。有关如何操作的详细说明,请参阅@mbelsky's answer

原始答案

如果您需要静态检查(例如,不是运行时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 提案SE-0190


  

旧版 -

显然,在设备上这是假的,但对于iOS模拟器,它会返回true,如documentation中所述:

  

当为32位iOS模拟器编译代码时,arch(i386)构建配置返回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变量(或iOS 8及更低版本中的TARGET_IPHONE_SIMULATOR),这在模拟器上是真实的。

请注意,这与使用预处理程序标志不同,稍微有限。例如,如果if/else在语法上无效(例如在函数范围之外),您将无法使用它。

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

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

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

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

答案 1 :(得分:159)

OUTIFT for SWIFT 4.1。请改用#if targetEnvironment(simulator)Source

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

  • Swift编译器 - 自定义标志>中定义此配置 -D IOS_SIMULATOR 其他Swift Flags
  • 在此下拉列表Drop down list
  • 中选择任意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

答案 2 :(得分:155)

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

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

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

上一个答案

根据@ WZW的回答和@Pang的评论,我创建了一个简单的实用程序结构。这个解决方案避免了@ WZW的回答所产生的警告。

import Foundation

struct Platform {

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

}

使用示例:

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

答案 3 :(得分:65)

来自Xcode 9.3

#if targetEnvironment(simulator)
  

Swift支持一个新的平台条件targetEnvironment   单个有效参数模拟器。条件汇编表格   ' #if targetEnvironment(模拟器)'现在可用于检测构建目标何时是模拟器。 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"
    }
}

<强>目标-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

答案 4 :(得分:50)

Swift 4

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

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

针对Xcode 9.3进行了更新

答案 5 :(得分:27)

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

    在许多情况下,
  1. TARGET_OS_SIMULATOR未在Swift代码中设置;由于桥接头可能会意外地导入它,但这很脆弱而且不受支持。它在框架中也是不可能的。这就是为什么有些人对这是否适用于Swift感到困惑。
  2. 我强烈反对使用架构作为模拟器的替代品。
  3. 执行动态检查:

    检查ProcessInfo.processInfo.environment["SIMULATOR_DEVICE_NAME"] != nil完全没问题。

    您还可以通过选中SIMULATOR_MODEL_IDENTIFIER来获取模拟的基础模型,iPhone10,3会返回#if targetEnvironment(simulator) // for sim only #else // for device #endif 等字符串。

    执行静态检查:

    Xcode 9.2&amp;之前:定义自己的Swift编译标志(如其他答案所示)。

    Xcode 9.3+使用新的targetEnvironment条件:

    Hybris

答案 6 :(得分:15)

因为Swift 1.0正在检查除arm之外的架构,所以对我有用:

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

     //simulator
#else 
     //device

#endif

答案 7 :(得分:14)

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

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

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

编辑:不是最佳解决方案,尤其是Xcode 9.3。见HotJard's answer

答案 8 :(得分:9)

在现代系统中:

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

它很简单。

答案 9 :(得分:5)

在iOS 9中不推荐使用

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

答案 10 :(得分:3)

在Xcode 7.2中(早些时候但我还没有测试过多少),你可以设置一个特定于平台的构建标志&#34; -D TARGET_IPHONE_SIMULATOR&#34; for&#34;任何iOS模拟器&#34;。

查看&#34; Swift Compiler - Customer Flags&#34;下的项目构建设置。然后在&#34;其他Swift Flags&#34;中设置标志。您可以点击“加号”设置特定于平台的标记。将鼠标悬停在构建配置上时的图标。

这样做有以下几个优点:1)您可以在Swift和Objective-C代码中使用相同的条件测试(&#34; #if TARGET_IPHONE_SIMULATOR&#34;)。 2)您可以编译出仅适用于每个构建的变量。

Xcode build settings screenshot

答案 11 :(得分:2)

此处描述 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

答案 12 :(得分:1)

我在Swift 3中使用了以下代码

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

答案 13 :(得分:1)

雨燕4:

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

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

但是,正如您所知,simModelCode并不是立即了解启动哪种模拟器的舒适代码,因此,如果需要,您可以尝试查看其他answer以确定当前的iPhone /设备型号,并具有更易读的字符串。

答案 14 :(得分:1)

我希望此扩展程序方便

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

用法:

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

答案 15 :(得分:1)

这是一个基于HotJard's出色答案above的Xcode 11 Swift示例,它还添加了isDevice Bool并使用SIMULATOR_UDID代替了名称。在每一行都完成了变量分配,因此您可以选择轻松地在调试器中检查它们。

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

答案 16 :(得分:1)

除了其他答案。

在Objective-c中,只需确保包含 TargetConditionals

#include <TargetConditionals.h>

在使用TARGET_OS_SIMULATOR之前。

答案 17 :(得分:0)

使用以下代码:

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

适用于Swift 4Xcode 9.4.1

答案 18 :(得分:0)

Xcode 11,Swift 5

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

答案 19 :(得分:0)

迅速5.2.4 Xcode 11.7


 #if targetEnvironment(simulator)

 #endif