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

Raf*_*fAl 256 ios swift

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

#if TARGET_IPHONE_SIMULATOR
    // Simulator
#else
    // Device
#endif
Run Code Online (Sandbox Code Playgroud)

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

我怎样才能在Swift中实现同样的目标?我搜索了堆栈溢出,看了一下docs并且无法搞清楚.

Gab*_*lla 324

更新30/01/19

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

原始答案

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

#if (arch(i386) || arch(x86_64)) && os(iOS)
    ...
#endif
Run Code Online (Sandbox Code Playgroud)

经过Swift 4.1版本

最新的使用,现在直接适用于所有类型的模拟器的所有条件 - 只需要应用一个条件 -

#if targetEnvironment(simulator)
  // your simulator code
#else
  // your real device code
#endif
Run Code Online (Sandbox Code Playgroud)

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


对于旧版本 -

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

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

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

检测watchOS模拟器

#if (arch(i386) || arch(x86_64)) && os(watchOS)
...
#endif
Run Code Online (Sandbox Code Playgroud)

检测tvOS模拟器

#if (arch(i386) || arch(x86_64)) && os(tvOS)
...
#endif
Run Code Online (Sandbox Code Playgroud)

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

#if (arch(i386) || arch(x86_64)) && (os(iOS) || os(watchOS) || os(tvOS))
...
#endif
Run Code Online (Sandbox Code Playgroud)

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

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

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

#if (arch(i386) || arch(x86_64)) && os(iOS)
  import Foo
#else
  import Bar
#endif
Run Code Online (Sandbox Code Playgroud)

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

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

  • 在Swift 4.1中,你可以说`#if targetEnvironment(simulator)`:)(https://github.com/apple/swift-evolution/blob/master/proposals/0190-target-environment-platform- condition.md) (8认同)
  • 这个答案不是解决这个问题的最佳方法!mbelsky的答案(目前非常遥远)是唯一没有任何陷阱的解决方案.即使是来自Apple的Greg Parker也建议这样做:https://lists.swift.org/pipermail/swift-evolution/Week-of-Mon-20160125/007960.html (3认同)
  • 到目前为止,@ russbishop对数百人提供了有用的建议,弥补了缺失的API.不是通过在顶部签署评论来劫持答案,而是进行沟通.我更新了答案以澄清这不再是最新的解决方案,而且我提供了一个看起来更正确的链接. (2认同)

mbe*_*sky 167

过时的SWIFT 4.1.#if targetEnvironment(simulator)改用.资源

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

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

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

#if IOS_SIMULATOR
    print("It's an iOS Simulator")
#else
    print("It's a device")
#endif
Run Code Online (Sandbox Code Playgroud)

你也可以扩展UIDevice类:

extension UIDevice {
    var isSimulator: Bool {
        #if IOS_SIMULATOR
            return true
        #else
            return false
        #endif
    }
}
// Example of usage: UIDevice.current.isSimulator
Run Code Online (Sandbox Code Playgroud)

  • 这应该是最好的答案!甚至来自Apple的Greg Parker也提出了这样的建议:https://lists.swift.org/pipermail/swift-evolution/Week-of-Mon-20160125/007960.html (7认同)
  • 这是唯一正确的答案.你也可以使用`OTHER_SWIFT_FLAGS = TARGET_OS_EMBEDDED`和`OTHER_SWIFT_FLAGS [sdk = embeddedsimulator*] = TARGET_OS_SIMULATOR`在`xcconfig`文件中设置它来覆盖模拟器. (3认同)

Dan*_*iel 156

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

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

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

上一个答案

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

import Foundation

struct Platform {

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

}
Run Code Online (Sandbox Code Playgroud)

用法示例:

if Platform.isSimulator {
    print("Running on Simulator")
}
Run Code Online (Sandbox Code Playgroud)

  • 比接受的解决方案更好的解决方案.事实上,如果有一天(即使它不太可能)Apple决定在iOS设备上使用i386或x85_64,接受的答案将无效......或者即使台式电脑获得新的处理! (10认同)
  • 确认这在Xcode 7上完美运行:`public let IS_SIMULATOR =(TARGET_OS_SIMULATOR!= 0)`...同样的事情,简化.+1谢谢 (2认同)
  • @Fattie`TARGET_OS_SIMULATOR!= 0`已经在答案**中了**.这是丹尼尔给出的解决方案.没有必要在自由变量中再次添加它,它已经存在.如果你认为在结构中使用它是坏的并且将它放在自由变量中更好,那么发表关于此的评论或做出你自己的答案.谢谢. (2认同)

Hot*_*ard 67

来自Xcode 9.3

#if targetEnvironment(simulator)
Run Code Online (Sandbox Code Playgroud)

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

iOS 9+:

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

斯威夫特3:

extension UIDevice {
    static var isSimulator: Bool {
        return ProcessInfo.processInfo.environment["SIMULATOR_DEVICE_NAME"] != nil
    }
}
Run Code Online (Sandbox Code Playgroud)

在iOS 9之前:

extension UIDevice {
    static var isSimulator: Bool {
        return UIDevice.currentDevice().model == "iPhone Simulator"
    }
}
Run Code Online (Sandbox Code Playgroud)

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
Run Code Online (Sandbox Code Playgroud)

  • 使用定义的常量比较字符串更脆弱. (2认同)

Mat*_*ift 54

斯威夫特4

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

#if targetEnvironment(simulator)
    // Simulator
#else
    // Device
#endif
Run Code Online (Sandbox Code Playgroud)

针对Xcode 9.3进行了更新

  • 这应该是公认的答案.我希望SO能够根据OS /编程语言的更新提出新的建议答案. (6认同)
  • 这是一个很好的观点@quemeful - 这是SO的少数几个基本失败之一.由于计算系统变化如此之快,**几乎所有关于SO的答案都会随着时间推移而变得错误** (4认同)

hec*_*ill 38

斯威夫特 5.2.4 Xcode 11.7


 #if targetEnvironment(simulator)

 #endif

Run Code Online (Sandbox Code Playgroud)


rus*_*hop 34

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

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

#if targetEnvironment(simulator)
    // for sim only
#else
    // for device
#endif
Run Code Online (Sandbox Code Playgroud)


aka*_*aru 15

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

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

     //simulator
#else 
     //device

#endif
Run Code Online (Sandbox Code Playgroud)


shi*_*him 14

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

if TARGET_OS_SIMULATOR != 0 {
    // target is current running in the simulator
}
Run Code Online (Sandbox Code Playgroud)

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

编辑:不是最好的解决方案,特别是从Xcode 9.3开始.请参阅HotJard的回答

  • 我这样做但在else子句中得到警告,因为它"永远不会被执行".我们有一个零警告规则,所以:-( (3认同)

Fat*_*tie 9

在现代系统中:

#if targetEnvironment(simulator)
    // sim
#else
    // device
#endif
Run Code Online (Sandbox Code Playgroud)

这很简单.


Luc*_*hwe 7

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

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

用法:

if UIDevice.isSimulator {
    print("running on simulator")
}
Run Code Online (Sandbox Code Playgroud)


Unc*_*rks 6

Xcode 11、斯威夫特 5

    #if !targetEnvironment(macCatalyst)
    #if targetEnvironment(simulator)
        true
    #else
        false        
    #endif
    #endif
Run Code Online (Sandbox Code Playgroud)


Nut*_*tch 5

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 */ 
Run Code Online (Sandbox Code Playgroud)