24

Why doesn't this code work when compiling an ApplicationTests unit test bundle?

#if TARGET_OS_IPHONE
   #import <Foundation/Foundation.h>
   #import <UIKit/UIKit.h>
#else
   #import <Cocoa/Cocoa.h>
#endif

One of my dependencies has this check and compiles just fine in my main application bundles, but it tries to load <Cocoa/Cocoa.h> when compiling my ApplicationTests bundle. It's probably just my lack of understanding of Xcode, but I get nervous when my test bundles don't build. Any suggestions?

ricardopereira
  • 11,118
  • 5
  • 63
  • 81
Matt Baker
  • 3,394
  • 3
  • 25
  • 35

6 Answers6

41

You need to add

#include <TargetConditionals.h>

source: https://opensource.apple.com/source/CarbonHeaders/CarbonHeaders-8A428/TargetConditionals.h.auto.html

DomQ
  • 4,184
  • 38
  • 37
antho
  • 943
  • 2
  • 8
  • 11
  • 1
    When running on watchOS Scheme, I was getting all TARGET_OS_* being evaluated to 0. Using the above import solved the problem – desu sai venkat Dec 20 '21 at 08:05
20

The simplest solution is to move the #import <Foundation/Foundation.h> statement out if the #if condition and replace Cocoa with AppKit like this:

#import <Foundation/Foundation.h>
#if TARGET_OS_IPHONE
   #import <UIKit/UIKit.h>
#else
   #import <AppKit/AppKit.h>
#endif

The Foundation umbrella header imports the NSObjCRuntime header which in turn imports the TargetConditionals header.

fjoachim
  • 906
  • 6
  • 11
15

I had a similar problem: TARGET_OS_IPHONE isn't defined when building a static library. My solution was to add "-DTARGET_OS_IPHONE" to the "Other C Flags" section of the target build options.

Sujay
  • 2,510
  • 2
  • 27
  • 47
James J
  • 6,428
  • 6
  • 35
  • 45
  • Yup, this ended up working. I'm not sure how I feel about having to force this being true, but I guess it works. Thanks. – Matt Baker Sep 20 '10 at 22:43
  • @MattBaker - Same issue here and I agree! – James Bedford Jun 25 '12 at 22:13
  • 8
    That sounds like the wrong solution. You should do as antho suggested below and include TargetConditionals.h. Documented here: http://developer.apple.com/library/ios/#DOCUMENTATION/Xcode/Conceptual/ios_development_workflow/15-Configuring_Applications/configuring_applications.html – Rhythmic Fistman Sep 04 '12 at 16:53
  • This was the solution that worked for me in a Swift Test Target. – ricardopereira Nov 16 '18 at 14:30
0

Both work well

  • #import "TargetConditionals.h"
  • #import <Foundation/Foundation.h>
<Foundation/Foundation.h> 
    |
    └-#import <Foundation/NSObjCRuntime.h>
        |
        └- #include <TargetConditionals.h> 
                |
                └- defined TARGET_OS_IPHONE
Bill
  • 194
  • 1
  • 8
0

The solution for me in Xcode 12.5 is to add TARGET_OS_IPHONE or TARGET_OS_IPHONE=1 to GCC_PREPROCESSOR_DEFINITIONS_NOT_USED_IN_PRECOMPS in build settings or in an .xcconfig file.

Details:

After updating to Xcode 12.5 beta, now carthage bootstrap will fail when trying to build iRate 1.12.2. I looked in the carthage build log, and the error responsible for the failure is:

error: 'TARGET_OS_IPHONE' is not defined, evaluates to 0 [-Werror,-Wundef-prefix=TARGET_OS_]

The problem for me is that iRate is no longer under development, and I'd rather not fork iRate it just to override some broken build setting.

However, there is a nifty workaround trick that I learned from the folks over at Carthage: you can override the build settings of any project using any .xcconfig file by setting an environment variable, XCODE_XCCONFIG_FILE=path/to/my.xcconfig before running xcodebuild. Any settings in that .xcconfig file will now override the settings of whatever project you're building with xcodebuild.

Furthermore you can do this dynamically by a script that you call instead of calling xcodebuild, e.g.:

#!/usr/bin/env bash

# Save this script as 'injectXcodeBuild.sh'
# Run it in place of xcodebuild (all arguments get forwarded through)
# The echo'd commands below will override any settings of the
# projects that get built by xcodebuild through this script.

set -euo pipefail

xcconfig=$(mktemp /tmp/static.xcconfig.XXXXXX)
trap 'rm -f "$xcconfig"' INT TERM HUP EXIT

echo 'GCC_PREPROCESSOR_DEFINITIONS_NOT_USED_IN_PRECOMPS=TARGET_OS_IPHONE=1' >> $xcconfig

export XCODE_XCCONFIG_FILE="$xcconfig"

xcodebuild "$@"

Alternatively instead of xcodebuild this script could call carthage if you're needing to override some Carthage dependency's build settings. It might also work for CocoaPods pod command (I'm not sure).

scaly
  • 509
  • 8
  • 18
0

Note: in Swift one must use:

#if os(iOS)
CommaToast
  • 11,370
  • 7
  • 54
  • 69