rust-objc
rust-objc copied to clipboard
How to call @available?
if (@available(macOS 10.15, *)){
/// whatever
}
How would I do this in rust?
Your question is a little off topic from this repo but I don't know of much better place.
The Swift @available
attribute and the objective-c API_AVAILABLE
aren't quite the same in rust. The objective-c macro I think is evaluated at compile time. I'm not sure how the @available
attribute works in swift.
I think the best way to emulate this type of thing is to use the sysinfo
crate's os_long_version
with something like...
let s = System::new();
if(s.os_version().starts_with("11.0")) { // for macOS
// whatever
}
The implementation for long_os_version
shows a few of the options.
You may also use the #[cfg(target_os("macos"))]
attribute for this type of thing but it doesn't give you OS version granularity.
@simlay Thank you for your reply. I agree, it's a bit off topic but wasn't sure where to ask.
I could be wrong, but I believe @available
in ObjC is runtime. https://stackoverflow.com/a/47334301/7886229
The sysinfo crate would be a last resort. I'd like to tap into the same thing @available
uses.
Hmm. The docks from clang: https://clang.llvm.org/docs/LanguageExtensions.html#objective-c-available
Before LLVM 5.0, when calling a function that exists only in the OS that’s newer than the target OS (as determined by the minimum deployment version), programmers had to carefully check if the function exists at runtime, using null checks for weakly-linked C functions,
+class
for Objective-C classes, and-respondsToSelector:
or+instancesRespondToSelector:
for Objective-C methods. If such a check was missed, the program would compile fine, run fine on newer systems, but crash on older systems.
This implies that it may actually do the check for you at runtime. I'm not sure how to send that to the objective-c runtime. @madsmtm has been doing some interesting work on objc2
and may have some thoughts.
However, https://godbolt.org/z/s8Gzx3hME, this seems to indicate that it's compile time if I am reading the assembly right
@available
is a compile-time directive, it conditionally compiles the code depending on the deployment target (can be set using the MACOSX_DEPLOYMENT_TARGET
/ IPHONEOS_DEPLOYMENT_TARGET
/ ... environment variables).
I am working on a crate to help do this in Rust, as it would be beneficial for a few things only available in newer versions, namely certain optimizations, linking to frameworks and some C symbols.
However, it'll probably take a while before it is done, so I would recommend the sysinfo
crate for now.
@madsmtm I'd like to be able to do runtime checks as well.
Something like from https://nshipster.com/available/:
@available(iOS 13.0, *)
final class CustomCompositionalLayout: UICollectionViewCompositionalLayout { … }
func createLayout() -> UICollectionViewLayout {
if #available(iOS 13, *) { // runtime check!
return CustomCompositionalLayout()
} else {
return UICollectionViewFlowLayout()
}
}
I'm not familiar with Swift (or low level stuff) enough to know how exactly that works.
Is it dynamic linking or something? Is that possible now in Rust? If not, would the crate you are making be able to do runtime checks as well?
Actually it's a little more complex than I thought: Objective-C's @available
/ Swift's #available
acts as both a compile-time and a run-time check!
As an example, if you have the following Objective-C code:
if (@available(macOS 10.10, *)) {
printf("Hello, macOS > 10.10!\n");
}
if (@available(macOS 10.14, *)) {
printf("Hello, macOS > 10.14!\n");
}
And tell clang
that the deployment version is 10.12
, it can remove the first @available
check at compile-time, but has insert a runtime check for the second @available
. Note that removing the first branch at compile-time is an optimization - it could choose not to!
Check out a tweaked version of your godbolt example, where we set -mmacos-version-min=10.12
(equivalent to setting environment variable MACOSX_DEPLOYMENT_TARGET=10.12
).
But again, using the sysinfo
crate you can essentially do the exact same runtime checks (albeit less efficiently).