ma11hew28
ma11hew28

Reputation: 126309

Swift: iOS Deployment Target Command Line Flag

How do I check the iOS deployment target in a Swift conditional compilation statement?

I've tried the following:

#if __IPHONE_OS_VERSION_MIN_REQUIRED < __IPHONE_8_0
    // some code here
#else
    // other code here
#endif

But, the first expression causes the compile error:

Expected '&&' or '||' expression

Upvotes: 11

Views: 12983

Answers (5)

pd95
pd95

Reputation: 2612

I have come across this issue recently when building a library whose source code supports multiple iOS and macOS versions with different functionality.

My solution is using custom build flags in Xcode which derive their value from the actual deployment target:

TARGET_IOS_MAJOR = TARGET_IOS_MAJOR_$(IPHONEOS_DEPLOYMENT_TARGET:base)
TARGET_MACOS_MAJOR = TARGET_MACOS_MAJOR_$(MACOSX_DEPLOYMENT_TARGET:base)

Referring to those user defined settings in Others Swift Flags like:

OTHER_SWIFT_FLAGS = -D$(TARGET_MACOS_MAJOR) -D$(TARGET_IOS_MAJOR)

allows me to check for the actual major OS version in my Swift sources as follows:

#if os(macOS)
    #if TARGET_MACOS_MAJOR_12
        #warning("TARGET_MACOS_MAJOR_12")

    #elseif TARGET_MACOS_MAJOR_11
        // use custom implementation
        #warning("TARGET_MACOS_MAJOR_11")
    #endif

#elseif os(iOS)
    #if TARGET_IOS_MAJOR_15
        #warning("TARGET_IOS_MAJOR_15")

    #elseif TARGET_IOS_MAJOR_14
        #warning("TARGET_IOS_MAJOR_14")

    #else
        #warning("older iOS")
    #endif
#endif

Currently I don't know if a similar approach would be possible in a SPM package. This is something I will try to do in a later phase.

Upvotes: 1

Mihai Fratu
Mihai Fratu

Reputation: 7663

I know your question is been here for a while but just in case someone's still looking for an answer they should know that starting with Swift 2.0 you can do something like this:

if #available(iOS 8, *) {
    // iOS 8+ code
} else {
    // older iOS code
}

You can read more about it here.

Upvotes: 0

Andrej
Andrej

Reputation: 7416

Tested in Swift 2.2

By saying Deployment Target you mean iOS version, or App Target? Below I'm providing the solution if you have multiple versions of the app (free app, payed app, ...), so that you use different App Targets.

You can set custom Build configurations:
1. go to your project / select your target / Build Settings / search for Custom Flags
2. for your chosen target set your custom flag using -D prefix (without white spaces), for both Debug and Release
3. do above steps for every target you have

enter image description here

To differentiate between targets you can do something like this:

var target: String {
    var _target = ""
    #if BANANA
        _target = "Banana"
    #elseif MELONA
        _target = "Melona"
    #else
        _target = "Kiwi"
    #endif
    return _target
}

override func viewDidLoad() {
    super.viewDidLoad()
    print("Hello, this is target: \(target)"
}

Upvotes: 7

Vincent Guerci
Vincent Guerci

Reputation: 14419

TL;DR? > Go to 3. Solution

1. Preprocessing in Swift

According to Apple documentation on preprocessing directives:

The Swift compiler does not include a preprocessor. Instead, it takes advantage of compile-time attributes, build configurations, and language features to accomplish the same functionality. For this reason, preprocessor directives are not imported in Swift.

That is why you have an error when trying to use __IPHONE_OS_VERSION_MIN_REQUIRED < __IPHONE_8_0 which is a C preprocessing directive. With swift you just can't use #if with operators such as <. All you can do is:

#if [build configuration]

or with conditionals:

#if [build configuration] && ![build configuration]

2. Conditional compiling

Again from the same documentation:

Build configurations include the literal true and false values, command line flags, and the platform-testing functions listed in the table below. You can specify command line flags using -D <#flag#>.

  • true and false: Won't help us
  • platform-testing functions: os(iOS) or arch(arm64) > won't help you, searched a bit, can't figure where they are defined. (in compiler itself maybe?)
  • command line flags: Here we go, that's the only option left that you can use...

3. Solution

Feels a bit like a workaround, but does the job: Other Swift flags

Now for example, you can use #if iOSVersionMinRequired7 instead of __IPHONE_OS_VERSION_MIN_REQUIRED >= __IPHONE_7_0, assuming, of course that your target is iOS7.

That basically is the same than changing your iOS deployment target version in your project, just less convenient... Of course you can to Multiple Build configurations with related schemes depending on your iOS versions targets.

Apple will surely improve this, maybe with some built in function like os()...

Upvotes: 13

Dylan Gattey
Dylan Gattey

Reputation: 1724

You can't do it in a conditional compilation statement like that. "Complex macros" as Apple calls them are not supported in Swift. Generics and types do the same thing, in their mind, with better results. (Here's a link they published https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/BuildingCocoaApps/InteractingWithCAPIs.html#//apple_ref/doc/uid/TP40014216-CH8-XID_13)

Here's a function I came up with that accomplishes the same thing (and obviously just replace the string returns with whatever is useful for you like a boolean or just the option itself):

func checkVersion(ref : String) -> String {
    let sys = UIDevice.currentDevice().systemVersion
    switch sys.compare(ref, options: NSStringCompareOptions.NumericSearch, range: nil, locale: nil) {
    case .OrderedAscending:
        return ("\(ref) is greater than \(sys)")
    case .OrderedDescending:
        return ("\(ref) is less than \(sys)")
    case .OrderedSame:
        return ("\(ref) is the same as \(sys)")
    }
}

// Usage
checkVersion("7.0") // Gives "7.0 is less than 8.0"
checkVersion("8.0") // Gives "8.0 is the same as 8.0"
checkVersion("8.2.5") // Gives "8.2.5 is greater than 8.0"

Upvotes: 0

Related Questions