How to detect if app is being built for device or simulator in Swift
IosSwiftIos 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
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.
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:
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.- 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.
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.