Swift compatibility between versions for a library - ios

I'm distributing libraries for other developers to use (http://empiric.al). I've noticed that between swift versions, even 2.0 to 2.1, I'll get Module file was created by a (newer/older) version of the compiler.
I need to be distribute in a future-proof way.
How can I make sure my compiled frameworks can be used by newer versions of Swift in the future so I don't have to recompile as soon as Apple puts a new beta out?

From Apple's website:
Binary Compatibility and Frameworks
While your app’s runtime compatibility is ensured, the Swift language
itself will continue to evolve, and the binary interface will also
change. To be safe, all components of your app should be built with
the same version of Xcode and the Swift compiler to ensure that they
work together.
This means that frameworks need to be managed carefully. For instance,
if your project uses frameworks to share code with an embedded
extension, you will want to build the frameworks, app, and extensions
together. It would be dangerous to rely upon binary frameworks that
use Swift — especially from third parties. As Swift changes, those
frameworks will be incompatible with the rest of your app. When the
binary interface stabilizes in a year or two, the Swift runtime will
become part of the host OS and this limitation will no longer exist.

Until the Swift ABI (application binary interface) stabilises (I'm guessing another year or two) the only way to distribute libraries that will work across different Xcode versions is to distribute the source code. Cocoa pods and Carthage are both good tools for making library distribution easier but for Swift code they will still rely on source code being available.
It might be possible to have an Cocoapod that detects the version of Xcode it is run with and then downloads and provides the correct build of your library but you will still need to build the libraries for all Xcode versions that you want to support and recompile every time Apple release a new Xcode but at least the user wouldn't need to download a new version manually.

Related

How to prebuilt libraries to be compatible with future swift versions

We are prebuilding some libraries (mainly with carthage) for some of our projects to reduce development times. These libraries are not updated very often, but we want to update our XCode versions pretty fast.
Now every time a new XCode brings a new swift version, we are seeing this incompatibility issue
File.swift:4:8: error: module compiled with Swift 5.3.2 cannot be imported by the Swift 5.4 compiler: /......./Debug-iphoneos/Alamofire.framework/Modules/Alamofire.swiftmodule/arm64-apple-ios.swiftmodule
How can I pre-build my dependencies in a way that a swift update wont affect it and I dont have to re-build the dependencies with every xcode update (I thought thats what ABI stability was for? How can I activate that?)
It sounds like you're misunderstanding what ABI stability enables. The main benefit is that it allows the OS to include only one version of the Swift standard library, and for all Swift binaries to share it. What you want is "module stability". From the docs on ABI stability:
ABI stability is about mixing versions of Swift at run time. What
about compile time? Right now, Swift uses an opaque archive format
called “swiftmodule” to describe the interface of a library, such as a
framework “MagicKit”, rather than manually-written header files.
However, the “swiftmodule” format is also tied to the current version
of the compiler, which means an app developer can’t import MagicKit if
MagicKit was built with a different version of Swift. That is, the app
developer and the library author have to be using the same version of
the compiler.
To remove this restriction, the library author needs a feature
currently being implemented called module stability. This involves
augmenting the opaque format with a textual summary of a module,
similar to what you see in Xcodeʼs “Generated Interface” view, so that
clients can use a module without having to care what compiler it was
built with.
This is not yet supported in any version of Swift.

Module XXX Was Not Built with Library Evolution Support

My iOS app has two dependencies (Alamofire and Charts) that it incorporates using Swift Package Manager.
When I build it, I get this warning wherever I import the modules in question, e.g.:
import Alamofire
Module 'Alamofire' was not compiled with library evolution support; using it means binary compatibility for 'YourApp' can't be guaranteed
What I don't understand is:
Why is this binary compatibility an issue, if my app builds the dependencies from source code using SwiftPM (not embedding frameworks distributed as binaries), and
What should I do about it?
The Alamofire repository's issue threads suggest that Library Evolution Support cannot be added to the current version because it hinders development, however it is supported in the previous major version (4). Perhaps that could be a workaround, but I don't want to perform the major code modifications that would result on my app from downgrading Alamofire.
I think the point is that with binary compatibility we don’t have to embed the Swift language frameworks into the app; the system Swift language frameworks will keep working for your app even if the language evolves in the future, without your having to recompile the app. Hence the name, library evolution.
So without that guarantee, the language will be embedded into the app, swelling its size and losing the other advantages of binary stability, such as the ability of your app to take advantage of faster code in the system frameworks when they improve things in the future, etc.
You can probably test that theory by building the app and looking inside it.
So if I’m getting this right, that is the sense in which Alamofire is holding you back, and explains the warning.

Is Swift ABI compatible between minor versions?

I know Swift is not yet ABI stable, but does that only count for major versions of Swift?
Is there any guarantee that minor or patch versions (under semantic versioning) of Swift are ABI stable?
I'm guessing there is no guarantee here, but just wanted to double check if anyone has come across anything detailing ABI stability for different minor/patch versions of Swift.
Also, if I use a Swift binary framework compiled with a different version of Swift I get a compiler error usually. If I don't get a compiler error in my project does that mean it is safe, or could there still potentially be runtime issues with a slightly different (patch version) of Swift?
Update 3
We also have module stability, starting with Xcode 11, with the help of the newly introduced .swiftinterface files. One caveat, though, is that the code will have to be build with the -enable-library-evolution flag. More details here.
Update 2 Module stability is scheduled for Swift 6: https://swift.org/blog/abi-stability-and-more/#module-stability
This is an excerpt from the Swift evolution repo.
Update Swift 5 comes with some ABI stability:
The Swift 5 release will provide ABI stability for the Swift Standard Library.
Unfortunately, not yet. For Swift 4, they state this here: https://swift.org/blog/swift-4-1-release-process/.
Swift 4.1 is not binary compatible with 4.0. It contains a variety of under-the-hood changes that are part of the effort to stabilize the Swift ABI in Swift 5.
Hopefully we'll get ABI stability in Swift 5
I think we should know what is ABI stability firstly, After that your confusion has already been removed.
Today, the latest version of Swift is 3.1, so chances are if you ship an app tomorrow, your app bundle will contain the Swift dynamic libraries for 3.1, however, there are plenty of apps in the store right now which link 3.0, 2.3, and probably even some older apps that link 2.1 and earlier. Nothing is stopping me from downloading your app (on 3.1) and my app (on 2.3) and running them side-by-side on my iPhone with iOS 10.3, since both apps link against their own bundled version of Swift. It's exactly the same as you bundling Alamofire 4.4 and while I bundle 3.0.
When a language is ABI-stable (Application Binary Interface), that means it is packaged and linked with the operating system itself, in this case: iOS. The Swift code you compile on your computer has a binary interface into the operating system itself rather than any dynamic library you bundle with your application. Because of this, Apple has to be able to guarantee that my Swift code, when compiled to machine code (bitcode, LLVM-IR, yada-yada), will be able to interface properly with the rest of the operating system, and (probably more importantly) will not break between versions of iOS / Swift.
As it stands today, the Swift language specification and compiler are not in a state where the Swift team would feel comfortable making this promise of ABI-stability; changes to Swift are still too frequent and the roadmap is still too long. As soon as the Swift library is merged into iOS, it becomes much much harder to make big changes.
Why does it matter?
Yes, the bundle size of your application will decrease because you will no longer have to include the Swift standard library in your Frameworks folder, which is nice.
Language changes will be smaller / less frequent, so you won't have to worry about events like migration from Swift 2 -> 3 (I'm still scarred from that)
Developers will be able to create 3rd-party libraries written in Swift and distribute pre-compiled frameworks (binaries), because they no longer need to bundle the Swift standard library into their framework, and will instead be linking against the same version of Swift as your app (the one packaged with iOS).

How to distribute Swift Library without exposing the source code?

The first thing I tried is to create a static library but later I found out that it's not supported yet. Apple Xcode Beta 4 Release Notes:
Xcode does not support building static libraries that include Swift
code. (17181019)
I was hoping that Apple will be able to add this in the next Beta release or the GA version but I read the following on their blog:
While your app’s runtime
compatibility is ensured, the Swift language itself will continue to
evolve, and the binary interface will also change. To be safe, all
components of your app should be built with the same version of Xcode
and the Swift compiler to ensure that they work together.
This means that frameworks need to be managed carefully. For instance,
if your project uses frameworks to share code with an embedded
extension, you will want to build the frameworks, app, and extensions
together. It would be dangerous to rely upon binary frameworks that
use Swift — especially from third parties. As Swift changes, those
frameworks will be incompatible with the rest of your app. When the
binary interface stabilizes in a year or two, the Swift runtime will
become part of the host OS and this limitation will no longer exist.
The news is really alarming for me a person who writes components for other developers to use and include in their apps. Is this means that I have to distribute the source code or wait for two years?. Is there any other way to distribute the library without exposing the code (company policy)?
Update:
Is Swift code obfuscation an option at this point ?
Swift is beta now, and even for 1.0 Apple has been pretty clear they're after a restricted feature set -- better to do a small number of things well than to try to do everything.
So for now, there's no way to distribute binary static libraries. Presumably that'll change sometime after Swift 1.0. For now, you can:
Distribute source
Ship a binary framework (instead of a library) if you're okay with the ABI being fragile
Use ObjC for library code
You can always combine approaches, too: e.g., implement the critical (secret) details of your library in ObjC, and ship Swift source that wraps it in a nice Swift API.
Obfuscating code written in a language that's very much subject to change sounds like a recipe for a maintenance nightmare.
I believe the whole approach is wrong. You cannot do something that is not (yet) doable by the technology you are trying to use.
Rationale: Swift is a new language, currently in Beta, and therefore changing. As I see it, this fact means not only that you are not able to ship static libraries, but that (real) developers will not be actually use third-party static libraries. What's the actual use of a library that may not work in the next release of the compiler? The issue gets bigger if you whant to use more than one library, because they might not be compatible! Therefore, even if you would be able to ship static libraries, they wouldn't be actually useful for production environment. So, what's the point?
Suggestion: write your static libraries in Objective-C (or C or whatever "non-beta"). Developers who need third-party libraries (e.g. yours) shouldn't expect them to be written in Swift until Swift is stable. You don't use experimental materials to build real bridges, right? You use well-tested, predictable ones.
From Xcode 9 beta 4, Xcode supports static library with Swift sources.

Secure 3rd party dependencies on iOS

So on iOS Apple won't let you release an application that has a dynamic library (dylib). (Xcode won't compile it although there are workarounds, and the app store won't approve it.)
We have an SDK that we are currently moving from Windows to iOS. On Windows, we statically link all of our dependencies into one dll, so we only have to provide 1 file to anyone using the dll, so we don't have to publicly release all of the libraries that we are using and to keep the size of that file small, since any parts of those libraries that we aren't using get compiled out.
On iOS, since it's not possible to release an application that loads a dynamic library, we would be forced to ship a framework or something like that, which includes all of the libraries that we link against. The developer consuming our sdk would then compile our lib with all of its dependencies into their application.
This would let anyone using our SDK know what libraries we're using and would increase the size of our deliverable, since it would have to include full version of all the libraries we're linking against. (At least with a framework we would still only be providing 1 "file".)
Is there any way that Apple allows to avoid letting everyone know what libraries we're using?

Resources