首页 文章

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

提问于
浏览
218

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

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

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

How can I achieve the same in Swift? 我搜索了堆栈溢出,看了一下docs并且无法搞清楚 .

16 回答

  • 153

    使用以下代码:

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

    适用于 Swift 4Xcode 9.4.1

  • 15

    更新14/09/17

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

    原始答案

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

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

    很明显,这在设备上是错误的,但对于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
    

    或者,甚至,检测 any 模拟器

    #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 表达式中使用它,编译器将发出有关无法访问代码的警告 .

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

  • 13

    Updated Info as of February 20, 2018

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

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

    Previous Answer

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

    import Foundation
    
    struct Platform {
    
        static var isSimulator: Bool {
            return TARGET_OS_SIMULATOR != 0
        }
    
    }
    

    用法示例:

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

    OUTDATED FOR SWIFT 4.1. 请改用 #if targetEnvironment(simulator) . Source

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

    • 在Swift编译器中定义此配置 -D IOS_SIMULATOR - 自定义标志>其他Swift标志

    • 在此下拉列表中选择任何iOS模拟器SDK
      Drop down list

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

    #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
    
  • 263

    我希望这个扩展很方便

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

    用法:

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

    From Xcode 9.3

    #if targetEnvironment(simulator)
    

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

    Swift 3:

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

    Before 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
    
  • 0

    斯威夫特4

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

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

    针对Xcode 9.3进行了更新

  • 1

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

    在许多情况下,

    • TARGET_OS_SIMULATOR 未在Swift代码中设置;由于桥接头可能会意外地导入它,但这很脆弱而且不受支持 . 它甚至在框架中也是不可能的 . 这就是为什么有些人对Swift是否有效感到困惑 .

    • 我强烈建议不要使用架构作为模拟器的替代品 .

    To perform dynamic checks:

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

    您还可以通过检查 SIMULATOR_MODEL_IDENTIFIER 来获取正在模拟的基础模型,这将返回像 iPhone10,3 这样的字符串 .

    To perform static checks:

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

    Xcode 9.3使用新的targetEnvironment条件:

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

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

    #if arch(i386) || arch(x86_64)
    
         //simulator
    #else 
         //device
    
    #endif
    
  • 3

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

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

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

    编辑:不是最好的解决方案,特别是从Xcode 9.3开始 . 见HotJard's answer

  • 8

    在现代系统中:

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

    这很简单 .

  • 5

    TARGET_IPHONE_SIMULATOR 在iOS 9中已弃用. TARGET_OS_SIMULATOR 是替代品 . 也 TARGET_OS_EMBEDDED 可用 .

    来自 TargetConditionals.h

    #if defined(__ GNUC )&&(已定义( APPLE_CPP__)||已定义(__ APPLE_CC__)||已定义(__ MACOS_CLASSIC__))
    . . .
    #define TARGET_OS_SIMULATOR 0
    #define TARGET_OS_EMBEDDED 1
    #define TARGET_IPHONE_SIMULATOR TARGET_OS_SIMULATOR / 已弃用 /
    #define TARGET_OS_NANO TARGET_OS_WATCH / 已弃用 /

  • 0

    在Xcode 7.2(以及之前但我之前没有测试过多少)中,您可以为“Any iOS Simulator”设置特定于平台的构建标志“-D TARGET_IPHONE_SIMULATOR” .

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

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

    Xcode build settings screenshot

  • 2

    这里所有描述 Darwin.TargetConditionalshttps://github.com/apple/swift-corelibs-foundation/blob/master/CoreFoundation/Base.subproj/SwiftRuntime/TargetConditionals.h

    TARGET_OS_SIMULATOR - Generated code will run under a simulator

  • 46

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

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

    斯威夫特4:

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

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

    但是,如您所知, simModelCode 不是一个舒适的代码,立即了解哪种模拟器是如此推出的,如果您需要,您可以尝试查看其他SO answer以确定当前的iPhone /设备型号并拥有更多人类可读的字串 .

相关问题