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

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/24869481/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-31 01:28:39  来源:igfitidea点击:

How to detect if app is being built for device or simulator in Swift

iosswift

提问by RaffAl

In Objective-C we can know if an app is being built for device or simulator using macros:

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

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

These are compile time macros and not available at runtime.

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

How can I achieve the same in Swift?

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

回答by Gabriele Petronella

Update 30/01/19

更新 30/01/19

While this answer may work, the recommended solution for a static check (as clarified by several Apple engineers) is to define a custom compiler flag targeting iOS Simulators. For detailed instructions on how to do to it, see @mbelsky's answer.

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

Original answer

原答案

If you need a static check (e.g. not a runtime if/else) you can't detect the simulator directly, but you can detect iOS on a desktop architecture like follows

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

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


After Swift 4.1version

之后雨燕4.1版本

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

最新使用,现在直接适用于所有类型的模拟器,只需应用一种条件即可——

#if targetEnvironment(simulator)
  // your simulator code
#else
  // your real device code
#endif

For more clarification, you can check Swiftproposal SE-0190

如需更多说明,您可以查看Swift提案SE-0190



For older version-

对于旧版本-

Clearly, this is false on a device, but it returns true for the iOS Simulator, as specified in the documentation:

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

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

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

If you are developing for a simulator other than iOS, you can simply vary the osparameter: e.g.

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

Detect the watchOSsimulator

检测watchOS模拟器

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

Detect the tvOSsimulator

检测tvOS模拟器

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

Or, even, detect anysimulator

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

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


If you instead are ok with a runtime check, you can inspect the TARGET_OS_SIMULATORvariable (or TARGET_IPHONE_SIMULATORin iOS 8 and below), which is truthy on a simulator.

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

Please notice that this is different and slightly more limited than using a preprocessor flag. For instance you won't be able to use it in place where a if/elseis syntactically invalid (e.g. outside of functions scopes).

请注意,这与使用预处理器标志不同,并且略有限制。例如,您将无法在 aif/else语法无效的地方使用它(例如,在函数范围之外)。

Say, for example, that you want to have different imports on the device and on the simulator. This is impossible with a dynamic check, whereas it's trivial with a static check.

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

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

Also, since the flag is replaced with a 0or a 1by the swift preprocessor, if you directly use it in a if/elseexpression the compiler will raise a warning about unreachable code.

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

In order to work around this warning, see one of the other answers.

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

回答by mbelsky

OUTDATED FOR SWIFT 4.1.Use #if targetEnvironment(simulator)instead. Source

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

To detect simulator in Swift you can use build configuration:

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

  • Define this configuration -D IOS_SIMULATORin Swift Compiler - Custom Flags > Other Swift Flags
  • Select Any iOS Simulator SDKin this drop down Drop down list
  • 定义这个配置-D IOS_SIMULATORin Swift Compiler - Custom Flags > Other Swift Flags
  • 在此下拉列表中选择Any iOS Simulator SDK下拉列表

Now you could use this statement to detect simulator:

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

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

Also you could extend UIDevice class:

你也可以扩展 UIDevice 类:

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

回答by Daniel

Updated Info as of February 20, 2018

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

It looks like @russbishop has an authoritative answer that renders this answer "incorrect" - even though it appeared to work for a long time.

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

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

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

Previous Answer

上一个答案

Based on @WZW's answer and @Pang's comments, I created a simple utility struct. This solution avoids warning produced by @WZW's answer.

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

import Foundation

struct Platform {

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

}

Example usage:

用法示例:

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

回答by HotJard

From Xcode 9.3

从 Xcode 9.3

#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)

Swift 支持具有单个有效参数模拟器的新平台条件 targetEnvironment。现在可以使用“#if targetEnvironment(simulator)”形式的条件编译来检测构建目标何时是模拟器。在通过现有的 os() 和 arch() 平台条件评估似乎是间接测试模拟器环境的平台条件时,Swift 编译器将尝试检测、警告和建议使用 targetEnvironment(simulator)。(SE-0190)

iOS 9+:

iOS 9+:

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

Swift 3:

斯威夫特 3:

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

Before iOS 9:

在 iOS 9 之前:

extension UIDevice {
    static var isSimulator: Bool {
        return UIDevice.currentDevice().model == "iPhone Simulator"
    }
}

Objective-C:

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

回答by Matt Swift

Swift 4

斯威夫特 4

You can now use targetEnvironment(simulator)as an argument.

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

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

Updated for Xcode 9.3

为 Xcode 9.3 更新

回答by russbishop

Let me clarify some things here:

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

  1. TARGET_OS_SIMULATORis not set in Swift code in many cases; you may be accidentally getting it imported due to a bridging header but this is brittle and not supported. It also isn't even possible in frameworks. This is why some people are confused about whether this works in Swift.
  2. I strongly advise against using architecture as a substitute for simulator.
  1. TARGET_OS_SIMULATOR在许多情况下,没有在 Swift 代码中设置;由于桥接头,您可能会意外地将其导入,但这很脆弱且不受支持。它甚至在框架中也是不可能的。这就是为什么有些人对这是否适用于 Swift 感到困惑。
  2. 我强烈建议不要使用架构来代替模拟器。

To perform dynamic checks:

要执行动态检查:

Checking ProcessInfo.processInfo.environment["SIMULATOR_DEVICE_NAME"] != nilis perfectly fine.

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

You can also get the underlying model being simulated by checking SIMULATOR_MODEL_IDENTIFIERwhich will return strings like iPhone10,3.

您还可以通过检查SIMULATOR_MODEL_IDENTIFIER哪些将返回像iPhone10,3.

To perform static checks:

要执行静态检查:

Xcode 9.2 & earlier: define your own Swift compilation flag (as shown in other answers).

Xcode 9.2 及更早版本:定义您自己的 Swift 编译标志(如其他答案中所示)。

Xcode 9.3+ use the new targetEnvironment condition:

Xcode 9.3+ 使用新的 targetEnvironment 条件:

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

回答by akaru

What works for me since Swift 1.0 is checking for an architecture other than arm:

自 Swift 1.0 以来对我有用的是检查 arm 以外的架构:

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

     //simulator
#else 
     //device

#endif

回答by shim

Runtime, but simpler than most of the other solutions here:

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

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

Alternatively, you can just call an Objective-C helper function that returns a boolean that uses the preprocessor macro (especially if you're already mixing in your project).

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

Edit: Not the best solution, especially as of Xcode 9.3. See HotJard's answer

编辑:不是最好的解决方案,尤其是从 Xcode 9.3 开始。见HotJard 的回答

回答by Fattie

In modern systems:

在现代系统中:

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

It's dat easy.

这很容易。

回答by Lucas Chwe

I hope this extension comes handy.

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

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

Usage:

用法:

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