Concurrency can definitely definitely "work" on earlier OSs – it is how it was implemented in the pre-Monterey toolchains – but as you note, it would then be subject to the risk of thread explosion. Whether that is an acceptable risk (both the performance issue, and the very difficult-to-manage situation where thread explosion is a risk but only when back deploying) needs some discussion.
It's important to understand this is not the only issue though. Just the biggest, easiest-to-explain one. There are many others – similar to ones that prevented opaque results types from back-deploying as Doug mentions. We're not aware of any reasons these can't be overcome with work, but often with these things, sometimes you only really determine that for sure by undertaking the implementation.
As someone relatively new to the Apple development ecosystem, these sort of technical justifications just don't make sense.
On Mac, Linux, and Windows, when...
Node added support for async/await, I upgraded the node runtime and I now have async/await
Rust added support for async/await, I upgraded the rust toolchain and I now have async/await
.NET added support for async/await... etc, etc.
The thing that seems to make Swift unique in this regard is Apple's support model. The swift toolchain is distributed with XCode, and recent XCode releases are only supported on recent releases of MacOS. Not supporting earlier versions of an OS are not technical limitation, they are a support limitation.
To further my point, these limitations don't seem present on non-Apple operating systems. Why? Because they're not technical limitations. User's are free to upgrade the runtime.
And to be clear, I'm not saying Apple's conservative support model is wrong, but it does appear to be what is driving this limitation, not any specific technical challenge in upgrading a runtime on older versions of MacOS/iOS.
I figured as much, and that is what I fear. What people won't realized is that they want to eat cake and have it too.
If the back-deploy ends up with unacceptable performance (it'd have to be on top of GCD, so it probably won't do better than GCD). We would just set back the adoption all the same, if not worse. We tried, it sucks, let's not use it for the time being, i.e., indefinitely is a scenario I can totally see.
To add another data point, I am totally fine with performance being "bad" for older OS versions, as long as I am aware I could limit use of it in ways that took potential slowness into account. Would just like to be able to use the new constructs (especially actors) all the time going forward and let people with older devices have somewhat slower experiences.
I thought at least async/await could be deployed backwards.
It's a little sad that this issue was not fully discussed earlier in the stage, and may be far more severe than it seems. I hope I'm wrong.
For the record, I love Swift. I love it so much that I fear this would be a huge give-away in the competition. There won't be too many windows for this language to miss towards its goal of world domination. Just personal thoughts.
Node runs in a virtual machine. You’ve given up deep OS integration and no small bit of performance to get there. It’s a trade off.
Rust doesn’t have ABI stability. You would need to package the whole framework stack in each app. Swift did that before ABI stability and the size of app binaries was problematic. It’s a trade off.
In a new version of the .NET frameworks, which sit on top of the OS. You probably have several such versions installed on any given system. If you’re using apps that are built for different .NET versions, you’re paying the overhead of having all of those redundant runtimes loaded. It’s a trade off.
Your conclusions are incorrect. Swift has strong technical reasons for being deeply integrated into the OS. Performance and binary compatibility are two big ones.
Again, this is incorrect. On Linux, Swift provides neither a guarantee of ABI stability nor any level of deeper integration into the OS. It sits on top of the OS and folks might have a bunch of different Swift runtimes sticking around. It’s a trade off.
Your conclusions are incorrect.
I know it’s tempting to want to make this about some policy decision where enough reasoning would just change someone’s mind and all of a sudden everything Just Works. It doesn’t work that way. There are real engineering tradeoffs here, made by engineers with deep expertise in optimizing platforms and supporting their evolution over many years.
As I said, I get it. Concurrency is cool and you’d like to be able to use it on older OS’s. We hear you. But we ask that you listen to us, too, and not trivialize our work by dismissing our factual and verifiable statements about the technical challenges this request involves.
Doug, Ted, Ben,
Thanks for taking the time to explain the issue, both your posts and the Behind the Scenes talk have helped a lot in understanding the issues.
Perhaps this discussion could move to a more practical theme of what, if anything, the Swift community can do to support backporting to earlier macOS/iOS versions. I assume the plan is that the concurrency features of swift 5.5 will be ported to other platforms such as Linux or Windows, so should backporting pre macOS12/iOS15 be considered as a port to another platform too ?
I appreciate the detailed response. To summarize, on Apple platforms:
Apps cannot statically link the swift runtime
iOS/macOS does not support multiple versions of the swift runtime
Some language features rely on new iOS/macOS features
The concurrency updates impose ABI changes (this one is common to all platforms)
Is the implication here that supporting older versions of macOS/iOS would, at the very least, require updating the runtime and all apps on the device?
But we ask that you listen to us, too, and not trivialize our work by dismissing our factual and verifiable statements about the technical challenges this request involves.
I apologize. Your explanation makes sense. What I will say is that Swift imposes far more restrictions on Apple platforms than any other platform it supports, and it's a combination of those restrictions that lend to the difficulty in supporting older Apple platforms. And I'm not saying there aren't good justification for these restrictions (they are trade-offs, as you point out, rooted in what appears to be an absolute commitment to performance on Apple platforms), but it still defies expectations for a cross-platform language to have such restrictions on any platform it supports (macOS in particular, iOS less so).
TL;DR: I suppose there could be a new iOS 14 release which bundles the new runtime and model to, at least, make the transition a lot more consistent.
Thinking less of bundling the runtime with the app (requiring so much back-patching work), we are still happy to see an iOS 14.8 with runtime support for Swift Concurrency. Updating Swift runtime in a minor iOS release is technically possible, and we’re actually used to it (the integrated Swift runtime itself is also introduced in a minor release). This will also help evolving iOS 14 by allowing users to stay with it and upgrade to iOS 15 at any time they want.
If Apple does choose this path, we’d better not expect this on iOS 13 and earlier. In a current model, if we set the minimum deploy target to iOS 14.8, we can ensure that any device that meets the requirement supports Concurrency. If we set it to an earlier one, eg. iOS 12.7, the requirement would break. Still, it may be a tough decision to give up users staying at iOS 13 and earlier. That’s a trade-off.
P.S. I assume, personally, that the “Known issue” of Xcode is suggesting that there should be lower deployment targets that support Concurrency. Very likely iOS 14.8, macOS 11.6, etc. from my perspective.
Please don’t speculate. You’re making assumptions that are technically incorrect and then drawing unfounded conclusions from them.
Please don’t speculate. Your post could easily be misread as authoritative by someone who wanders over here and doesn’t read through the 100+ messages.
No. It requires bundling some code in apps and back-patching older Swift runtimes to cope with the new model.
Back-porting requires work deep in the runtime and compiler. Much of that work is understood. I don’t think this discussion will produce new insight into that work; the insight will come from doing it.
The best thing the Swift community can do for the concurrency effort is to try out the features on their own code. Even if you can’t ship it. Find the rough spots so we can smooth over them. Find the latent bugs, the design oversights, the ideas that are hard to communicate. Write the tutorials that make all of this more accessible to more people. We have time to make the model even better.
Concurrency is already supported on these platforms as part of 5.5. They don’t benefit from OS integration, so their performance characteristics aren’t as well understood.
Not really. The tricky part of back-porting is the back-patching. It’s a different set of concerns than bringing up an implementation on a new platform.
It looks that this is a welcome move: many people were also disappointed because there was no WWDC announcement for the M1 successor, new large iMacs, new yummy macBooks, etc. Many people are drooling (and I'm quite eager to replace my 5-years-old machine as well).
So we expect machines that run really fast, and consume really little energy, while they run the applications we write.
But who's responsible for this efficiency? Sometimes we developers are pretty good at writing efficient concurrent code. And sometimes it is the operating system that runs our mundane code very efficiently, finely tuning the allocation of its tightly constrained resources.
Absolutely all Swift concurrency features are about giving the compiler a lot of information about the architecture of our asynchronous code, much more information than raw threads or libDispatch could give. This is how we can profit from better low-level implementation strategies. Those strategies are the product of the collaboration between the Swift compiler and some OS-provided runtime.
With such an architecture (language features that drill down to the kernel), we can even expect performance enhancements with future OS releases. That's something I'd call an advantage. I'd even say that's something many users are actively asking for (even those who complain that they have to wait until they can use Swift concurrency features).
Would limiting try/await support to a "simple" desugaring of nested closures when deploying back to an unsupported OS runtime something that the team could consider? For example:
func f() async -> Int {
let result = await a()
return result + 1
}
Could be compiled as:
func f(_ completion: @escaping (Int) -> Void) {
a { result in
completion(result + 1)
}
}
Understandably, this may not work with async sequences or loops. But this also won't have any worse performance than the equivalent code we write today.
Or this wouldn't make much sense, will require a significant amount of work to implement, and generally doesn't feel like it's going to bring any meaningful value?
What's the exact reason for not backporting SDKs on older versions? App bundle size? Seriously? Why can't you let me decide how many megabytes my app will weight? I can decide do I need this feature and increase app bundle size, or I will make something like Clips for critical functions. But let me as a developer those runtimes to include during app downloading\installation. This is just so simple, but people have to wait for years until they start using async\awaits, the latest SwiftUI. Not a funny way of promoting our platform. I literally need to go and watch WWDC2019 or even WWDC2018, to know about new features I could use this year, lol.
Another dumb feeling is updates of SwitfUI. Okay, now you can use second iteration of it, targeting at iOS14. But in iOS 15, i.e. 3rd iteration we have a lot of fixes. Why would I wait for them one more year before I can use them? You can't substantiate it as an "adaptation".
Have the same opinion here. Like many companies, we still support iOS 12 right now and will bump the minimal deployment target to iOS 13 in august.
But the reality is that we don't need to support iOS 13.0, we have to support the latest iOS 13 version. So including the latest Swift Runtime to the latest versions of iOS 12 / 13 / 14 deal with the problem in many cases.
And not related to the topic, but it will be good to do the same with SwiftUI to get the latest API on the latest versions of iOS 13 / 14.
It is not correct referring the idea, that 85% is a large number and that's why it os ok.
For business even 1% of users can be critical, that's why we still support iOS 12. Those users, finally, forms our salary.
Niave question: Would it be possible to allow an app to bundle a specific swift version/runtime with an app in cases where the app developer is personally willing to pay the size cost?
(Or are there technical assumptions which have been made post stable-API now that no longer allow that without breaking things?)