Preprocessor macro for OS X targets?

As per the documentation in TargetConditionals.h (it seems, as of 2020; any platform):

+---------------------------------------------------------------------+
|                            TARGET_OS_MAC                            |
| +---+ +-----------------------------------------------+ +---------+ |
| |   | |               TARGET_OS_IPHONE                | |         | |
| |   | | +---------------+ +----+ +-------+ +--------+ | |         | |
| |   | | |      IOS      | |    | |       | |        | | |         | |
| |OSX| | |+-------------+| | TV | | WATCH | | BRIDGE | | |DRIVERKIT| |
| |   | | || MACCATALYST || |    | |       | |        | | |         | |
| |   | | |+-------------+| |    | |       | |        | | |         | |
| |   | | +---------------+ +----+ +-------+ +--------+ | |         | |
| +---+ +-----------------------------------------------+ +---------+ |
+---------------------------------------------------------------------+

This tells us:

  • TARGET_OS_MAC will be 1 for (probably) any Cocoa application running on an Apple platform.

    • TARGET_OS_OSX will only be 1 for macOS targets
    • TARGET_OS_IPHONE will be 1 for any non-Mac Apple products
      • TARGET_OS_IOS is just for iOS
        • TARGET_OS_MACCATALYST is just for Project Catalyst. It seems TARGET_OS_UIKITFORMAC will also work.
      • TARGET_OS_TV is just for tvOS
      • TARGET_OS_WATCH is just for watchOS
      • TARGET_OS_BRIDGE is just for bridgeOS (which currently doesn't even support 3rd-party apps so you'll likely always see that be 0)
    • TARGET_OS_DRIVERKIT will be 1 when building for DriverKit

⚠️ But wait! ⚠️

I got that from the iOS 14 (macOS 11, watchOS 7) SDK. If I look back into the iOS 13 (macOS 10.15, watchOS 6) SDK, I see this:

+----------------------------------------------------------------+
|                TARGET_OS_MAC                                   |
| +---+  +-----------------------------------------------------+ |
| |   |  |          TARGET_OS_IPHONE                           | |
| |OSX|  | +-----+ +----+ +-------+ +--------+ +-------------+ | |
| |   |  | | IOS | | TV | | WATCH | | BRIDGE | | MACCATALYST | | |
| |   |  | +-----+ +----+ +-------+ +--------+ +-------------+ | |
| +---+  +-----------------------------------------------------+ |
+----------------------------------------------------------------+

Notably, TARGET_OS_DRIVERKIT is new in 14, and TARGET_OS_MACCATALYST is inside IOS now. This tells me that compiling against the iOS 14 SDK can break some C code written for iOS 13, if it assumes that TARGET_OS_MACCATALYST and IOS are completely separate.


Additionally, these are defined:

  • TARGET_OS_SIMULATOR is just for iOS, tvOS, and watchOS simulators. You can further refine this using the above #defines
  • TARGET_OS_WIN32 is in case you wanna use Apple's SDKs to make Windows apps. I don't personally know of any other than Apple's own (like iTunes, Safari, and QuickTime). This might become useful now that Swift has Windows support, if you want to take existing Objective-C code with you.
  • TARGET_OS_UNIX is for non-Apple UNIX systems

And these are deprecated, and should not be used anymore. That said, you might find them in code you have to maintain, so here's what they meant:

  • TARGET_IPHONE_SIMULATOR used to mean the iPhoneOS simulator
  • TARGET_OS_EMBEDDED used to mean iOS, tvOS, and watchOS non-simulated devices
  • TARGET_OS_NANO probably used to mean iPod Nano (I can't find any historical usage online)

Something else to note is that the TargetConditionals.h used in swift-corelibs-foundation is significantly different, and includes #defines for Android, Cygwin, and other not-explicitly-supported-but-they-technically-work platforms.

I'm not entirely sure what to make of this. I would guess it's for compiling the Swift Foundation framework, and not for consuming it, since Swift doesn't consume #defines.


As of Xcode 8 GM, this is the relevant section of TargetConditionals.h in iOS 10.0:

#define TARGET_OS_MAC               1
#define TARGET_OS_WIN32             0
#define TARGET_OS_UNIX              0
#define TARGET_OS_OSX               0
#define TARGET_OS_IPHONE            1
#define TARGET_OS_IOS               1
#define TARGET_OS_WATCH             0
#define TARGET_OS_BRIDGE            0
#define TARGET_OS_TV                0
#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 */

(In the 'breadcrumb' above the text editor in Xcode, iOS 10.0 > usr/include > TargetConditionals.h)

...And this is the same file, for macOS:

#define TARGET_OS_MAC               1
#define TARGET_OS_WIN32             0
#define TARGET_OS_UNIX              0
#define TARGET_OS_OSX               1
#define TARGET_OS_IPHONE            0
#define TARGET_OS_IOS               0
#define TARGET_OS_WATCH             0
#define TARGET_OS_BRIDGE            0
#define TARGET_OS_TV                0
#define TARGET_OS_SIMULATOR         0
#define TARGET_OS_EMBEDDED          0 
#define TARGET_IPHONE_SIMULATOR     TARGET_OS_SIMULATOR /* deprecated */
#define TARGET_OS_NANO              TARGET_OS_WATCH /* deprecated */ 

(In the 'breadcrumb' above the text editor in Xcode, macOS 10.12 > usr/include > TargetConditionals.h)

TARGET_OS_MAC is defined as 1 on both platforms, but TARGET_OS_OSX is 1 only on macOS.

In the file's comments, they are described as follows:

  • TARGET_OS_MAC: Generated code will run under Mac OS X variant.
  • TARGET_OS_OSX: Generated code will run under OS X devices.

I guess at some point back then (perhaps around when the iPhone was announced?) somebody decided that iOS (né "iPhone OS") fits the definitions of "Mac OS X variant".


That is because TARGET_OS_MAC is defined when building for iOS as well.

See http://sealiesoftware.com/blog/archive/2010/8/16/TargetConditionalsh.html on that.

I would try and build my own target specific define via build-settings on the target.


If you're using Swift, there's a great language feature for this. If you're using Objective-C, it's often useful to do something like this:

#include <TargetConditionals.h>

#if TARGET_OS_IPHONE
    @import UIKit;
#else
    @import AppKit;
#endif

Understanding the TARGET_OS_* defines will make this make a lot more sense. Most notably, TARGET_OS_MAC is any Apple platform which is pretty unexpected. !TARGET_OS_IPHONE is macOS and TARGET_OS_IPHONE is anything besides that. There are more specific defines for iOS, tvOS, and watchOS.

From TargetConditions.h:

TARGET_OS_* 
These conditionals specify in which Operating System the generated code will
run.  Indention is used to show which conditionals are evolutionary subclasses.  

The MAC/WIN32/UNIX conditionals are mutually exclusive.
The IOS/TV/WATCH conditionals are mutually exclusive.


    TARGET_OS_WIN32           - Generated code will run under 32-bit Windows
    TARGET_OS_UNIX            - Generated code will run under some Unix (not OSX) 
    TARGET_OS_MAC             - Generated code will run under Mac OS X variant
       TARGET_OS_IPHONE          - Generated code for firmware, devices, or simulator 
          TARGET_OS_IOS             - Generated code will run under iOS 
          TARGET_OS_TV              - Generated code will run under Apple TV OS
          TARGET_OS_WATCH           - Generated code will run under Apple Watch OS
       TARGET_OS_SIMULATOR      - Generated code will run under a simulator
       TARGET_OS_EMBEDDED       - Generated code for firmware

    TARGET_IPHONE_SIMULATOR   - DEPRECATED: Same as TARGET_OS_SIMULATOR
    TARGET_OS_NANO            - DEPRECATED: Same as TARGET_OS_WATCH