Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Preprocessor macro for OS X targets?

I have a project that has both an iOS and an OS X target. Is there a preprocessor macro that is true when I'm compiling for OS X? I tried this:

#if TARGET_OS_MAC
@interface BFNode : NSObject <NSPasteboardReading, NSPasteboardWriting> {
#else
@interface BFNode : NSObject {
#endif

But TARGET_OS_MAC doesn't seem to work. When I try to run the app on iOS I get a compiler error, because it tries to compile the first line and there is no NSPasteboardReading protocol defined on iOS.

I know there is TARGET_OS_IPHONE too. If I use that and swap the @interface declarations it works.

But there are a lot of places where I have code, that has no iOS version, so I need a macro for OS X too.

SOLUTION:

I ended up defining a new macro in the .pch file:

#define TARGET_OSX TARGET_OS_IPHONE == 0
like image 375
DrummerB Avatar asked Aug 26 '12 18:08

DrummerB


3 Answers

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
like image 26
Sam Soffes Avatar answered Nov 19 '22 18:11

Sam Soffes


The documentation in TargetConditionals.h has this diagram (it seems, as of 2022; 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 us that compiling against the iOS 14 / macOS 11 SDK can break some C code written for iOS 13 / macOS 10.15, if it assumes that TARGET_OS_MACCATALYST and TARGET_OS_IOS are completely separate.


There's More!

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. Use TARGET_OS_SIMULATOR instead (along with TARGET_OS_IOS to target only iOS simulators)
  • TARGET_OS_EMBEDDED used to mean iOS, tvOS, and watchOS non-simulated devices. Use the standard OS targets instead.
  • TARGET_OS_NANO probably used to mean iPod Nano (I can't find any historical usage online). Apple advises to use TARGET_OS_WATCH instead.

Something else to note is that the TargetConditionals.h which is 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.

like image 66
Ky. Avatar answered Nov 19 '22 18:11

Ky.


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.

like image 8
Till Avatar answered Nov 19 '22 17:11

Till