Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to conditionally compile version-specific Swift (1.2 vs 2.0) code in the same source file?

I have a Swift demo project that comes bundled with my framework. I want to ensure that the Swift code in the demo compiles successfully with both Xcode 6 (Swift 1.2) and Xcode 7 (Swift 2.0) without user intervention.

Since there's only marginal preprocessor support in Swift, how can I determine at compile-time which version of Swift or Xcode is being used to compile the code?

Now, here's the important detail:

  • It has to work automatically!
    • Open the project in Xcode 6 -> compiles the Swift 1.2 code.
    • Open the project in Xcode 7 -> compiles the Swift 2.0 code.

No build settings or other means that require the user to specify, one way or another, which Swift/Xcode version she is using.

I keep thinking: this is such a trivial task, how could that not be possible with Swift?

As a framework developer this is driving me nuts since a successful compile of a Swift project now entirely depends upon the user's version of Xcode, and I can't ask them all to "update to Xcode 6.4" and at a later point having to ask them all over again to "update to Xcode 7.1". This is insane!

The alternative would of course be to have separate demo projects, managing different code bases, one for each version of Swift. And hoping the user will know what project will work with her version of Xcode. Not a real alternative.

The other alternative, to simply not use any of Swift 2.0's enhancement, is unfortunately not possible either. There is syntax, classes and methods that won't work in one or the other Swift version, if only due to the compiler being more picky in newer Xcode versions.

like image 786
LearnCocos2D Avatar asked Aug 06 '15 17:08

LearnCocos2D


1 Answers

You can accomplish this using some of Xcode's advanced build settings, in particular:

  • XCODE_VERSION_MAJOR: Which encodes the Xcode major version as a string like "0700".
  • EXCLUDED_SOURCE_FILE_NAMES: A "fnmatch"-style pattern of source files to exclude by default.
  • INCLUDED_SOURCE_FILE_NAMES: A "fnmatch"-style pattern of source files to include.

I would not generally recommend doing this, as it will make your project hard to understand for most Xcode users, but if you absolutely want to make it work you can use this technique.

The way you accomplish it is as follows:

  1. For any source files which need to be versioned, name them something like "Thing-Versioned-0600.swift" and "Thing-Versioned-0700.swift". Make sure both files are in the sources build phase.

  2. Use the excluded mechanism to prevent any versioned files from being compiled by default, by adding a project-level build setting: EXCLUDED_SOURCE_FILE_NAMES = *-Versioned-*.swift.

  3. Use the included mechanism to only add back in files that match the current Xcode major version, by adding another project-level build setting: INCLUDED_SOURCE_FILE_NAMES = *-Versioned-$(XCODE_VERSION_MAJOR).swift.

like image 99
Daniel Dunbar Avatar answered Nov 04 '22 04:11

Daniel Dunbar