Detect If Swift App Is Being Run from Xcode

Detect if Swift app is being run from Xcode

Clarification: Your C code (and the Swift version below) checks if
the program is run under debugger control, not if it's being run from
Xcode. One can debug a program outside of Xcode (by calling lldb or
gdb directly) and one can run a program from Xcode without debugging it
(if the “Debug Executable” checkbox in the scheme setting is off).


You could simply keep the C function and call it from Swift.
The recipes given in How do I call Objective-C code from Swift? apply to pure C code as well.

But it is actually not too complicated to translate that code to Swift:

func amIBeingDebugged() -> Bool {
// Buffer for "sysctl(...)" call's result.
var info = kinfo_proc()
// Counts buffer's size in bytes (like C/C++'s `sizeof`).
var size = MemoryLayout.stride(ofValue: info)
// Tells we want info about own process.
var mib : [Int32] = [CTL_KERN, KERN_PROC, KERN_PROC_PID, getpid()]
// Call the API (and assert success).
let junk = sysctl(&mib, UInt32(mib.count), &info, &size, nil, 0)
assert(junk == 0, "sysctl failed")
// Finally, checks if debugger's flag is present yet.
return (info.kp_proc.p_flag & P_TRACED) != 0
}

Update for Swift 5 (Xcode 10.7):
strideofValue and the related functions do not exist anymore,
they have been replaced by MemoryLayout.stride(ofValue:).

Remarks:

  • kinfo_proc() creates a fully initialized structure with all
    fields set to zero, therefore setting info.kp_proc.p_flag = 0 is not necessary.
  • The C int type is Int32 is Swift.
  • sizeof(info) from the C code has to be strideOfValue(info)
    in Swift to include the structure padding. With sizeofValue(info)
    the above code always returned false in the Simulator for 64-bit devices. This was the most difficult part to figure out.

Swift 2 logic:

func amIBeingDebugged() -> Bool {
var info = kinfo_proc()
var mib : [Int32] = [CTL_KERN, KERN_PROC, KERN_PROC_PID, getpid()]
var size = strideofValue(info)
let junk = sysctl(&mib, UInt32(mib.count), &info, &size, nil, 0)
assert(junk == 0, "sysctl failed")
return (info.kp_proc.p_flag & P_TRACED) != 0
}

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

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.

iOS - detect if app is running from Xcode

You can check if a debugger is attached (probably, but not definitely, Xcode) using sysctl. Here's how HockeyApp does it:

#include <Foundation/Foundation.h>
#include <sys/sysctl.h>

/**
* Check if the debugger is attached
*
* Taken from https://github.com/plausiblelabs/plcrashreporter/blob/2dd862ce049e6f43feb355308dfc710f3af54c4d/Source/Crash%20Demo/main.m#L96
*
* @return `YES` if the debugger is attached to the current process, `NO` otherwise
*/
- (BOOL)isDebuggerAttached {
static BOOL debuggerIsAttached = NO;

static dispatch_once_t debuggerPredicate;
dispatch_once(&debuggerPredicate, ^{
struct kinfo_proc info;
size_t info_size = sizeof(info);
int name[4];

name[0] = CTL_KERN;
name[1] = KERN_PROC;
name[2] = KERN_PROC_PID;
name[3] = getpid(); // from unistd.h, included by Foundation

if (sysctl(name, 4, &info, &info_size, NULL, 0) == -1) {
NSLog(@"[HockeySDK] ERROR: Checking for a running debugger via sysctl() failed: %s", strerror(errno));
debuggerIsAttached = false;
}

if (!debuggerIsAttached && (info.kp_proc.p_flag & P_TRACED) != 0)
debuggerIsAttached = true;
});

return debuggerIsAttached;
}

How to detect if an iOS app is running on an M1 mac?

Apple's framework allows you to detect if the app is running as an iOS app on Mac by using the process info flag isiOSAppOnMac.

This flag is available from iOS 14.0 and therefore needs to be encapsulated to only run on those versions. Note that since version 14.0 is also the first version for iOS on Mac, you can safely assume that it cannot be on a Mac if the version is prior 14.0.

// Swift
var isiOSAppOnMac = false
if #available(iOS 14.0, *) {
isiOSAppOnMac = ProcessInfo.processInfo.isiOSAppOnMac
}
print("\(isiOSAppOnMac ? "iOS app on Mac" : "not iOS on Mac")!")

Or if you prefer Objective-C:

// Objective-C
BOOL isiOSAppOnMac = false;
if (@available(iOS 14.0, *)) {
isiOSAppOnMac = [NSProcessInfo processInfo].isiOSAppOnMac;
}
NSLog(@"%@", isiOSAppOnMac ? @"iOS app on Mac" : @"not iOS app on Mac");

Reference:
Apple: Running Your iOS Apps on macOS

Detect if the application in background or foreground in swift

[UIApplication sharedApplication].applicationState will return current state of applications such as:

  • UIApplicationStateActive
  • UIApplicationStateInactive
  • UIApplicationStateBackground

or if you want to access via notification see UIApplicationDidBecomeActiveNotification

Swift 3+

let state = UIApplication.shared.applicationState
if state == .background || state == .inactive {
// background
} else if state == .active {
// foreground
}

switch UIApplication.shared.applicationState {
case .background, .inactive:
// background
case .active:
// foreground
default:
break
}

Objective C

UIApplicationState state = [[UIApplication sharedApplication] applicationState];
if (state == UIApplicationStateBackground || state == UIApplicationStateInactive) {
// background
} else if (state == UIApplicationStateActive) {
// foreground
}


Related Topics



Leave a reply



Submit