16

Swift 2 have API availability checking.

The compiler will give you an error when using an API too new for your minimum target OS

Why can't the objective-c compiler do the equivalent?

I googled objective c API availability checking and only swift 2 results came out so I assume the compiler for objective c can't do that.

oky_sabeni
  • 7,672
  • 15
  • 65
  • 89
  • Swift was designed with this feature in mind, so Apple could add the feature without creating incompatibilities or hampering the usual abilities of the language. Objective-C is very dynamic, and it's difficult to tell at compile time what will or will not be available at runtime. – Avi Dec 01 '15 at 05:34
  • Take a look at https://developer.apple.com/library/ios/documentation/DeveloperTools/Conceptual/cross_development/Configuring/configuring.html You can essentially achieve the same thing by fiddling with your Base SDK and deployment targets but it's more cumbersome. – pvg Dec 01 '15 at 05:40
  • 1
    you can use `respondsToSelector:@selector(methodName)` to check its availability, as there is no other dedicated mechanism like swift. – Dipen Panchasara Dec 01 '15 at 05:42
  • 1
    Please read the [SDK Compatibility Guide](https://developer.apple.com/library/ios/documentation/DeveloperTools/Conceptual/cross_development/Introduction/Introduction.html#//apple_ref/doc/uid/10000163i). – rmaddy Dec 01 '15 at 05:48
  • A method or class not available at a SDK version has *nothing* to do with dynamic dispatch. Of course the Objective-C compiler warns you about the absence of such a declaration. In contrast to Swift you can react on such an error at compile time *or* at runtime. In contrast to Swift, Objective-C does not need any special feature for it. One might think that this is less complexity. One might think this is a sign of strength of language. – Amin Negm-Awad Dec 01 '15 at 06:03

5 Answers5

49

Xcode 9.0 brings the runtime availability checking syntax from Swift to Objective-C:

if (@available(macOS 10.9, *))
{
// call 10.9+ API
}
else
{
// fallback code
}

this comes complete with warnings for calling APIs that are newer than your deployment target (if those calls are not wrapped in checks).

finally ;)

user1259710
  • 868
  • 2
  • 9
  • 12
21

The warning (Swift makes it an error) just hadn't been implemented in the Clang compiler for years, but it's not an inherent Objective-C limitation (although due to its dynamic nature, you won't be able to catch all cases), nor Swift terminology.

The Apple macros (e.g., NS_CLASS_AVAILABLE) and source attributes (__attribute__((visibility(...))), __attribute__((availability(...)))) to annotate headers with availability information have been there for years, and they are widely-used in Apple's SDKs. The macros are defined in Foundation's NSObjCRuntime.h, and the Availability.h/AvailabilityMacros.h system headers, and the compiler can (and does) read them.

In early 2015, the -Wpartial-availability warning has been added to Clang's master branch, but this commit/warning hadn't made its way into Apple's version of Clang until (including) Xcode 7.2. You will get an unknown warning option log when adding the warning flag to a project in Xcode 7.2, but the flag is available in Xcode 7.3. There's currently no predefined setting for it, but you can add the flag to Other Warning Flags under Build Settings.

There are other tools that use LLVM libraries to detect partially available APIs, e.g., Deploymate. For my diploma thesis, I developed a tool that integrates directly into Xcode and is based on a modification to the Clang compiler. The code is still online, but I haven't kept up with the general Clang development so it won't be of much use, except for learning purposes. However, the "official" code (linked above) is much cleaner and better.

Edit: Starting with Xcode 9, availability checking will work for Objective-C (and C), too. Instead of using the above-mentioned warning flag, which does not support raising the deployment target temporarily/locally and therefore causes plenty of false positives, there's -Wunguarded-availability, and if (@available(iOS 11, *)) {...} to check and raise the deployment target for the following block of code. It is off by default, but -Wunguarded-availability-new will be on by default, and starts checking anything beyond iOS/tvOS 11, watchOS 4, and High Sierra. More details on that can be found in the Xcode 9 beta release notes, which currently requires signing in with a developer account.

hagi
  • 11,503
  • 3
  • 35
  • 48
  • Thanks for linking deploymate and your thesis. It looks interesting. – oky_sabeni Dec 02 '15 at 16:58
  • @hagi. Thanks for the point about the partial availability flag. I was able connect latest clang with Xcode 7.2 and get the warnings. https://gist.github.com/vigneshr89/befe6a99e7979772e449 – Vignesh Feb 08 '16 at 20:34
  • 1
    The Xcode 7.3 betas seem to come with a version of Clang that supports the flag out of the box :) – hagi Feb 09 '16 at 09:48
3

Objective C does not have availability checking as part of the language, as the same result is available via Objective C preprocessor. That is the "traditional" way of doing that in C derived languages.

Want to know if compiled in debug mode?

#ifdef DEBUG
  // code which will be inserted only if compiled in debug mode
#endif 

Want to check at compile time for a minimum version? Use the Availability.h header in iOS, and similar headers for Mac OS X.

This file reside in the /usr/include directory.

just test __IPHONE_OS_VERSION_MAX_ALLOWED with the preprocessor, e.g.:

#if __IPHONE_OS_VERSION_MAX_ALLOWED >= 80000
            if ([application respondsToSelector:@selector(registerUserNotificationSettings:)]) {
                [[UIApplication sharedApplication] registerUserNotificationSettings:[UIUserNotificationSettings settingsForTypes:(UIUserNotificationTypeBadge|UIUserNotificationTypeSound|UIUserNotificationTypeAlert) categories:nil]];
            }else{
                [[UIApplication sharedApplication] registerForRemoteNotificationTypes: (UIUserNotificationTypeBadge|UIUserNotificationTypeSound|UIUserNotificationTypeAlert)];
            }
#else
            [[UIApplication sharedApplication] registerUserNotificationSettings: (UIUserNotificationTypeBadge|UIUserNotificationTypeSound|UIUserNotificationTypeAlert)];
#endif

As Swift does not have a preprocessor, they had to invent a way of doing these kind of checks within the language itself.

If you want to check availability of a method at runtime, please notice that the appropriate way is by using the method respondsToSelector:, or instancesRespondToSelector: (the latter at class level).

You will normally want to combine both approaches, compile time conditional compilation and runtime check.

Objective C method presence verification, e.g. at class level:

if ([UIImagePickerController instancesRespondToSelector:
              @selector (availableCaptureModesForCameraDevice:)]) {
    // Method is available for use.
    // Your code can check if video capture is available and,
    // if it is, offer that option.
} else {
    // Method is not available.
    // Alternate code to use only still image capture.
}

If you want to test if a C function exists at runtime, it is even simpler: if it exists, the function itself it is not null.

You can't use the same identical approach in both languages.

Michele Giuseppe Fadda
  • 1,302
  • 1
  • 17
  • 26
1

It does these days. Furthermore, with Xcode 11 (including the current Xcode 11.3.1), you can even get it from the snippets. Press the + button towards the top right of Xcode (as shown in the image below). enter image description here

Then in the search box, type "API". All 3 versions of the snippet for API Availability Check will appear -- Objective C, C and Swift. enter image description here

auspicious99
  • 3,902
  • 1
  • 44
  • 58
-3

Of course, you will get errors in Objective-C code. But you won't find results in google for Objective-C, if you use a term defined for Swift as you will not find kisuaheli website in google if you search for a german word. ;-)

You will get an error linking Objective-C code against a too old SDK. This is simply because the used method or class or $whatever is not defined in the header for that SDK. Again, of course.

This is typical Swift marketing of Apple: Because of the incapability of Swift they have to extend the language to get something, which is quite easy in Objective-C. Instead of clarifying that this is the result of the poorness of Swift, they tell you that this is a great feature of Swift. It is like cutting your fingers and then saying: "We have the great plaster feature!!!!!!!!" And you have to wait only some days and one comes around on SO with the Q: "Why does Objective-C does not have the great plaster feature???????" The simple answer: It does not cut your fingers.

The problem is not to generate the errors. The problem is to have one source code for all versions, so you can simply change the SDK version and get new code (or errors). You need that for easier maintenance.

In Objective-C you simply can use the answer found here: Conditionally Hide Code from the Compiler or you can do that at runtime as mentioned in the comments to the Q. (But this is a different solution of the problem by nature, because it a dynamic approach instead of a static one as you have to do in Swift.)

The reason for having a language feature in Swift is that Swift has no preprocessor, so the Objective-C solution would not work in Swift. Therefore conditional code would be impossible in Swift and they had to add the plaster, eh, language feature.

Community
  • 1
  • 1
Amin Negm-Awad
  • 16,582
  • 3
  • 35
  • 50