hasSymbol compiler directive aims to be a viable alternative to availability where it has not been explicitly attributed, or where a symbol is available in a library on one platform, but not available on another.
There are many instances where you would want to check the symbol availability at compile time. One good reason to do this during compile time is binary size. Any code inside the
#if block wouldn't be compiled if the symbol wasn't available. If the symbol has been marked with
@available, it would take that into account; But it wouldn't replace the #available check where there is no deployment target. Since it also takes into account whether the module is importable in addition to the symbol check, it makes it trivial to use.
Since this also works where symbols are not attributed with
@available it also makes it the go-to for platforms outside of apple as attributes like
@available(Linux, *) do not work/exist.
Lets say you want to write some cross-platform code to access the System libraries
FileDescriptor.OpenOptions.sharedLock symbol. You can't access this symbol on linux, but you can on apple-darwin systems. Now, you could check for the symbol like this:
#if os(iOS) || os(macOS) || os(iPadOS) || os(watchOS) // Access symbol here #endif
And this is ok. There's no clear alternative to this as
#if canImport(System) would be true on both platforms. But there is more than one trade-off here. The first one is that if in the future Linux gets the sharedLock symbol, you now have to remove the check to have the code on at linux. The second trade-off is readability. There has been an increase in groans associated with using an os-chain like this one.
The example changes into a symbol-specific check that reflects whether the module and the symbol exists on the target platform.
#if hasSymbol(System.FileDescriptor.OpenOptions.sharedLock) // Access symbol here #endif
From reading the check you immediately are able to figure out why the check is there. It clarifies the ambiguity of the alternatives and is symbol specific which confirms whether it will be there like the developer intended it to be.
Well, the only way for both symbols to have the same name, is if they are function symbols, or else they wouldn't have the same hierarchy. Therefore, a selector-like autocomplete would be necessary for that.
As this is just a pitch, there are still things that can be improved in the way this is implemented. Feel free to critique and help make this idea even better.
This directive will be checking a few things to determine whether it resolves to
- Symbol scope. (Is the symbol private from the access location?)
- Target version. (Is the symbol available on the target version or future versions?)
- Platform. (Does the platform support the symbol trying to be accessed?)
- Symbol existing. (Does the module even export such a symbol?)