What is the Swift preprocessor equivalent to iOS v

2019-01-14 12:45发布

问题:

I am getting

yld: Symbol not found: _OBJC_CLASS_$_UIUserNotificationSettings

and here's the function that is causing the error when the application is running on an iOS7 device and without even calling the function at all in the code.

func reigsterForRemoteUserNotifications(notificationTypes: UIUserNotificationType, categories: NSSet) {
        let userNotificationSettings = UIUserNotificationSettings(forTypes: notificationTypes, categories: categories)
        (UIApplication.sharedApplication()).registerUserNotificationSettings(userNotificationSettings)
        UIApplication.sharedApplication().registerForRemoteNotifications()
    }

I don't want this method to be accessible at all when running on an iOS7 device. I do not want a select check inside of it because that means the method is available for use to begin with.

What I want is a build config parameter to check the version : I can't figure out a way to write a swift equivalent preprocessor macro to check for the correct iOS version and neglect the new and undeclared iOS 8 library functions.

#if giOS8OrGreater
// declare the functions that are iOS 8 specific
#else 
// declare the functions that are iOS 7 specific

#endif

In the documentation apple is suggesting functions and generics to substitute for complex macros but in this case I need a build config precompile check to avoid processing undeclared functions. Any suggestions.

回答1:

The other answers fail to mention proper ways of checking the system version. You should absolutely never utilize: Device.systemVersion You shouldn't make custom macros to check version numbers, and you shouldn't dig beyond the libraries that Apple has specifically defined for this task.

There's a great article detailing this out here.

Note that Swift 2.0 allows you to directly check if an OS version number is available via:

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

Prior to Swift 2.0, the recommended approach was via the system macros provided:

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

or via:

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

For anything missing beyond the system macros.

Any other approach has been downplayed as unreliable and not recommended by Apple. There's actually an approach that will break in iOS 10.

Note that if you need macro like functionality in a check and you'd like to use #available you can use @available defined in this article as such:

 @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
}

For further information on attributes in Swift, you can reference Apple's documentation.



回答2:

In 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

Then in other files

if iOS8
{

}
else
{

}


回答3:

var systemVersion = UIDevice.currentDevice().systemVersion



回答4:

Update: This is fixed in Xcode 6 beta 6 (build 6A280e)


Here's a (perhaps not so great) workaround: explicitly link to UIKit weakly (I know that import statements are supposed to link to frameworks already, but we do it explicitly anyway).

  • Click on the project in Xcode
  • select the target in the drop down at the top left
  • go to General tab
  • scroll down to Linked Frameworks and Libraries
  • add UIKit.framework
  • change Status from Required to Optional

It may cause all of UIKit to be weakly linked, which may affect performance. Not sure.

In Objective-C, even with Required, the compiler automatically weak-linked the symbols whose availability is above the deployment target, and leave the others strongly linked. I don't know why this doesn't work with Swift.



回答5:

According to Apple, there are no preprocessor calls in Swift, but Swift code can be conditionally compiled based on the evaluation of build configurations in two functions. For now, we get os() for a choice of OSX or iOS and arch() for a choice of x86_64, arm, arm64 or i386. So, you can evaluate #if os(iOS), but not #if os(iOS8) (though that does seem like a good idea for the further versions of Swift).



回答6:

You can check iOS version by -

let iosVersion = UIDevice.currentDevice().systemVersion

And compare it using 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
}

You can also use the new NSProcessInfo APIs, but they're unavailable for iOS 7.

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

You can see more details here. Hope this solves your query.



标签: ios macros swift