使用@available与存储属性

54

我有一个使用本地通知且支持iOS 10的应用程序。我正在尝试添加对iOS 9的支持,这需要我使用旧版的本地通知API。在我的iOS 10代码中,我尝试使用@available#available,但我无法弄清楚如何使我的中心变量仅适用于运行iOS 10的设备。

当我将目标从iOS 10设置为9时,会出现此变量的错误消息:

UNUserNotificationCenter is only available on iOS 10.0 or newer.

建议我将@available(iOS 10.0, *)添加到整个类中,但我不想这样做,因为该类中有将用于iOS 9的代码。我希望能提供任何关于如何仅限于iOS 10的建议。

class ViewController: UIViewController, UITextFieldDelegate {
  
  let center = UNUserNotificationCenter.current()
  ...
}
11个回答

80

适用于Xcode 14(及以下版本)的解决方案

这里有一个潜在的解决方案(感谢博客帖子)。思路是使用一个类型为Any的存储属性,然后创建一个计算属性来转换存储属性(必要时进行实例化)。

private var _selectionFeedbackGenerator: Any? = nil
@available(iOS 10.0, *)
fileprivate var selectionFeedbackGenerator: UISelectionFeedbackGenerator {
    if _selectionFeedbackGenerator == nil {
        _selectionFeedbackGenerator = UISelectionFeedbackGenerator()
    }
    return _selectionFeedbackGenerator as! UISelectionFeedbackGenerator
}

太聪明了! :) - Toma Radu-Petrescu
有没有一种方法可以在Objective-C中实现相同的事情? - Jan Ehrhardt
懒惰变量是个好主意。 - Baran Gungor

56

Xcode 14.0.0 beta 1更新

不幸的是,看起来lazy属性现在被视为存储属性,因此这种解决方法不再有效。编写自己的带有备份存储的计算属性是目前的最佳解决方案。

编辑:事实证明,这种方法始终是错误的,只是没有产生诊断。 Swift 5.7已修复了这个问题: https://github.com/apple/swift/pull/41112


先前回答

我知道这是一个比较老的问题,但是我想为那些像我一样通过Google进入这里的人添加一个答案。

正如kgaidis和Cœur所提到的,您可以在计算属性上使用@available。但是,lazy变量也被视为计算属性,因此您也可以在它们上面使用@available。这有一个很好的好处,即消除了额外存储属性和强制转换的模板代码 - 实际上,在你的iOS 10以下代码中,它不会留下任何属性的痕迹。

您可以简单地像这样声明:

@available(iOS 10.0, *)
private(set) lazy var center = UNUserNotificationCenter.current()

很遗憾,没有办法完全将其设为只读,但private(set)至少使其在类外部成为只读。


感谢分享! - LinusGeffarth
对我来说,这个在构建时可以工作,但在运行时不能工作:https://stackoverflow.com/questions/62301128/is-it-possible-to-use-an-availableios-13-computed-property-in-a-class-that-ru - lewis
@lewis 我无法在iOS 12.4设备上重现此问题,但您可能需要像一个答案建议的那样进行弱链接框架的研究。 - Jayson
1
另一种方法是:@available(iOS 14.0, *); private(set) lazy var whateverClass: WhateverClass? = nil - Lance Samaria
4
无法在Xcode 14 beta中继续工作。 - nathanwhy

42

在XCode 14中,我的Flutter项目出现了这个错误。其中一个外部包被使用:

@available(iOS 14.0, *)

我的当前解决方法是:

更新Podfile中的版本号 platform :ios, '14.0'

重置pods cd ios && rm -rf Pods/ Podfile.lock && pod install --repo-update

问题:https://github.com/pichillilorenzo/flutter_inappwebview/issues/1216


有没有一种方法可以在不将全局平台设置为iOS 14的情况下解决它? - jed1
您可以检查软件包的PR并从GitHub中使用它们,直到它们合并。例如,flutter_inappview有一个尚未合并的PR。https://github.com/pichillilorenzo/flutter_inappwebview/issues/1216#issuecomment-1245077988而且就个人而言,我认为没有问题,因为7岁的iPhone 6S已经升级到iOS 14了。https://gs.statcounter.com/ios-version-market-share/ - mirkancal
1
所以有一个快速的临时解决方案,您可以使用PODS...然后查找flutter_inappwebview,单击它并转到构建设置,搜索"IOS Deployment Target",将其更改为iOS 14,如果您不想更改整个POD文件为平台14。 - Bobby
在重置 pods 之前执行 'flutter clean' 和 'flutter pub get' 对我来说修复了这个问题。 - berkaykurkcu

7

@available 可以用于整个类或一个或多个函数,但不能用于属性。

关于您的UNUserNotificationCenter使用,current 返回一个永远不会改变的单例,所以为什么不删除 center 常量,并在使用 center 的地方直接使用 UNUserNotificationCenter.current() 呢?


3

与kgaidis类似的想法,是使用一个在任何版本中都被接受的类型的单独存储属性。但是Any可能太泛泛了,因为它不能被声明为weak,所以您可能希望在某些情况下将其替换为符合协议的内容:

private weak var _notificationCenterDelegate: NSObjectProtocol?
@available(iOS 10.0, *)
var notificationCenterDelegate: UNUserNotificationCenterDelegate? {
    return _notificationCenterDelegate as? UNUserNotificationCenterDelegate
}

1

针对 Objective-C

@property (nonatomic, strong) CustomClass *object API_AVAILABLE(ios(10.0));


1
我在升级到xCode 14.*时遇到了这个问题。
pod update
pod install 

对我有用。

1

从@kgaidis的解决方案开始,我为苹果登录按钮做了这个:

private var _appleButton: UIControl? = nil
@available(iOS 13.0, *)
private var appleButton: ASAuthorizationAppleIDButton {
    if _appleButton == nil {
        let btn = ASAuthorizationAppleIDButton()
        btn.addTarget(self, action: #selector(handleAppleSignInPress), for: .touchUpInside)
        _appleButton = btn
    }
    return _appleButton as! ASAuthorizationAppleIDButton
}

这样我仍然可以在@available(iOS 13.0, *)下使用appleButton

if #available(iOS 13.0, *) {
    holderView.addSubview(appleButton)
}

0

存储属性不能使用 '@available' 标记为可能不可用

只需将 NSPersistentCloudKitContainer 更改为 NSPersistentContainer 并检查其是否正常工作即可。


0

我在我的应用程序中使用的反馈生成器代码支持iOS 9。正如您所看到的,它很简单,没有强制转换。主要思想是将值存储在Any?属性中,并通过计算属性使用它。

private var storedFeedbackGenerator: Any? = nil
@available(iOS 10.0, *)
private var feedbackGenerator: UISelectionFeedbackGenerator {
    if let generator = storedFeedbackGenerator as? UISelectionFeedbackGenerator {
        return generator
    }

    let generator = UISelectionFeedbackGenerator()
    generator.prepare()
    storedFeedbackGenerator = generator
    return generator
}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接