Is this correct way to use `@available` attributes?

Some method have system version constraint. for example, NSKeyedUnarchiver.unarchiveTopLevelObjectWithData(_:) requires macOS 10.11 or higher:

@available(* 4, OSX 10.11, iOS 9.0, *)
@nonobjc public class func unarchiveTopLevelObjectWithData(_ data: Data) throws -> Any?

If I use it in target lower than macOS 10.11, an error will be emitted. However, if I implement it for lower version, the error disappears:

@available(macOS, obsoleted: 10.11) // (?)
@nonobjc class func unarchiveTopLevelObjectWithData(_ data: Data) throws -> Any? {
    // My own implementation
}

I haven't seen this usage. isn't @available just a flag, and #available does the actual dynamic checking? How does this works? Is this correct?

1 Like

I presume you’re implementing this in an extension. If so, my recommendation is that you implement it using a different name. At that point your implementation can:

  • Call through to the system implementation if it’s available

  • Implement a compatibility path on older systems

Share and Enjoy
—
Quinn “The Eskimo!”
Apple Developer Relations, Developer Technical Support, Core OS/Hardware
let myEmail = "eskimo" + "1" + "@apple.com"

Related: Best way to implement new feature for old SDKs

I knew #available would work. My question is, what’s the intended behavior in my example? Will the two method switch seamlessly? Considering the compiler doesn’t complain, can I assume it would work as I expected? If not, shouldn’t compiler emits an error?

The obsoleted annotation isn't really meant to be used in this way; it's for advertising to other code that this declaration has been removed. The compiler should really enforce obsoleted the way it enforces introduced (which your first example is shorthand for), but there's been little call for it in practice.

So, no, your code does not do what you want it to do. It's doing the same thing as if you left off the obsoleted, which is shadowing the system implementation of this method within your module (but not replacing it).

It seems the system implementation is not shadowed:

extension NSKeyedUnarchiver {
    
    @available(macOS, obsoleted: 10.11)
    @nonobjc class func unarchiveTopLevelObjectWithData(_ data: Data) throws -> Any? {
        fatalError("Not Implemented")
    }
}

try? NSKeyedUnarchiver.unarchiveTopLevelObjectWithData(Data())
// nil

No fatal error.