Using @available to validate Package Manager dependencies

Have there been any thoughts around extending the @available mechanism to express a declaration’s lifecycle relative to versions of a library (as opposed to language version, platforms or OS version which we have currently) and to allow for validation of the version range of Package Manager dependencies (similar how an error is generated for a mismatch in the SDK range being compiled against and the availability of the declarations used)?

2 Likes

I haven't seen anything about this before but it is something I would very much like to have.

Yep! It's a good idea but actually doing it is a ways off. There's a bit of abstract discussion around doing it for run-time versions of libraries in docs/LibraryEvolution.rst (also in rendered form).

(Note that that document is fairly out-of-date by now. Haven't had the time to go update it for everything that's happened in Swift 4.1 and 4.2.)

EDIT: To be clear, "a ways off" implies "likely out of scope for Swift 5". That doesn't mean you can't talk about it, though!

2 Likes

Thanks Jordan,

It great to see that there has been some thinking around this for down the road.

This model is largely not of interest to libraries that are bundled with their clients ... as used by the Swift Package Manager

It is actually really important for library evolution in an ecosystem such as SPM.

There is a particular form of Dependency Hell when libraries unnecessarily over-specify the version-requirements of their dependencies. An application that depends on libraries with a common dependency could fail to resolve dependencies even if in actuality there is a version of the common dependency that would satisfy both libraries. Ideally a package management system should encourage/make it easy for library developers to accurately describe their compatibility with dependencies; limiting dependency version conflicts to situations when there is literally no library version that could satisfy the dependency closure.

Currently it doesn't seem realistically possible for a consumer of a library to determine the limits of compatibility with their dependencies. Most dependencies are expressed as being compatible from the current version forward or from the current version to the next major version. This isn't a great concern now but is likely to have greater impact as libraries become longer lived and start to have histories across many major versions.

References to a versioned API must always be guarded with the appropriate availability checks.

A question about this in the shared library case - making a library consumer handle the fact that 10 years ago there was a - now deprecated - version of the library that didn't have the API they want to use seems non-optimal. How would this be handled? Would it be recommended that these APIs just stop being versioned and fail if it ever encounters that older version or will shared libraries have a concept of dependency compatibility?

I'm late getting back to this, but

This model is largely not of interest to libraries that are bundled with their clients ... as used by the Swift Package Manager

It is actually really important for library evolution in an ecosystem such as SPM.

I didn't mean to say that versioning isn't interesting for source-built libraries, but that this document is mostly not interesting for that purpose. That's an oversimplification, but I'm going to sweep that into the "mostly". Still, I'll keep it in mind when I have time to update that document to what's in Swift 5.

A question about this in the shared library case - making a library consumer handle the fact that 10 years ago there was a - now deprecated - version of the library that didn't have the API they want to use seems non-optimal. How would this be handled? Would it be recommended that these APIs just stop being versioned and fail if it ever encounters that older version or will shared libraries have a concept of dependency compatibility?

The vague idea is that there'd be some kind of "minimum dependency version" just like there are "minimum deployment targets". Whether that's an annotation on the import, or a separate config file, or something you specify in the package manager config I haven't really thought about yet, but as long as there's space for it in the design everything should be good.