Swift.org blog: ABI Stability and More

There is a new Swift.org blog post available titled "ABI Stability and More", authored by @jrose. From the post:

This post describes what binary compatibility means in Swift 5 and how it will evolve in future releases of Swift.

The post also covers related topics such as module stability and library evolution.

If you have questions related to the blog post, feel free to ask them on this thread!

25 Likes

Thanks for this, but I think the main concern from the other thread was the situation of a hypothetical Swift 6 App wanting to deploy to an OS with built-in Swift 5 libraries.

As well as saying that we won't have to bundle the Swift libraries with Apps any more, there doesn't seem to be a clear statement that using cutting-edge language features may restrict deployment options in the future.

I assume Apple will make some kind of reasonable effort to back-port what they can (as happened with ARC), but we don't know for sure. It's really a policy issue for Apple to address with their developer community (rather than a Swift project issue), but lots of people here deploy to Apple platforms so they'd like to know what this will mean in practice.

7 Likes

In practice, I think the availability of language features for "backwards deployment" depends on whether those features can be built on the runtime support that is available — or could be made available retroactively with reasonable confidence and effort. Further, not all language features require additional runtime hooks. In the case where they did, creative solutions may arise — such as the one you mention for Objective-C ARC — that depend on the importance of the feature to users and the difficulty/feasibility of retrofitting the needed runtime support. In some cases retrofitting the runtime support may be quite doable, and other cases it won't be.

It will really come down to the concrete cases that we will encounter in the future. Generally speaking everyone recognizes that most app developers don't raise their minimum deployment target for their apps to always match the latest OS release, and language features that depend on runtime availability could lag in adoption (which is not great) if they are gated on what OS the app is running on.
All of us involved with Swift want to make it successful and a real asset to the developers who use it, and we will continue to explore technical approaches to evolving the language that maximum its impact on its users. I know that's a vague statement, but ultimately the "reasonable effort to back-port" and other possible options all are derived from that intent. To be clear that's not a promise that all language features can or will be backported to work with earlier runtimes; it's just a recognition that it is an important thing to always consider as the language evolves.

18 Likes

And what about the immediate future, the one we have to plan for? Will Swift 5 be able to build an app that targets iOS 11, and runs on iOS11, 12, 13, etc, for example?

I think we've been pretty clear about that. If you build an app with a minimum deployment target of iOS 11 or 12, it will be bundled with the Swift 5 library. If that app happens to find itself running on iOS 12.2 or later, it will use the OS library over the bundled library, and the App Store will take advantage of that to "thin" the bundled library out of downloads to such OSes. But it will run on any OS that's at least the OS you set as the minimum deployment target.

If Swift 5 apps couldn't deploy backwards or forwards, we, er, would never have taken the technical approach that imposed that constraint because it'd be totally unacceptable.

20 Likes

That's crystal clear. Thanks, John!

1 Like

Thank you both for the clarifications! Super excited about Swift 5 and what's to come.

1 Like

Thanks for the big milestones that will help getting swift closer to SDK developers.

I'm a SDK developer myself and I have a question about Module Stability, Will having a Swift 5 core with an Objective-C wrapper work for future versions of Swift as substitute for Module Stability?

1 Like

Yes, that should be fine too! In that case, the Objective-C layer serves as your interface layer, and the Swift parts that have ABI concerns (the parts that interact with the Swift runtime) will be set.

1 Like

Hi,
Many thanks for the post! Two questions about Library Evolution:

This happens when Apple updates the libraries in an OS, but it’s also important when one company’s binary framework depends on another company’s binary framework. In this case, updating the first framework would ideally not require recompiling the second framework.

Shouldn't the last sentence be the other way round? If I read it correctly, the first framework is the client so ideally there should not be a need to recompile the first framework (the client) if the second framework changes.

And the second question: Can ABI stability be considered as implementation of Library Evolution, but limited to Swift Standard Library only?

Oops, you're exactly right about the quote. My bad. I'll fix it later today.

As for your second question, ABI stability doesn't actually cover Library Evolution for the standard library. As noted in that table, on its own it only lets you change the compiler, not the standard library implementation underneath it. For Apple's "Swift in the OS" story, we've implemented enough of Library Evolution in Swift 5 to use in the standard library already, so that it's forward-compatible in the way we plan any "resilient" library to eventually be.

1 Like

I'd like to do the same for a project, but how would you do that? As soon as I add a swift file to my Objective-C framework and build, a .swiftmodule file is added to the Modules folder in the built target.

Hm. I hadn't thought about this, but I guess you could manually delete the swiftmodule out of the Modules folder as a Run Script build phase. Can you file a Radar to suggest that this be added as a build setting? (It'll be less relevant when module stability is done, I guess, but not totally unreasonable.)

Done, thanks for the support.

1 Like

@yousefhamza @jrose here's what I've done so far, can you help me on this?

To clearly state what my issue is, I want to avoid the following problem:

  • I release a (closed-source) framework that contains Swift 5.0 code;
  • some developer adds the framework to their app;
  • Swift 5.1 is released;
  • the same developer updates the app to Swift 5.1;
  • the developer must also update the framework to a version compiled with Swift 5.1;
  • I'm forced to immediately release a Swift 5.1 update for my framework.

My understanding is that ABI stability will prevent this need from a runtime point of view, but without module stability Xcode will tell the developer that my framework needs to be built with Swift 5.1 to work in its Swift 5.1 app.

Now, to avoid this problem I can release an Objective-C framework in which the Objective-C part is just the public interface, plus an implementation file that calls the underlying Swift code. In trying to do this, I've run in 2 problems:

  • to be able to call the Swift code from the .m Objective-C file I need to make all the needed Swift code public, so that it appears in the <$PRODUCT_NAME/$PRODUCT_NAME-Swift.h> header, that I'm going to import in my .m file;
  • the built .framework package still contains lots of references to the fact that Swift is there;

The first issue is not really a big deal: I can simply expose just a single Swift class to the Objective-C runtime, and call the real code from that class.

The second issue seems more problematic, also because I'm not sure which generated files will affect module stability, and which won't. In the built .framework package there are exactly 3 places where Swift is referred to:

  • the $PRODUCT_NAME.swiftmodule folder;
  • the $PRODUCT_NAME-Swift.h header;
  • within the module.modulemap file.

I added a script in the last build phase to delete the first two, and edit the module.modulemap to remove the lines that describe the Swift module. I then tried to embed the framework in two example projects, one in Swift and one in Objective-C, and everything seems to be working fine: I'm able to call both import $PRODUCT_NAME and #import <$PRODUCT_NAME/$PRODUCT_NAME.h> and call the code with no error, both during compilation and runtime.

Does all this make sense? :smiley:

You should be able to avoid the module map editing by turning off "Install Objective-C Compatibility Header", but I have a vague memory of there being an Xcode bug where it adds the Swift section anyway. Other than that, this does seem like it should work.

I cannot do this because if I do I cannot #import <$PRODUCT_NAME/$PRODUCT_NAME-Swift.h> in the .m file to call the underlying Swift code.

Ah, in that case the header is not part of the API of your framework, so you would "just" use #import "$PRODUCT_NAME-Swift.h".

I was wondering if it is safe to assume that patch versions of swift (5.0.1 vs 5.0, 4.2.1 vs 4.2) are already always both ABI and Module stable with their parent minor version.

It is important for us for binary caching purposes (e.g. do we have to recompile a binary swift framework when moving from Xcode 10.2 to 10.2.1).

No, it is not safe to assume that.