Xcode/iOS -- get rid of deprecation warnings for specific constants? - objective-c

I have some deprecated constants in my project. They need to stay. I don't want to be warned about them, but do want to be warned if other deprecated constants should turn up in my project later.
Apple's header declares them as follows:
extern NSString * const NameOfStringConstant __OSX_AVAILABLE_BUT_DEPRECATED(version availability info here)
How can I silence the warning?
Related answer for silencing the warning for a deprecated method here
Related answer for silencing the warning about a deprecated string conversion here

I know this is an old topic but today i was dealing with the same annoyance.
Example: you want to get rid of the annoying deprecation warning but just for [[UIDevice currentDevice] uniqueIdentifier]] since you most probably want to use it in development phase with TestFlight.
You'd still want the compiler to warn you if you use some other deprecated declaration by mistake.
I like sarfata's answer: it does the job. But there's more politically correct way available:
Following recipe is taken from The Goo Software Blog.
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wdeprecated-declarations"
[TestFlight setDeviceIdentifier:[[UIDevice currentDevice] uniqueIdentifier]];
#pragma clang diagnostic pop
Make sure you comment out this lines before building for distribution. Or simply use a preprocessor macro to exclude this lines from a release build.

Add to the compiler flags:
-Wno-deprecated-declarations
or, in Xcode, select "No" for the build setting option:
Warn About Deprecated Functions
and then if you look at the build output (Apple+7 in Xcode 4), you'll notice the aforementioned compiler flag.

The proper answer to this question is to not use deprecated constants. Check the documentation for the recommended way to accomplish something now. On the deprecated methods/constants/whatever, there's almost always a link to the "replacement" if you will. Use that instead. This way your code doesn't mysteriously break when those disappear forever, but your users still have a build built against the old sdk, and now their code crashes, or worse, does weird things.

This is #1 answer in google and I believe there are some faire case when using a deprecated method is useful and when you want to avoid warnings to keep the build "clean". This solutions is inspired by: http://vgable.com/blog/2009/06/15/ignoring-just-one-deprecated-warning/
The idea is to declare a new protocol that has the same method (but not deprecated of course) and to cast the object to that protocol. This way you can call the method without getting the warning and without getting rid of all the deprecation warnings.
Exemple: If you want to integrate TestFlight in your application, the SDK documentation suggests transmitting the uniqueIdentifier of the device while in BETA. This can help track which tester had problems. This method is deprecated by Apple (and they will not let you submit the app) but I believe this is a good example of using a deprecated method.
In your App Delegate:
/* This is to avoid a warning when calling uniqueIdentifier for TestFlight */
#protocol UIDeviceHack <NSObject>
- (NSString*) uniqueIdentifier;
#end
#implementation MyAppDelegate
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
[TestFlight takeOff:#"Your-Testflight-team-id"];
// TODO: Remove this in production (forbidden APIs) - Used here to improve beta reporting.
[TestFlight setDeviceIdentifier:[(id<UIDeviceHack>)[UIDevice currentDevice] uniqueIdentifier]];
// ...
}

Related

Why does objective-c not have API availability checking?

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.
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 ;)
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.
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.
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).
Then in the search box, type "API". All 3 versions of the snippet for API Availability Check will appear -- Objective C, C and Swift.
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.

__attribute__ ((deprecated)) does not work on objective-c protocol methods?

I need to deprecate a single method in objective-c protocol. On normal class/instance methods I add __attribute__ ((deprecated)); after declaration.
It seems, that it does not work on protocol methods. If I mark them deprecated and use them somewhere the project compiles OK, without expected deprecation warning.
Is it a flaw in Apple LLVM 3.1, or am I doing something wrong?
Although the answers here provide some very good information, they are outdated. Starting with Xcode 5.0 and LLVM 5.0 it looks like deprecation warnings for Objective-C protocol methods are recognized. When implementing the method, Xcode 5 flags it:
Warning: Implementing deprecated method
Here are the steps I used to produce a deprecation warning for the implementation of a deprecated protocol method:
Mark the protocol method as deprecated using __deprecated. From the new SDK 7.0 documentation:
__deprecated causes the compiler to produce a warning when encountering code using the deprecated functionality. __deprecated_msg() does the same, and compilers that support it will print a message along with the deprecation warning. This may require turning on such warning with the -Wdeprecated flag. __deprecated_enum_msg() should be used on enums, and compilers that support it will print the deprecation warning.
#define __deprecated __attribute__((deprecated))
To deprecate your method, do something like this:
- (void)aDeprecatedProtocolMethod __deprecated;
This alone should be enough for Xcode to display a deprecation warning. However, you should follow the next few steps (knowing that Xcode can be very finicky at times) to ensure the warning displays.
Add a documentation comment with a deprecation warning tag. See the code example below to learn how:
/** Describe the method here - what does it do, how does it work, etc. Very brief.
#deprecated This delegate method is deprecated starting in version 2.0, please use otherMethodNameHere:withAnExtraParameter: instead. */
- (void)aDeprecatedProtocolMethod __deprecated;
Clean the project (⌘+⇧+K) and then Build the project (⌘+B) - just because Xcode can be funky sometimes.
I'm not 100% sure when or where this feature was introduced (maybe with SDK 7.0 and 10.9, or Xcode 5.0 / 5.0.1, or with LLVM 5.0) - but it works nonetheless.
Well, I just realised, that even Apple use __attribute__((deprecated)) at the end. And it does not work either. If I use any deprecated delegate method, e.g.
- (UITableViewCellAccessoryType)tableView:(UITableView *)tableView
accessoryTypeForRowWithIndexPath:(NSIndexPath *)indexPath
there is no warning.
So it seems like a candidate for radar.
EDIT: filed a radar, Bug ID# 11849771.
Apple deprecated some methods in the UITableViewDelegate protocol, perhaps you'll be able to find the solution using Apple's code as example.
The relevant code of the protocol is as follows:
- (UITableViewCellAccessoryType)tableView:(UITableView *)tableView
accessoryTypeForRowWithIndexPath:(NSIndexPath *)indexPath
__OSX_AVAILABLE_BUT_DEPRECATED(__MAC_NA,__MAC_NA,__IPHONE_2_0,__IPHONE_3_0);
As you can see, Apple uses a macro. Perhaps this is the way to go?
EDIT: As noted on the following link [1] __attribute__((deprecated)) is a GCC construct so this might not work in LLVM. I guess this is the reason Apple uses macros, so some other (or no) deprecation construct will be called when other compilers are used.
[1] How to deprecate a method in Xcode

Private Methods in Objective-C, in Xcode 4.3 I no longer need to declare them in my implementation file ?

I have a lot question marks tolling above my head.
What I don't get is before xcode 4.3 I needed to declare forward declarations (for private methods) in my implementation file.
Like in my .m file:
// deleting this with xcode 4.3 the below code still does work
// in previous versions i had to put this because otherwise the compiler can't find methodFirst
#interface DetailViewController ()
- (void)methodFirst;
- (void)methodSecond;
#end
#implementation DetailViewController
- (void) methodSecond
{
// if i delete the forward declaration now adays i dont get a compiler error that he cant find method first
[self methodFirst];
}
- (void) methodFirst
{
}
#end
Now it seems I don't need to do that anymore? Did Apple update the compiler so that it isn't needed anymore to put forward declarations?
I can't find any reference to an official Apple source about this change. I wonder what other people have encountered in their new environment.
As of the LLVM Compiler version shipped with Xcode 4.3, if you try to call a method that the compiler has not previously seen, it will look in the rest of the current #implementation block to see if that method has been declared later. If so, then it uses that, and you don't get a warning. Hence, as of Xcode 4.3, there's much less need to pre-declare your internal methods. Clearly, you still need to declare methods that are publicly exposed to other classes.
This change was noted in the release notes of some of the Xcode 4.3 betas, but apparently didn't make it into the "What's New in Xcode 4.3" final documentation.
Unlike has been suggested in other answers, this is not just an "Undeclared Selector" warning that has been turned off by default. In fact, if you're using ARC, unrecognized selectors are still hard errors. Try calling [self myNonexistentMethod] and you'll see; the compiler still complains.
There aren't any private methods in Objective-C. What you're thinking of is the class continuation, the "nameless" category interface you can declare in your .m file to declare methods that will be in the class implementation, but that aren't in the public interface.
There's never been a requirement to declare methods before they're used. However, it's always been a good idea, and the compiler has a warning flag to indicate when methods that haven't been seen are used. The reason is to do with the operating system's calling convention for functions. Different types, such as structures, floating point numbers, integer numbers, and pointers, can all be handled in different ways when they are the arguments to or return values from functions. Indeed, on different computers and in different operating systems, they are handled in different ways. To know how to handle the arguments and return values for an Objective-C method, the compiler needs to know the signature for that method: how many arguments of what types it takes, and what type it returns.
If it hasn't seen a declaration of the method, then the compiler will need to make a guess. If that guess is incorrect, then it can end up putting the wrong values into the arguments, or interpreting the return value incorrectly, or trying to take something off the stack that doesn't exist.

XCode warns for unreleased objects

Is there a way to make XCode warn for unreleased objects?
It may be useful for classes with lots of objects and I forgot to put [obj release]; in the dealloc method.
You can use the Analyze tool ("Build and Analyze" in Xcode 3), which does static analysis and points out where you have unreleased objects. More detail here: Build and Analyze: running the analyzer within Xcode
It's not a compiler warning because it's valid Objective-C (though bad Objective-C), but if you use the static analyzer instead of the compiler, it can often detect cases where an object is leaked. (You still have to use the compiler to run your app, of course.)
I would recommend that you change your build settings to let it run everytime you compile your software.
How to turn it on:
Select your project - Build Settings - Build Options - Run static Analyzer [YES]

Suppressing "'…' is deprecated" when using respondsToSelector

I'm supporting 10.4+ by picking the most-current API at runtime:
if ([fileManager respondsToSelector:#selector(removeItemAtPath:error:)])
[fileManager removeItemAtPath:downloadDir error:NULL];
else
[fileManager removeFileAtPath:downloadDir handler:nil];
In this case, 10.5 and up will use removeItemAtPath:error: and 10.4 will use removeFileAtPath:handler:. Great, but I still get compiler warnings for the old methods:
warning: 'removeFileAtPath:handler:' is deprecated [-Wdeprecated-declarations]
Is there a syntax of if([… respondsToSelector:#selector(…)]){ … } else { … } that hints the compiler (Clang) to not warn on that line?
If not, is there a way to tag that line to be ignored for -Wdeprecated-declarations?
After seeing some of the answers, let me clarify that confusing the compiler into not knowing what I'm doing is not a valid solution.
I found an example in the Clang Compiler User's Manual that lets me ignore the warning:
if ([fileManager respondsToSelector:#selector(removeItemAtPath:error:)]) {
[fileManager removeItemAtPath:downloadDir error:NULL];
} else {
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wdeprecated-declarations"
[fileManager removeFileAtPath:downloadDir handler:nil];
#pragma clang diagnostic pop
}
You could declare a separate file that is designated for calling deprecated methods and set the per-file compiler flags in Xcode to ignore -Wdeprecated-declarations. You can then define a dummy function in that file to call the deprecated methods, and thereby avoid the warnings in your real source files.
I'm not sure if clang is smart enough to catch this, but if it's not, you could try using performSelector:withObject:withObject: or building and invoking an NSInvocation object.
You could just cast fileManager to an id — ids are able to refer to any Objective-C object, so the compiler isn't supposed to check methods which are called on one:
[(id)fileManager removeItemAtPath:downloadDir error:NULL];
shouldn't raise any warnings or errors.
Of course, this raises other problems — namely, you lose all compile-time checking for methods called on the id. So if you misspell you method name, etc, it wont be caught until that line of code is executed.
If you consider any form of "confusing" the compiler to be an invalid solution, you're probably going to have to live with the warning. (In my book, if you asking how to get rid of a warning, it's unwise to look a gift horse in the mouth and say something is invalid just because it doesn't look like you'd expect.)
The answers that work at runtime involve masking the operation that's happening with dynamic dispatch so the compiler doesn't complain about the deprecated call. If you don't like that approach, you can turn off "Warn About Deprecated Functions" in your Xcode project or target settings, but that's generally a bad idea. You want to know about deprecated APIs, but in this case you want to use it without warning. There are easy and hard ways to do this, and odds are you'd consider all of them "invalid" in some form, but that doesn't prevent them from being effective, even correct. ;-)
One possible way to avoid the warnings yet still select at runtime is to use objc_msgSend() directly:
objc_msgSend(fileManager, #selector(removeFileAtPath:error:), downloadDir, nil];
This is what the Objective-C runtime does under the covers anyway, and should accomplish the result you want with a minimum of fuss. You can even leave the original line commented above it for clarity. I know the documentation says, "The compiler generates calls to the messaging function. You should never call it directly in the code you write." You alone have to decide when it's okay to bend the rules.