5

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.

Community
  • 1
  • 1
CodeSmile
  • 64,284
  • 20
  • 132
  • 217
  • I don't even know how this would be possible. Theres some significant syntax changes and logic changes. – Aggressor Aug 06 '15 at 18:03
  • It's really, really straightforward in any decent language: you just #ifdef based on defines provided by the compiler/system library - or the equivalent thereof. Alas, Swift gives a %&§$! – CodeSmile Aug 06 '15 at 18:17
  • 2
    In Swift, even the "inactive" parts of #if/#else/#endif have to be syntactically valid, so I don't think it is possible. Similar/related: http://stackoverflow.com/questions/29140476/how-can-i-programmatically-find-swifts-version, http://stackoverflow.com/questions/27242690/gcc-version-equivalent-for-swift-code. – Martin R Aug 06 '15 at 18:23
  • Unity3D does this a lot but from what I was reading in the 7.4 beta notes I had a feeling it wouldn't honor anything like this. They don't even have variable name refactoring yet... – Aggressor Aug 06 '15 at 18:41
  • Even if this was possible (and I don't believe it is currently), I think the amount of time you would end up maintaining two codebases in one would outweigh the added flexibility of being able to compile in Xcode 6 or 7. My advice would be to hold onto Swift 1.2 until you can make the leap to Swift 2/Xcode 7 completely. – Andrew Ebling Oct 19 '15 at 10:09
  • Apple doesn't want us to use Swift 1.x in any way. They don't want to support it. Swift 1.x was like a beta version. Use either Swift 2 only or have two versions of your code. You don't have other possibilities. – Sulthan Oct 19 '15 at 11:11

2 Answers2

5

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.

Daniel Dunbar
  • 3,465
  • 1
  • 21
  • 13
0

Having 2 versions of the code inside your project won't work since the code would not compile. There is no compiler directive for conditional compiling based on a version.

There is one workaround that could work (did not test it)

First create 3 files named version_current.swift, version_1_2.swift and version_2.swift. Make sure that only version_current.swift is part of your build target.

Then create a new build script phase and place it right above the 'compile sources' phase. In that script you will copy over the content of either the 1_2 or the 2 version over the current.

My scripting knowledge is not so good, so I can't give you much help doing this. You can get the version with code like:

$ xcrun swift -version

And then just execute a copy statement.

But then this will only work for the default Xcode version on your system. When you want to use a different version, you also have to change the default version.

Edwin Vermeer
  • 13,017
  • 2
  • 34
  • 58