Swift预处理器中用于iOS版本检查比较的等效方法是什么?

26

我遇到了问题

yld: Symbol not found: _OBJC_CLASS_$_UIUserNotificationSettings

以下是导致应用在 iOS7 设备上运行时出现错误的函数,即使在代码中根本没有调用该函数

func reigsterForRemoteUserNotifications(notificationTypes: UIUserNotificationType, categories: NSSet) {
        let userNotificationSettings = UIUserNotificationSettings(forTypes: notificationTypes, categories: categories)
        (UIApplication.sharedApplication()).registerUserNotificationSettings(userNotificationSettings)
        UIApplication.sharedApplication().registerForRemoteNotifications()
    }
我不希望在 iOS7 设备上运行时能够访问此方法。我不希望在方法中进行选择检查,因为这意味着该方法可以被使用。
我想要的是一个构建配置参数来检查版本: 我无法想出一种方法来编写一个 Swift 等效的预处理器宏来检查正确的 iOS 版本并忽略新的和未声明的 iOS 8 库函数。
#if giOS8OrGreater
// declare the functions that are iOS 8 specific
#else 
// declare the functions that are iOS 7 specific

#endif
在文档中,苹果建议使用函数和泛型来替代复杂的宏,但在这种情况下,我需要一个构建配置预编译检查以避免处理未声明的函数。有什么建议吗?
在文档中,苹果建议使用函数和泛型来替代复杂的宏,但在这种情况下,我需要一个构建配置预编译检查以避免处理未声明的函数。有什么建议吗?

你为什么不在运行时检查呢?实际上,我不确定是否可以在编译时检查,因为否则它只能在iOS8及更高版本或iOS7及更低版本上运行。 - miho
看看NSHipster最近的这篇文章吧:http://nshipster.com/ios8/ 特别是关于检查版本的部分! - Jack
1
NSHipster的文章中提到NSProcessInfo().isOperatingSystemAtLeastVersion(yosemite),似乎是在检查操作系统是否为Yosemite。不确定它是否适用于iOS。 - GlennRay
1
你可以使用以下代码:var systemVersion = (UIDevice.currentDevice().systemVersion as NSString).floatValue但是它在 iOS 7 上无法正常工作,因为如果编译的代码有任何新框架 API 的引用,它将崩溃,并显示以下错误信息: 找不到符号: OBJC_CLASS$_UIUserNotificationSettings总之,在运行时你不能检查它。 //抄送 @miho - Wojtek Turowicz
1
这个问题似乎在Xcode 6 beta 6中已经被修复了。 - user102008
显示剩余4条评论
6个回答

26

其他答案没有提到检查系统版本的正确方法。您绝不能使用:Device.systemVersion。不应该创建自定义宏来检查版本号,也不应该深入苹果专门为此任务定义的库。

这里有一篇很棒的文章详细介绍了这个问题 here

请注意,Swift 2.0允许您通过以下方式直接检查操作系统版本号是否可用:

if #available(iOS 10.0, *) {
    // modern code
} else {
    // Fallback on earlier versions
}

在Swift 2.0之前,推荐的方法是通过系统提供的宏来实现:

    if (NSFoundationVersionNumber > NSFoundationVersionNumber_iOS_9_0) {
    // do stuff for iOS 9 and newer
} else {
    // do stuff for older versions than iOS 9
}

或通过:

    if NSProcessInfo().isOperatingSystemAtLeastVersion(NSOperatingSystemVersion(majorVersion: 10, minorVersion: 0, patchVersion: 0)) {
    // modern code
}

对于系统宏以外的任何遗漏内容。

苹果公司不推荐使用任何其他方法,因为它们都被认为是不可靠的。实际上,在iOS 10中会出现一种失效的方法。

请注意,如果您需要在检查中使用类似宏的功能,并且想要使用#available,则可以使用此文章中定义的@available来实现:

 @available(iOS 7, *)
func iOS7Work() {
    // do stuff

    if #available(iOS 8, *) {
        iOS8Work()
    }
}

@available(iOS 8, *)
func iOS8Work() {
    // do stuff
    if #available(iOS 9, *) {
        iOS9Work()
    }
}

@available(iOS 9, *)
func iOS9Work() {
    // do stuff
}

如果您想了解有关Swift中属性的更多信息,可以参考苹果公司的文档


很好。我需要使用@available语法来使一个类符合新的与Apple登录相关的协议,这些协议仅在iOS 13及以上版本中可用,而不会放弃对旧操作系统版本的支持。你只需在符合协议的扩展中标记此标签,例如:@available(iOS 13, *) extension MyClass: ASAuthorizationControllerDelegate。完美地解决了问题。 - Phlippie Bosman

12

Constants.swift 文件中:

Swift 1:

let Device = UIDevice.currentDevice()
private let iosVersion = NSString(string: Device.systemVersion).doubleValue

Swift 2:

let Device = UIDevice.currentDevice()
private let iosVersion = Double(Device.systemVersion) ?? 0 

let iOS8 = iosVersion >= 8
let iOS7 = iosVersion >= 7 && iosVersion < 8

然后在其他文件中

if iOS8
{

}
else
{

}

但是,当我在目标iOS 7上调用一些仅适用于iOS8的方法(例如:SKShareNode(rect ...)),尝试构建时会出现错误。 - Mr Phuc 87
请尝试使用Swift 2版本。 - ChikabuZ

4

变量systemVersion = UIDevice.currentDevice().systemVersion

意思是获取当前设备的系统版本号。

这很糟糕,不建议使用。请参考我的回复。 - TheCodingArt

4

更新:这已在Xcode 6 beta 6(版本为6A280e)中得到修复。


这是一个(也许不太好的)解决方法:显式地弱链接UIKit(我知道import语句应该已经链接了框架,但我们仍然要显式地链接)。

  • 在Xcode中点击项目
  • 选择左上角的目标
  • 进入“General”选项卡
  • 向下滚动到“Linked Frameworks and Libraries”
  • 添加UIKit.framework
  • 将“Status”从“Required”更改为“Optional”

这可能会导致整个UIKit被弱链接,从而影响性能。不确定。

在Objective-C中,即使是“Required”,编译器也会自动对可用性高于部署目标的符号进行弱链接,并将其他符号保持强链接状态。我不知道为什么Swift不能这样做。


3
根据苹果公司的说法,Swift中没有预处理器调用,但是基于两个函数中构建配置的评估,可以有条件地编译Swift代码。目前,我们可以使用os()选择OSX或iOS,以及arch()选择x86_64、arm、arm64或i386。因此,您可以评估#if os(iOS),但不能评估#if os(iOS8)(尽管这似乎是Swift更高版本的好主意)。

那本来会很好,但不幸的是事实并非如此。 - Essa A. Haddad
你可以使用 #if os(iOS),但它并没有指定操作系统的版本。 - David Berry

1
你可以通过以下方式检查 iOS 版本 -
let iosVersion = UIDevice.currentDevice().systemVersion

使用NSStringCompareOptions.NumericSearch进行比较。

switch iosVersion.compare("8.0.0", options: NSStringCompareOptions.NumericSearch)
{
    case .OrderedSame, .OrderedDescending:
        //iOS>=8.0.0
        return LAContext()
    case .OrderedAscending:
        //iOS<8.0.0
        return nil
}

您也可以使用新的NSProcessInfo API,但它们在iOS 7上不可用。

if NSProcessInfo().isOperatingSystemAtLeastVersion(NSOperatingSystemVersion(majorVersion: 8, minorVersion: 0, patchVersion: 0)) {
//iOS>=8.0.0
}

你可以在这里查看更多细节。希望这能解决你的问题。

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