I would like to modify the @available
macro to be easier to read and more enforceable by the compiler. First I would like to go over the parameters you can provide to this new version:
- Build Group: this can be a platform, language names(ie.
swift
) or package(when usingPackage.swift
) unavailable
/disabled
: Prevent the compiler from building the attached code.unavailable
is for platforms and swift versions,disabled
is for packages.introduced
: Takes a version argument that represents the introduction versiondeprecated
: Takes a version argument that represents the version before the code section became deprecatedobsolete
: Takes a version argument that represents the version when the code section became obsoletemessage
: Takes a string and outputs it when using a version where it is deprecated or obsoleterenamed
: Provide the old name for the code section. This can be invoked repeatedlyreplaced
: indicate that a code section was replaced with another code section referenced from where the old function was based off of the absolute location in the program structure where the code block it replaced was. This argument can be invoked repeatedlyrequires
: indicates the compiler conditions required to enable code(ie.AdvancedNetworking
), this just automatically generates the#if
macroincompatible
: indicates the compiler conditions that prevent compilation, this automatically generates inverse conditions to the provided ones.
I want to make obsolete
stop the compiler from checking the code allowing you to stubify your code. If you want to discuss unavailable
/disabled
just hold on for a bit.
I also want to introduce replaced
to indicate that the code was moved or rewritten from the old version at the parameter location as it clearly denotes that the code is the successor of the specified code. renamed
indicates a prior name that it held.
Here is a Demo of how this works in the new @available
works, assuming we building version 3.1 of our program;
class Swift {
@available(introduced:1, deprecated:2.0.2, obsolete:3.2, message:”Lost all children”)
class stdio{
@available(introduced:1.0, deprecated:2.0.2, obsolete:3.1, message:”parent removed”, replaced: Swift.printf)
func printlnf(_ message:String...) // Throws a error
}
@available(introduced:2.0.2, renamed:printf)
func print(_ message:String...) { … }
}
Feedback:
I am seeking your feedback on a few critical questions. These will be sprinkled in to this postHow should
replaced
and related parameters work?
- Use a
replacement
parameter for the item that was replaced that has >the same syntax as Replaced- Keep
replaced
as it already was- Keep
replaced
in the same spot but call itreplacement
- No special behavior
Versions will be parsed sequentially with the Leftmost number being the major version and the Rightmost number being the most minor version and only require the most relevant version.
If your project is versioned macro allows you to regress or upgrade the interface by changing the version number in Package.swift
; If you don’t have a Package.swift
the code will not be affected and the macro will not affect. This means teams can be working on future versions alongside older versions allowing your teams to work in the necessary direction so that a full feature set can be worked on across your build versions all at the same time so future features can be set up to work with each other and you can write poly-versioned code without necessarily needing to splice it in later or use git.
The new functionality for unavailable
/disabled
are a very interesting concept to use. Mostly the ability for them to not require that code to compile properly. This allows for developers who are building a feature that is buggy to allow the project to still work. This functionality is extended in obsolete
where it can allow for stubified code blocks.
Feedback:
How should
unavailable
/disabled
work
- Hide and prevent localized errors from reaching the console
- Show but prevent localized errors from affecting your build
- Show all localized errors
@available
can be used in a variety of ways but one of the most important things it detailed was the introduction, depreciation, and obsolescence of a section in your code. This new version also has some additional implementation details relating to these; It will use a compiler/PackageDescription
managed flag to indicate the software’s version. I also am interested in some related build conditions for things like build(
Build number<=)
and some tree parsing macros like useerror(<#String#>)
and usewarning(<#String#>)
so that people may more manually use these tools.
This is some example code:
@available(introduced:1.0.0, deprecated:2.0.2, obsolete:3.1, message:”Connected API obsolete”)
@available(required:DEBUG DEMO, required:DEBUGDEMO, message:”Requires ”)
func demo() { print(“Demo”) }
and it will pre expand to:
#if build(>=1.0.0) && !(build(<3.1))
#if (DEBUG && DEMO) || DEBUGDEMO
func demo() {
#if build(2.0.2)
#usewarning(“Connected API Obsolete”)
#endif
print(“Demo”)
}
#else
func demo() {
#useerror(“Requires DEBUG and DEMO or DEBUGDEMO to use”)
}
#endif
#endif
Then the compiler works with this representation and then processes the macros generated
Making this work like this can help developers learn about what is happening. Additionally since developers have access to do this, this can allow for a pure swift implementation and practice Separation of concerns. But the macro can allow over declarations where a prior namespace can be reallocated, as long as they are mutually exclusive.
An example of this is provided
@available(introduced:1.0.0, deprecated:2.0.2, obsolete:3.1, message:”Connected API obsolete”)
@available(required:DEBUG DEMO, required:DEBUGDEMO, message:”Requires DEBUG and DEMO or DEBUGDEMO to use”)
func demo() { print(“Demo”) }
@available(introduced:3.1) // This is required for a over declaration
func demo() { print(“Demo 2”) }
demo() // “Demo 2”
Suggested Mutual Exclusivity Error Message
Error: a over declaration for the function demo() failed, as the declarations are not Mutually Exclusive
Note: You may want to use `@available(introduced:)` with version 3.1 or later to achieve mutual exclusivity
Note: You may want to use `@available(incompatable:DEBUG DEMO, incompatible: DEBUGDEMO)` to achieve mutual exclusivity
and this unwraps pretty similarly
#if build(1.0.0>=) && !(build(<3.1))
#if (DEBUG && DEMO) || DEBUGDEMO
func demo() {
#if build(2.0.2)
#usewarning(“Connected API Obsolete”)
#endif
print(“Demo”)
}
#else
func demo() {
#useerror(“Requires DEBUG and DEMO or DEBUGDEMO to use”)
}
#endif
#else
#if build(>=3.1)
func demo() {
print(“Demo 2”)
}
#endif
#endif
Feedback:
Which is the most readable assuming we want to check the availability of the package and that it conforms
@available(introduced:1.0.0, deprecated:2.0.2, obsoleted:3.1, message:”Connected API obsolete”)
@available(self, introduced:1.0.0, deprecated:2.0.2, obsoleted:3.1, message:”Connected API obsolete”)
@available(MyPackage, introduced:1.0.0, deprecated:2.0.2, obsoleted:3.1, message:”Connected API obsolete”)
DocC can always use this information and it can allow for documentation to let your users know when it is getting deprecated beforehand and a current timeline from when you start support, when you sunset it all the way to when you end it. It can also let your users know that a flag needs to be set to use a feature.
Thank you for all of your time and have fun coding!