问题
Using Xcode 10 (beta 6) I am able to write and run the following code with no trouble:
import Intents
func test() {
let activity = NSUserActivity(activityType: "com.activtiy.type")
activity.title = "Hello World"
activity.isEligibleForSearch = true
activity.isEligibleForHandoff = false
if #available(iOS 12.0, *) {
activity.isEligibleForPrediction = true
activity.suggestedInvocationPhrase = "Say something"
}
print(activity)
}
As of iOS 12 the .isEligibleForPredictions
and .suggestedInvocationPhrase
properties have been added, so Xcode 10 can keep the code itself backwards compatible using the if #available
conditional.
However, I want to ensure this code is backwards compatible with earlier versions of Xcode. When run in Xcode 9, I get the following errors:
if #available(iOS 12.0, *) {
// ERROR: Value of type 'NSUserActivity' has no member 'isEligibleForPrediction'
activity.isEligibleForPrediction = true
// ERROR: Value of type 'NSUserActivity' has no member 'suggestedInvocationPhrase'
activity.suggestedInvocationPhrase = "Say something"
}
This appears to be because the #available
macro is actually resolved at runtime, therefore all code contained still needs to be compiled successfully.
Is there a way for me to tell the compiler to just ignore these two lines of code when building for iOS 11, or using Xcode 9?
回答1:
Xcode 10 uses Swift 4.2 while Xcode 9 uses Swift 4.1. So you can use that knowledge at compile time:
func test() {
let activity = NSUserActivity(activityType: "com.activtiy.type")
activity.title = "Hello World"
activity.isEligibleForSearch = true
activity.isEligibleForHandoff = false
#if swift(>=4.2) // compile-time check
if #available(iOS 12.0, *) { // run-time check
activity.isEligibleForPrediction = true
activity.suggestedInvocationPhrase = "Say something"
predictionApiAvailable = true
}
#endif
print(activity)
}
(This answer assumes that you are using Swift 4.2 on Xcode 10.)
回答2:
The Availability Condition (if #available) you are using as you correctly noted is evaluated at run-time, but the compiler will use this information to provide compile-time safety (It will will warn you if you are calling an API that does not exist min deployment target).
For conditionally compiling code you must use Conditional Compilation Block
#if compilation condition
statements
#endif
To conditionally build your code based on the Xcode version you can include a Active Compilation Condition in the build settings (a -D swift compile flag) that its name is dynamically created based on the Xcode version. Then use this as compilation condition. Xcode already provides the build setting XCODE_VERSION_MAJOR that resolves to the current Xcode version( e.g. 0900 for Xcode 9). So you can add an an Active Compilation Condition with name XCODE_VERSION_$(XCODE_VERSION_MAJOR) that will resolve to the flag XCODE_VERSION_0900 for Xcode 9.
Then you can conditionally compile your code using:
#if XCODE_VERSION_0900
statements
#endif
I have an example project here
来源:https://stackoverflow.com/questions/52118357/keeping-nsuseractivity-backwards-compatible-with-xcode-9