Support Negative Availability Literals

Yeah, the real test is whether the compiler complains about symbol availability, not whether the run-time check is correct. Once the compiler's done checking availability, #available is emitted as just a boolean check.

I might prefer that it's spelled:

if #notavailable(iOS 13, *) { }

or

if #unavailable(iOS 13, *) { }

which are reminiscent of

#ifndef BLAH

Those empty braces needed for the current syntax to work are a wart in need of fixing.

4 Likes

My previous commit wasn't properly applying the version ranges, but this new iteration correctly complains about symbol availability when expected. It's now just missing proper treatment of the ! prefix.

From the implementation, I'm starting to think that using the normal boolean notation might not be the best solution for this. As availability literals can't be used as expressions, some hacking will be necessary to make the ! notation work. I think #unavailable could be a good way to cleanly introduce this change, and probably could be implemented in a way that SwiftUI can use it without adding a new protocol method.

4 Likes

Given that I now have a fully functional solution, I've submitted a draft proposal and implementation PR: https://github.com/apple/swift-evolution/pull/1184

1 Like

One thing I was wondering is that #available and #unavailable are very similar in the sense of reading through the code base, so it may be hard sometimes to spot that is an unavailable instead of an available if we are reading through the code looking for a problem. So one suggestion could be in code coloring use a slightly different color for #unavailable ... not sure if this was already thought or discussed but the point is to make it easier to look at a piece of code and easily spot which is an #available and #unavailable condition.

Maybe that's a stupid idea, but I'll throw it in the room nonetheless.

For this to work, we would need to basically have a (in lack of a better word) generic type, that has the availability requirements as a generic parameter.
Something like the following:

let hasiOS13: AvailabilityBoolean<iOS 13.0> = #available(iOS 13.0) // type could of course be inferred

This could enable an app having a single global variable, which can be used in if or guard statements or in ternary expressions and which could still be statically enforced.
It would probably mean a bit more implementation work, but could also pay off quite well IMHO.

It's a fair point. I wish we could go for the basic #available(...) == false but I don't know if the core team would accept it given that the implementation would be a huge hack. It's still in the table though if they think otherwise.

It would’ve been nice if there was a method you could call, like available(.iOS(14)), and get a Bool back which can used in various places like if or just storing it in some variable. So basically #available is modelled as a regular method call and we can deprecate existing uses of it. It might be a bit of a hack too but it seems more natural to me.

1 Like

Or a Struct/Class that provides this method and perhaps other methods to query the runtime environment. That seems more Swifty to me and better than adding free functions.

Using a hashmark to indicate something that doesn't resemble the C pre-processor doesn't seem that sensible, although I guess we're all used to it by now.

The Struct/Class would resemble UIKit's classes like UIDevice, UIScreen etc. that are Singletons and model aspects of the environment.

1 Like

Folks, while I would love to have #available as an expression I don't think it's really technically feasible to do so. This would likely require refactoring the entire symbol availability system, has many edge cases and doesn't really solve many problems. I would rather not diverge from the original proposal which just includes the ability to negate the current check, which is clearly an oversight in the compiler given that you can do so just fine in Obj-C.

2 Likes
Terms of Service

Privacy Policy

Cookie Policy