136

I have a piece of code which is generating lots of warnings (deprecated API)

Using clang* I could do:

#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wdeprecated-declarations"
    ...
#pragma clang diagnostic pop

However this does not work in Swift.

How to do it in Swift?

Note: I don't want to disable the warning globally, nor even file wide, but just disable a specific warning in a specific part of my source code.

I do not want conditional compilation (which is the proposed answer of the supposed duplicate). I just want to silence a warning WITHOUT using the new APIs.

pkamb
  • 33,281
  • 23
  • 160
  • 191
Antzi
  • 12,831
  • 7
  • 48
  • 74
  • possible duplicate of [Swift alternative for #pragma clang diagnostic](http://stackoverflow.com/questions/28357297/swift-alternative-for-pragma-clang-diagnostic) – zrzka Jul 21 '15 at 19:28
  • 6
    This is not a duplicate. The other question fails to answer this problem. – Claus Jørgensen Jul 25 '15 at 10:09
  • @ClausJørgensen in which way it fails to answer this problem? There's no other way as stated in answers in linked question. Just conditional compilation or new `#available` macro where developer should use new methods and fallback to the old ones if new ones are not available. – zrzka Jul 27 '15 at 19:53
  • @robertvojta No, as the answers does, in fact, *not* state that there's no other ways to silence a warning. – Claus Jørgensen Jul 27 '15 at 19:54
  • @ClausJørgensen it says - no simple/complex macros, no preprocessor and in comments _"The Swift compiler does not include a preprocessor. Instead, it takes advantage of compile-time attributes, build configurations, and language features to accomplish the same functionality. For this reason, preprocessor directives are not imported in Swift." It does not look like Swift will ever (?) support clang diagnostic macros and it currently has no native alternative built in._ What else do you want? There's no alternative for clang diagnostics macros. – zrzka Jul 27 '15 at 20:01
  • @robertvojta As SO answers have to answer to the test of time, such a answer is simply not valid. For once it's given during Swift 1.x timeline, and secondly, it doesn't provide any official documentation to support those claims. A question should be left unanswered, if no answer can be given. – Claus Jørgensen Jul 27 '15 at 20:04
  • @robertvojta Also, the suggested duplicate asks about a specific area, which *do* have a Swift replacement. Whereas this question asks about something that doesn't, currently, have a Swift replacement. Assuming Apple isn't daft, we'll have a replacement for clang diagnostics later this year. – Claus Jørgensen Jul 27 '15 at 20:06
  • @ClausJørgensen both questions are about `#pragma clang diagnostic ignored "-Wdeprecated-declarations"` - silence warning when using deprecated methods. Question and answer was updated to reflect Swift 2.0 changes and in the question there's lot of excerpts from Apple documentation supporting all claims. But whatever, I consider it as a dupe, you not, I assume we have more important work to do then _fighting_ about dupes :-) – zrzka Jul 27 '15 at 20:14
  • 3
    This isn't a dupe. What about a situation where you're given a warning for missing an initialiser? – NSTJ Sep 30 '15 at 01:35
  • Discussion about silencing warnings by a lot of awesome people: https://forums.swift.org/t/swift-should-allow-for-suppression-of-warnings-especially-those-that-come-from-objective-c/19216 – Klaas Oct 19 '19 at 13:45

5 Answers5

233

As of 2022, Xcode 14.x, Swift 5.x, the consensus is that there is no direct way to achieve that.

I'll update/edit this answer if Apple adds the feature.

Put it in your wish list for WWDC 2023!

Cœur
  • 37,241
  • 25
  • 195
  • 267
Antzi
  • 12,831
  • 7
  • 48
  • 74
86

There is no general construct to silence deprecation warnings in Swift, but there is a workaround that can be applied in many cases.

Let's say you have a method getLatestImage() on class Foo which uses deprecated methods/classes.

Use @available as Daniel Thorpe described to silence all the warnings inside the method:

class Foo {
    @available(iOS, deprecated: 9.0)
    func getLatestImage() -> UIImage? {
        ...
    }
}

Now you would like to call the method getLatestImage() without having a deprecation warning. You can achieve that by first defining a protocol and an extension:

private protocol GetLatestImage {
    func getLatestImage() -> UIImage?
}
extension Foo: GetLatestImage {}

And then call the method without a deprecation warning.

If foo is an instance of Foo:

(foo as GetLatestImage).getLatestImage() // no deprecation warning

If you want to call a static property/function of Foo:

(Foo.self as GetLatestImage.Type).someStaticProperty

The result is you have Swift code that uses deprecated API without any deprecation warnings.

What about global functions? We can also work around those. Imagine we have a module X containing the function foo which is declared @available(iOS, deprecated: 13.0) func foo() -> Int.

Here, we can work around with a protocol and an enum:

protocol Undeprecated {
    static func foo() -> Int
}
enum Deprecated: Undeprecated {
    @available(iOS, deprecated: 13.0)
    static func foo() -> Int { X.foo() }
}

Calling foo() without deprecation warning is now possible using (Deprecated.self as Undeprecated.Type).foo().

Tammo Freese
  • 10,514
  • 2
  • 35
  • 44
  • 3
    So clever. Kind of evil? :) But so good. Great for a use case like suppressing warnings over ongoing use of some aspects of the AddressBook framework that have been deprecated, but have a replacement doesn't actually provide all of the required functionality yet. Thanks. – Duncan Babbage Sep 16 '17 at 23:19
  • 6
    if this works, i will send you a six pack of your favorite drink. you have an outstanding mind sir, thank you. – John Apr 25 '18 at 02:38
  • @John Thanks for the kind words! It does work, I had to come up with it as we treat warnings as errors in our codebase, and there is one section still using a deprecated library. – Tammo Freese May 13 '18 at 12:47
  • 1
    @John did you send him the six pack? :P This is awesome. Genius. Thanks. – Baran Sep 12 '18 at 10:27
  • I believe this relies on the Objective-C runtime, so this'll only work for classes of that sort. Just a thing to keep in mind when trying to use this workaround. – saagarjha Dec 04 '19 at 08:25
  • It appears I had already tried this by annotating a class `@available(*, unavailable)` which unintuitively did *not* remove any warnings. Replacing it with `@available(iOS, deprecated: 9.0)` did. – Andreas Dec 06 '20 at 05:16
  • Nice to see you here @saagarjha. Keep up the good work with IINA. – Andreas Dec 06 '20 at 05:19
  • I wondered why this trick works. Looks like compiler doesn't see that you use a deprecated method, because you actually use a protocol method. – Denis Kutlubaev Feb 20 '22 at 12:00
  • 1
    Unfortunately with Xcode 14 this workaround does not work anymore. I get a warning like "Conformance of 'DeprecationClass' to 'DeprecationProtocol' is deprecated" – thetrutz Oct 12 '22 at 13:22
  • @thetrutz Interesting, I could not convince Xcode to show me that warning. I tried with `struct Foo { @available(iOS, deprecated: 9.0) func foo() { } } protocol FooProtocol { func foo() } extension Foo: FooProtocol {} func bar() { (Foo() as FooProtocol).foo() } ` – Tammo Freese Oct 13 '22 at 14:29
  • How do I make that work for a global function such as CommonCrypto's `MD5()`? – Thomas Tempelmann May 24 '23 at 19:03
  • 1
    @ThomasTempelmann I will adapt the answer accordingly! – Tammo Freese May 26 '23 at 14:33
43

Actually, you can suppress these warnings by using @available in the enclosing logical structure (i.e. function/type).

For example, say you have some code which uses the AddressBook framework, but you're building against iOS 9.

@available(iOS, deprecated: 9.0)
func addressBookStatus() -> ABAuthorizationStatus {
    return ABAddressBookGetAuthorizationStatus()
}

As of Xcode 7.0.1 this will prevent the inline warnings from being displayed.

Eric Aya
  • 69,473
  • 35
  • 181
  • 253
Daniel Thorpe
  • 3,911
  • 2
  • 28
  • 28
  • 11
    Yes, but you will see the same warning when you call your `addressBookStatus()`... that you mark as deprecated. – Valentin Shergin Oct 18 '15 at 01:28
  • 4
    Pro tip: if you want to silence it for an entire class just slam this puppy up above your class statement (ex: `class ViewController: UIViewController`) – Allison May 30 '16 at 17:35
  • 2
    @Sirens Then you'll see this warning every time you call this class ☹️ (at least with Xcode 8) – Alexander Vasenin Oct 03 '16 at 03:07
  • Does anyone succeeded in silencing *all* deprecated warnings with this fix? I was able to decrease their number to just *one*, but I don't see a way to get rid of the last one. Any suggestions? – Alexander Vasenin Oct 04 '16 at 08:22
  • 1
    So how do use this to silence the warning _“cast from 'CGFloat.NativeType' (aka 'Double') to unrelated type 'Float' always fails”_ when I'm doing an `if CGFloat(0).native is Float { … }`?  Answer: I don't use this because you didn't answer the question. – Slipp D. Thompson Mar 27 '17 at 00:23
  • Doesn't seem to work if you add the annotation to an extension of a deprecated class. – bompf Oct 11 '18 at 14:11
  • This doesn't seem to include use of Swift in other applications. E.g., Server-side Swift. – Chris Prince Oct 04 '19 at 21:04
  • What exactly does `@available` do? I'm building a Mac Catalyst + iOS + iPadOS app, and the deprecation warning for my function only applies to Mac Catalyst. If I add `@available(macCatalyst, deprecated: 13.0)` to my function, will that mess with its execution on other OSs? – Matt Apr 03 '20 at 05:03
9

I was having the issue for a top level function outside of a class or struct:

@available(*, deprecated)
func GetImage(url: URL) -> UIImage? { ... }

I've talked to an engineer at Apple and they told me that you can hide the implementation by a protocol and mark extensions as deprecated. Let's see how it works:

  1. Create a protocol that has a similar signature for the function you want to wrap.
  2. Use the protocol on any class or struct in an extension.
  3. Mark the extension as deprecated.

Everything that's within the extension does not bring up any deprecation warnings.

protocol ImageStoreProtocol {
    func imageFromURL(_ url: URL) -> UIImage?
}

class ImageStore {}

@available(*, deprecated)
extension ImageStore: ImageStoreProtocol {
    func imageFromURL(_ url: URL) -> UIImage? {
        return GetImage(url: url) // Warning does't show up
    }
}
Lukas Würzburger
  • 6,543
  • 7
  • 41
  • 75
2

While there’s no way to silence deprecation warnings in Swift for now, technically you can do that for a particular symbol by editing the header file.

  • Copy the deprecated symbol name
  • Select File > Open Quickly
  • Paste the symbol and press Enter

    Make sure the Swift icon is disabled in the Open Quickly box

  • Select File > Show in Finder

  • Change file permissions to allow editing if necessary
  • Edit the deprecation macros for the symbol. See surrounding APIs for reference. E.g. replace:

__OSX_AVAILABLE_BUT_DEPRECATED(__MAC_10_6, __MAC_10_10, __IPHONE_3_0, __IPHONE_8_0)

with

__OSX_AVAILABLE_STARTING(__MAC_10_6, __IPHONE_3_0)

Now there’s one less distracting warning you can do nothing about.

I know, it’s dirty. But if there’s no replacement API available in the current SDK, it should be safe. Once a new version of Xcode comes out, the change will get overwritten and you will see the warning again. Then you can test the new SDK and OS to make sure the deprecated API is still available and did not get a replacement.

Please comment if you can come up with any downsides.

pointum
  • 2,987
  • 24
  • 31
  • 2
    Upvoting for the resourcefulness, but it would leave a dirty taste in my mouth :P – Matt Apr 03 '20 at 05:05