Keeping NSUserActivity backwards compatible with Xcode 9

时间秒杀一切 提交于 2019-12-24 08:35:37

问题


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

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!