Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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

Tags:

ios

swift

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?

like image 251
Rafa de King Avatar asked Jul 21 '14 15:07

Rafa de King


People also ask

What does the targetEnvironment () compiler condition do?

The targetEnvironment() platform condition returns true when code is being compiled for the specified environment; otherwise, it returns false .

How can I test my iOS app on a real device?

Open up a project in Xcode and click on the device near the Run ▶ button at the top left of your Xcode screen. Plug your iPhone into your computer. You can select your device from the top of the list. Unlock your device and (⌘R) run the application.

Does iOS simulator simulate performance?

The simulator does a really lousy job of indicating app performance. In fact it doesn't try. For most things the simulator is much, much faster than an iOS device. It runs your code on an x86 processor, which is much faster than the ARM and has many times more memory.

Where are iOS simulators stored?

type: ~/Library/Application Support/iPhone Simulator. The Directories are the iOS version of the different Simulators. The Sub Directories are the Apps install on the simulator. The Documents folder is where the user generated content which gets backup up onto iCloud.


2 Answers

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.

like image 54
Gabriele Petronella Avatar answered Oct 02 '22 06:10

Gabriele Petronella


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 
like image 42
mbelsky Avatar answered Oct 02 '22 05:10

mbelsky