Why your Xcode version still matters with Module Stability

Why your Xcode version still matters with Module Stability

Β·

3 min read

In this blog post, you learn about Swift's Module Stability and a common misconception about its compatibility with older Xcode versions:

You will know how to deal with the following error when trying to use a binary framework (.xcframework).

Error Message

You heard about Module Stability in Swift and the promise of compatibility across compiler versions. You think the vendor of the binary framework did a mistake and you are ready to contact them.

Stop. The error here is correct because you cannot use a binary framework in an app that uses an older compiler (Swift 5.6.1 == Xcode 13.4) than the one used to generate the binary framework (Swift 5.7 == Xcode 14).

Does not work

The vendor of the binary framework should have documented:

  • What is the minimum OS version the library supports?
  • What is the minimum Xcode version the library supports?

Let's step back and check the exact promise of Module Stability.

Great sources about Module Stability are

In essence:

  • Module stability allows Swift modules built with different compiler versions to be used together in one app.
  • Library evolution support allows developers of binary frameworks to make additive changes to the API of their framework while remaining binary compatible with previous versions.

Binary frameworks built with Module Stability have Swift Module Interfaces, a text-based definition of their framework's APIs as part of generated .swiftinterface and .swiftmodule files.

And since they behave like source code, future versions of the Swift Compiler will be able to import Module Interfaces created with older versions.

Works

Module interfaces aren't backward-compatible, only forward-compatible.

To be precise:

Module interfaces generated by a given version of the Swift compiler are guaranteed to be imported by any future Swift compiler that supports the -swift-version that it was compiled with.

This is because the @inlinable code in the .swiftinterface file may be using a new syntax that is unknown to older compilers. [Source]

I also found this interesting explanation in this great Apple developer forum answer that explains the relative terms of backward- and forwards-compatibility.

Part of what might have confused you is the specific language used to discuss these issues. The language is important, because the concept of backward- and forwards-compatibility are relative terms, and the direction of that relativity between compiler and library matters.

The following are equivalent statements with the directionality flipped, and accurately represent what will work:

  • A new compiler is backward-compatible with the textual interface of a binary compiled by an older compiler.
  • An older binary's textual interface is forward-compatible with newer Swift compilers.

That is not the same as these statements, both of which are incorrect and will not work:

  • An older compiler is forwards-compatible with the textual interface of a binary complied by a newer compiler.
  • The textual interface for a binary from a newer compiler is backward-compatible with older Swift compilers.

By the way: if you wanna look up which Swift version is used in an Xcode version then you can use the following website as a reference:

Did you find this article valuable?

Support Marco Eidinger by becoming a sponsor. Any amount is appreciated!