Xcode 13.2 Beta release notes features a promise for Swift Concurrency support for iOS 13.
You can now use Swift Concurrency in applications that deploy to macOS 10.15, iOS 13, tvOS 13, and watchOS 6 or newer. This support includes async/await, actors, global actors, structured concurrency, and the task APIs. (70738378)
However, back in Summer 2021 when it first appeared at WWDC it was hard constrained to be run on iOS 15 only.
My question is: what changed? How did they achieve backwards compatibility? Does it run in any way that is drastically different from the way it would run in iOS 15?
CodePudding user response:
Back-deploying concurrency to older OS versions bundles a concurrency runtime library along with your app with the support required for this feature, much like Swift used to do with the standard library prior to ABI stability in Swift 5, when Swift could be shipped with the OS.
This bundles parts of the Concurrency portions of the standard library (stable link) along with some additional support and stubs for functionality (stable link).
This bundling isn't necessary when deploying to OS versions new enough to contain these runtime features as part of the OS.
Since the feature on iOS 15 (and associated OS releases) was stated to require kernel changes (for the new cooperative threading model) which themselves cannot be backported, the implementation of certain features includes shims based on existing functionality which does exist on those OSes, but which might perform a little bit differently, or less efficiently.
You can see this in a few places in Doug Gregor's PR for backporting concurrency — in a few places, you can see checks for SWIFT_CONCURRENCY_BACK_DEPLOYMENT
which change the implementation where some assumptions no longer hold, or functionality isn't present. For example, the GlobalExecutor
can't rely on getting a global Dispatch queue as its main shared queue when backporting, and has to create its own for use. @objc
-based actors also need to have their superclass swizzled, which doesn't need to happen on non-backdeployed runtimes. (Symbols also have to be injected in some places into the backdeploy libs, and certain behaviors have to be stubbed out, but that's a bit less interesting.)
Overall, there isn't comprehensive documentation on the exact differences between backdeploying and not (short of reading all of the code), but it should be safe to assume that the effective behavior of the backdeployed lib will be the same, though potentially at the cost of performance.