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

IosSwift

Ios Problem Overview


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

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

Ios Solutions


Solution 1 - Ios

Update 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.

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 (arch(i386) || arch(x86_64)) && os(iOS)
    ...
#endif

After Swift 4.1 version

> 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 Swift proposal 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:

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

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

Detect the watchOS simulator

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

Detect the tvOS simulator

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

Or, even, detect any simulator

#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_SIMULATOR variable (or TARGET_IPHONE_SIMULATOR in iOS 8 and below), which is truthy on a simulator.

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/else is syntactically invalid (e.g. outside of functions scopes).

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 0 or a 1 by the swift preprocessor, if you directly use it in a if/else expression the compiler will raise a warning about unreachable code.

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

Solution 2 - Ios

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

To detect simulator in Swift you can use build configuration:

  • Define this configuration -D IOS_SIMULATOR in Swift Compiler - Custom Flags > Other Swift Flags
  • Select Any iOS Simulator SDK in this drop down Drop down list

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:

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

Solution 3 - Ios

Updated Info as of February 20, 2018

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

https://stackoverflow.com/questions/24869481/detect-if-app-is-being-built-for-device-or-simulator-in-swift/48871580#48871580

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.

import Foundation

struct Platform {

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

}

Example usage:

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

Solution 4 - Ios

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

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

Solution 5 - Ios

Swift 4

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

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

Updated for Xcode 9.3

Solution 6 - Ios

Let me clarify some things here:

  1. TARGET_OS_SIMULATOR is 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.

To perform dynamic checks:

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

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

To perform static checks:

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

Xcode 9.3+ use the new targetEnvironment condition:

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

Solution 7 - Ios

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

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

     //simulator
#else 
     //device

#endif

Solution 8 - Ios

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

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

Solution 9 - Ios

In modern systems:

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

It's dat easy.

Solution 10 - Ios

Swift 5.2.4 Xcode 11.7


 #if targetEnvironment(simulator)

 #endif

Solution 11 - Ios

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

Solution 12 - Ios

TARGET_IPHONE_SIMULATOR is deprecated in iOS 9. TARGET_OS_SIMULATOR is the replacement. Also TARGET_OS_EMBEDDED is available.

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

Solution 13 - Ios

Xcode 11, Swift 5

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

Solution 14 - Ios

In Xcode 7.2 (and earlier but I haven't tested how much earlier), you can set a platform specific build flag "-D TARGET_IPHONE_SIMULATOR" for "Any iOS Simulator".

Look in the project build settings under "Swift Compiler - Customer Flags" and then set the flag in "Other Swift Flags". You can set a platform specific flag by clicking the 'plus' icon when you hover over a build configuration.

There are a couple of advantages of doing it this way: 1) You can use the same conditional test ("#if TARGET_IPHONE_SIMULATOR") in your Swift and Objective-C code. 2) You can compile out variables that only apply to each build.

Xcode build settings screenshot

Solution 15 - Ios

All described here 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

Solution 16 - Ios

Use this below code:

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

Works for Swift 4 and Xcode 9.4.1

Solution 17 - Ios

Here is an Xcode 11 Swift example based off of HotJard's awesome answer above, this also adds an isDevice Bool and uses SIMULATOR_UDID instead of name. Variable assignments are done on each line so that you can more easily examine them in the debugger if you choose to.

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

There also is the dictionary entry of DTPlatformName which should contain simulator.

Solution 18 - Ios

In addition to other answers.

In Objective-c, Just make sure you included TargetConditionals.

#include <TargetConditionals.h>

before using TARGET_OS_SIMULATOR.

Solution 19 - Ios

I used this below code in Swift 3

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

Solution 20 - Ios

Swift 4:

Currently, I prefer to use the ProcessInfo class to know if the device is a simulator and which kind of device is in use:

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

But, as you know, simModelCode isn't a comfortable code to understand immediatly which kind of simulator was launched so, if your need, you can try to see this other SO answer to determine the current iPhone/device model and to have a more human readable string.

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionRafa de KingView Question on Stackoverflow
Solution 1 - IosGabriele PetronellaView Answer on Stackoverflow
Solution 2 - IosmbelskyView Answer on Stackoverflow
Solution 3 - IosDanielView Answer on Stackoverflow
Solution 4 - IosHotJardView Answer on Stackoverflow
Solution 5 - IosJustin BushView Answer on Stackoverflow
Solution 6 - IosrussbishopView Answer on Stackoverflow
Solution 7 - IosakaruView Answer on Stackoverflow
Solution 8 - IosshimView Answer on Stackoverflow
Solution 9 - IosFattieView Answer on Stackoverflow
Solution 10 - IoshectorsvillView Answer on Stackoverflow
Solution 11 - IosLucas ChweView Answer on Stackoverflow
Solution 12 - IosNuthatchView Answer on Stackoverflow
Solution 13 - IoswebcpuView Answer on Stackoverflow
Solution 14 - IosxgerritView Answer on Stackoverflow
Solution 15 - IosdimpiaxView Answer on Stackoverflow
Solution 16 - IosHaroldo GondimView Answer on Stackoverflow
Solution 17 - IosAlex ZavatoneView Answer on Stackoverflow
Solution 18 - IosM. AdamView Answer on Stackoverflow
Solution 19 - Iosak_ninanView Answer on Stackoverflow
Solution 20 - IosAlessandro OrnanoView Answer on Stackoverflow