So, I have a library, which includes a public API for percent-encoding. The library comes with a bunch of "encode sets" (sets of characters which should be percent-encoded) as defined in the URL standard, and it is possible for users for create their own by conforming to a protocol.
The API looks like this:
extension StringProtocol {
public func percentEncoded<EncodeSet: PercentEncodeSet>(
as encodeSet: EncodeSet
) -> String {
// ...
}
public func percentEncoded<EncodeSet: PercentEncodeSet>(
as encodeSet: KeyPath<URLEncodeSet, EncodeSet>
) -> String {
percentEncoded(as: URLEncodeSet()[keyPath: encodeSet])
}
}
public struct URLEncodeSet {
public var userInfo: UserInfo { ... }
}
Now, this might look a bit strange at first - I have one API which accepts an instance of a PercentEncodeSet
, and another which accepts a KeyPath on an empty struct which serves as a namespace. The benefit of this is that it allows a more fluid API for well-known encode sets:
someString.percentEncoded(as: \.userInfo)
Without requiring the user to instantiate an object or write out type names such as URLEncodeSet.UserInfo.self
.
In the mean time, SE-0299: Extending Static Member Lookup in Generic Contexts happened. The API I have is exactly the kind of "static member on protocol" API which the proposal intends to make easier. So instead of all of this awkward KeyPath stuff: I can simply write an extension on the protocol:
extension PercentEncodeSet where Self == URLEncodeSet.UserInfo {
public static var userInfo: URLEncodeSet.UserInfo { ... }
}
And users can write:
someString.percentEncoded(as: .userInfo)
Without having to go through the KeyPath overload. Very cool!
Unfortuntately, this new syntax requires a Swift 5.5 compiler, and I'd like to support Swift 5.3+. So what I'd like to do is to include both, but steer users with a Swift 5.5 compiler towards the new syntax. It turns out that it is actually possible:
-
Enclose the above
PercentEncodeSet where Self == X
extensions in a#if swift(>=5.5)
block. -
Use
@available
to mark the KeyPath overloads as deprecated for users with Swift 5.5+ compiler:@available(swift, deprecated: 5.5, message: "Use static member syntax instead; e.g. percentEncoded(as: .userInfo)") public func percentEncoded<EncodeSet: PercentEncodeSet>( as encodeSet: KeyPath<URLEncodeSet, EncodeSet> ) -> String { percentEncoded(as: URLEncodeSet()[keyPath: encodeSet]) }
But what happens is that now I'm getting a bunch of deprecation warnings when compiling with Swift 5.5! Not cool! I'd still like to use the KeyPath version internally, because I still need the ability to build with Swift 5.3, but I'd like clients to use the more modern syntax.
So this is my question: Is there a way to say "this API is deprecated for clients using the latest Swift compilers, but not for me, because I still need to work with older compilers"?