Big picture, I'd love to see the issue of 'necessary' uses of deprecated APIs solved, so I'm a big fan of the explorations in this thread.
I think another important thing to keep in mind here when comparing potential solutions (if it hasn't already been mentioned up-thread somewhere), is that there is legitimate value in deprecation warnings. Even more precisely, I see immense value in warnings about newly deprecated declarations when libraries are updated. It may very well be the case that at the time of importing FooKit
, I feel comfortable about ignoring all the deprecated APIs, but I don't know if we should provide a way for users to easily express "I will never care about any deprecated APIs in the future".
ETA: Another way of phrasing what I'm trying to get at is that while a blanket "ignore all deprecation warnings" compiler flag, import/scope annotation, etc., might seem to be to the benefit of users, it might actually do more harm than good, and I think that it is strictly worse for library developers who have an interest in making sure that their users are alerted in a timely manner about new deprecations
The story of the danger I'm imagining, with both a scope-by-scope solution and an import-by-import solution looks like the following. I'm writing an app, using FooKit 1.0, which has various APIs, some deprecated from the 0.x days:
// FooKit
@available(*, deprecated, message: "No replacement, sorry!")
func myGreatFunc() {}
func myOtherFunc() {}
As an application developer, I really want to use myGreatFunc()
, and I know it won't be going away any time soon, so I want to disable the deprecation warning (either so I can compile with warnings-as-errors, or keep warnings spam low, whatever). With coarser solutions, that might look like this
// MyApp
// import-based
@noDeprecationWarnings
import FooKit
// scope based
@noDeprecationWarnings
func useFooKit() {
myGreatFunc()
myOtherFunc()
}
One day, I decide to update to FooKit 2.0:
// FooKit
@available(*, deprecated, message: "No replacement, sorry!")
func myGreatFunc() {}
@available(*, deprecated, message: "This is inefficient, use myFixedOtherFunc!")
func myOtherFunc() {}
func myFixedOtherFunc() {}
Now, in the MyApp target, I would really have liked to get the deprecation warning for myOtherFunc()
, but I won't! Sure, with the scope-based solution I could have preemptively wrapped the call to myGreatFunc()
in a separate scope, something like:
@noDeprecationWarnings
do {
myGreatFunc()
}
but I suspect most people wouldn't do that by default.
This pulls me toward a solution closer to "line-by-line", but I might even prefer something more granular. Since deprecations are applied on a declaration-by-declaration basis, they should be disabled on a declaration-by-declaration basis. The solution in the FooKit example above would look something like:
func useFooKit() {
#deprecated(myGreatFunc)()
myOtherFunc()
}
This would apply anywhere you refer to a (deprecated) declaration:
class MySubclass: #deprecated(OldSuperclass) {
func myMethod() {
#deprecated(self.oldMethod)()
}
}
In cases where you're using the deprecated declaration liberally, you would of course be free to redeclare the declaration to 'undeprecate' it:
typealias OldSuperclass = #deprecated(FooKit.OldSuperclass)
extension OldSuperclass {
func oldMethodFixed() {
#deprecated(self.oldMethod)()
}
}
This would fix all the cases where I've wanted to disable deprecation warnings, and I can't think of a situation where it wouldn't work (though please poke holes if you see any ). I also really like that it mirrors the granularity at which deprecations are applied. IMO, the burden of local redeclaration for common uses of deprecated is not too high.
ETA:
For an example of why I think even line-by-line is too coarse, consider (using syntax akin to try
/await
):
// FooKit
@available(*, deprecated, message: "No replacement, sorry!")
func myGreatFunc(_: Int) {}
@available(*, deprecated, message: "This is inefficient, use getAnIntFast!")
func getAnInt() -> Int {}
func getAnIntFast() -> Int {}
// MyApp
func useFooKit() {
// This ignores for myGreatFunc, but also getAnInt which we _do_ want a warning for!
ignoreDeprecations myGreatFunc(getAnInt())
}