Mike Hedman Mike Hedman - 3 months ago 19
Swift Question

Detect if Swift app is being run from Xcode

I would like to programmatically determine if the iOS app is being run directly from XCode (either in the simulator or on a tethered device).
I've tried the -D DEBUG solution described here, but when I then disconnect from Xcode and re-run the app, it still thinks it's in debug mode.
I think what I'm looking for is a Swift version of this function

#include <assert.h>
#include <stdbool.h>
#include <sys/types.h>
#include <unistd.h>
#include <sys/sysctl.h>

static bool AmIBeingDebugged(void)
// Returns true if the current process is being debugged (either
// running under the debugger or has a debugger attached post facto).
{
int junk;
int mib[4];
struct kinfo_proc info;
size_t size;

// Initialize the flags so that, if sysctl fails for some bizarre
// reason, we get a predictable result.

info.kp_proc.p_flag = 0;

// Initialize mib, which tells sysctl the info we want, in this case
// we're looking for information about a specific process ID.

mib[0] = CTL_KERN;
mib[1] = KERN_PROC;
mib[2] = KERN_PROC_PID;
mib[3] = getpid();

// Call sysctl.
size = sizeof(info);
junk = sysctl(mib, sizeof(mib) / sizeof(*mib), &info, &size, NULL, 0);
assert(junk == 0);

// We're being debugged if the P_TRACED flag is set.
return ( (info.kp_proc.p_flag & P_TRACED) != 0 );
}

Answer

You could simply keep the C function and call it from Swift. The recipes given in How to 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 {

    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
}

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.

Update for Swift 3 (Xcode 8 beta 6):

strideofValue and the related functions do not exist anymore, they have been replaced by MemoryLayout:

func amIBeingDebugged() -> Bool {

    var info = kinfo_proc()
    var mib : [Int32] = [CTL_KERN, KERN_PROC, KERN_PROC_PID, getpid()]
    var size = MemoryLayout<kinfo_proc>.stride
    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
}
Comments