Search code examples
objective-cswiftplatform

Is there something like os(iOS) in Objective-C?


Swift provides a special preprocessor directive called os to check what operating system you are running:

struct A {
#if os(iOS)
    let a: AvailableOnIOS
#elseif os(tvOS)
    let a: AvailableOnTVOS
#endif
}

I am interested if there is something similar in Objective-C that can be used like

@interface A: NSObject
#if os(iOS)
@property (nonatomic, strong) AvailableOnIOS* a;
#elseif os(tvOS)
@property (nonatomic, strong) AvailableOnTVOS* a;
#endif
@end

Solution

  • The answer to your question is:

    @interface A : NSObject
    #if TARGET_OS_IOS
        @property(nonatomic) AvailableOnIOS * a;
    #elsif TARGET_OS_TV
        @property(nonatomic) AvailableOnTVOS * a;
    #endif
    @end
    

    Do not use TARGET_OS_IPHONE as that includes all embedded systems. Just as TARGET_OS_MAC includes all Apple systems.

    To quote from Apple's TargetConditionals.h header:

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

    The reason why MAC covers everything is because all the other operating systems are in fact stripped down macOS systems, so MAC has become a synonym for "Apple Platform". The other platforms on this level are:

    • TARGET_OS_WIN32
    • TARGET_OS_WINDOWS
    • TARGET_OS_UNIX
    • TARGET_OS_LINUX
    • TARGET_OS_MAC

    And iPhone OS (today named iOS) was the first stripped down version of macOS, all other systems are modified version of iPhone OS, that's why the layer below TARGET_OS_MAC is split into:

    • TARGET_OS_OSX
    • TARGET_OS_IPHONE
    • TARGET_OS_SIMULATOR
    • TARGET_OS_DRIVERKIT

    And TARGET_OS_IPHONE has the following sublayers:

    • TARGET_OS_IOS
    • TARGET_OS_TV
    • TARGET_OS_WATCH
    • TARGET_OS_VISION
    • TARGET_OS_BRIDGE

    There is one final sublayer below TARGET_OS_IOS and that is TARGET_OS_MACCATALYST. "Mac Catalyst" is not truly a platform of its own but instead it is a subset of the iOS API extended with desktop API functionality. This added desktop functionality is how iPadOS differs from iOS on iPhones: They are both iOS but iPadOS has additional functionality that standard iOS on iPhone has not. The entire Catalyst API was backported to macOS, so apps that are limited to the Catalyst API can run on iPads and on Macs without further modification. Note that most normal iPhone apps can also run on Macs but only on ARM Macs, whereas Catalyst apps can also run on Intel Macs, as the Catalyst APIs are also available for x86_64 systems.

    Missing in this picture is TARGET_OS_SIMULATOR which is always set when code runs in a simulator; no matter what simulator. E.g. if you want a piece of code to only be used when you run in an iOS simulator but not in other simulators (tvOS, watchOS), then you would use

    #if TARGET_OS_SIMULATOR && TARGET_OS_IOS
    
    #endif
    

    Note that this code will not run on a real iOS device either.

    Also noteworthy are the defines for different target CPUs:

    • TARGET_CPU_PPC
    • TARGET_CPU_PPC64
    • TARGET_CPU_68K
    • TARGET_CPU_X86
    • TARGET_CPU_X86_64
    • TARGET_CPU_ARM
    • TARGET_CPU_ARM64
    • TARGET_CPU_MIPS
    • TARGET_CPU_SPARC
    • TARGET_CPU_ALPHA

    and the defines for the following properties:

    • TARGET_RT_LITTLE_ENDIAN: Generated code uses little endian format for integers
    • TARGET_RT_BIG_ENDIAN: Generated code uses big endian format for integers
    • TARGET_RT_64_BIT: Generated code uses 64-bit pointers
    • TARGET_RT_MAC_MACHO: TARGET_OS_MAC is true and Mach-O/dlyd runtime is used

    All other target conditionals are deprecated or pretty much useless in practice.