7

If your code requires a feature that is only available in macOS 10.12 or newer, but you want your code to also deploy to earlier system versions, you can use @available in Objective-C:

if (@available(macOS 10.12, *)) {
    // Code that requires 10.12 APIs
} else {
    // Code that doesn't require 10.12 APIs
}

You can do the same thing in Swift using #available:

if #available(macOS 10.12, *) {
    // Code that requires 10.12 APIs
} else {
    // Code that doesn't require 10.12 APIs
}

But what can you use if you write C or C++ code?

Mecki
  • 125,244
  • 33
  • 244
  • 253

1 Answers1

12

In C and C++ you can use the Clang compiler extension __builtin_available:

if (__builtin_available(macOS 10.12, *)) {
    // Code that requires 10.12 APIs
} else {
    // Code that doesn't require 10.12 APIs
}

That is if you have at least Clang 5 available (Xcode 5.0 or newer).

Keep in mind that you will have to set a deployment target to make this feature work correctly (e.g. -mmacosx-version-min=10.9). The reason is that the linker requires this information to decide when to perform weak linking. Setting the deployment target to 10.9 for example tells the linker that if your code is using any symbols that were not already available in 10.9, these symbols must be weakly linked.

Normally an executable or library will try to resolve all referenced symbols at load and fail to load if any such symbol cannot be found. However, when a symbol is weakly linked, being unable to resolve a symbol will not cause loading to fail, instead any reference to that symbol becomes a NULL reference at runtime. Needless to say, if you try to call a function that was weakly linked and has not been found at load, your app will crash (you are trying to call NULL as a function). And that's where __builtin_available comes to the rescue.

if (__builtin_available(macOS 10.12, *)) {
    // ...
} else {
    // Symbols only available in 10.12 or newer are all NULL when
    // you reach this code block but you wouldn't use any of them
    // in this code block, would you? So, no problem.
}

If you don't use -mmacosx-version-min, the __builtin_available construct will still work correctly, yet as the linker doesn't know what system you are targeting, it assumes the current system and won't perform any weak linking. If you then try to run your app on an earlier system version, some symbols won't be found and your app even refuses to load, despite the fact that it would have alternative code paths for that system.

Mecki
  • 125,244
  • 33
  • 244
  • 253
  • How is this implemented? I need to use this in non-C code or with a different C compiler. Is there a runtime function outside the ObjC libs for it? – Thomas Tempelmann Dec 19 '22 at 11:28