On my Misalignment with Apple's Love Affair with Swift
During my life as a software engineer there only have been a few occasions where the design and principles of a language struck me as inherently beautiful and consequently inspired me to work with them.
- Lua for its simplicity, modularity and clear design goals that are followed through.
- Objective‑C / Cocoa for its very simple object oriented extension to C, with a great tradeoff of being a consistent and clear while still having optimal inter-op to the existing world of C and C++. And of course because of Cocoa and its design principles, purpose and empowerment.
- Erlang/OTP for being the most powerful implementation of the actor model, its concept of lightweight processes, and its ease to build distributed system. And due to being purely functional, the eye opening benefit of always having all the state necessary to analyze and fix any crash.
- Ruby (on Rails) for being an going all in, everything is an object, dynamic, object oriented scripting language. Rails for adding fantastic model view controller abstractions on top of it, and for such great use of the dynamism of the language. Amongst other parts, I'm still in awe of Builder for the XML view part.
None of those languages / language framework combos are perfect. But they all have one thing in common: A well defined set of carefully chosen design goals that are tailored towards certain usage scenarios. And those goals give guidance when using it, move you towards consistency and greatness when writing in and for them.
So now contrast that to Swift. First of all: Which question did it desire to answer? Think about it. There is no one clean answer. It just wanted to be better, more modern, the future – the one language to rule them all. A first red flag for anyone who ever tried to do a 2.0 rewrite of anything. From the outset, it wanted to be a silver bullet:
- It should scale from App/UI language down to system language.
- It should inter-op with existing Foundation and Cocoa, but still bring its own wide reaching standard library, adding a lot of duplication.
- It is functional, object-oriented and protocol oriented all at the same time.
- It wants to be strongly typed, but at the same time so convenient in type inference, that it falls over its own feet while trying to grasp simple expressions, and they become too complex to manage. By design.
- It is compiled and static, but emphasized the REPL and playground face that makes it want to look like a great scripting solution. Which it isn't.
- It seems to have been driven by the needs of the compiler and the gaps that needed to be filled for the static analyzer. Those seem to have been super-charged instead of catering to app developer's actual needs: efficient, hassle free, productive (iOS) App development.
- It is meant to offer progressive disclosure and be simple, to be used in playgrounds and learning. At the same time learning and reading through the Swift book and standard library is more akin to mastering C++. It is quite unforgiving, harsh, and complex.
On top of that it chose to be opinionated about features of Objective‑C, that many long time developers consider virtues, not problems:
- Adding compile time static dispatch, and making dynamic dispatch and message passing a second class citizen and introspection a non-feature.
- Define the convenience and elegance of nil-message passing only as a source of problems.
- Classify the implicit optionality of objects purely as a source of bugs.
At the same time it failed to attack, solve and support some of the most prominent current and future computing problems, which in my book all would be more important than most of the areas it tries to be good in:
- overall API interaction complexity
- actual App and UI development
- developer productivity
It keeps defering the big wins to the future while it only offered a very labour intensive upgrade path. Without a steady revenue stream, many apps that would have just compiled fine if done in Objective‑C, either can't take advantage of new features of the devices easily, or had to be taken out of the App Store alltogether, because upgrading would be to costly. If you are working in the indie dev-scene, you probably know one of those stories as well. And while this is supposed to be over now, this damage has been done and is real.
On top of all of this, there is that great tension with the existing Apple framework ecosystem. While Apple did a great job on exposing Cocoa/Foundation as graspable into Swift as they could, there is still great tension in the way Swift wants to see the world, and the design paradigms that created the existing frameworks. That tension is not resolved yet, and since it is a design conflict, essentially can't be resolved. Just mitigated. From old foundational design patterns of Cocoa, like delegation, data sources, flat class hierarchies, over to the way the collection classes work, and how forgiving the API in general should be.
If you work in that world you are constantly torn between doing things the Swift/standard-library way, or the Cocoa way and bridging in-between. To make matters worse there are a lot of concepts that don't even have a good equivalent. This, for me at least, generates an almost unbearable mental load. It leads to writers block and/or running around in cognitive circles trying to answer questions on how to best express the problem, or make Swift be happy with my code.
To add insult to injury, all this attention is taken away from solving and focussing on the actual problem: writing a great app or framework that is a joy to use. Whereas Objective‑C/Cocoa always strived for maximum developer productivity and purpose.
Yes, Swift code might end up being more correct in the end. It also might alert you to edge cases early on. However, the flip side is it inhibits your creativity while writing. When starting out to program, the way I enjoy working, I don't yet know how the API is best expressed. So I try out different ways to express it until I find a sweet spot. Then I go back and unify accordingly.
Swift actively distracts me in that endeavor by making me answer questions I really don't want to answer right now. Yes, stuff might be less correct in the meantime, but heck that is what I want during the design phase. Find my concept, sweet spot, iterate, pivot quickly.
So my opposition to Swift is very deep – on a fundamental design level. I see it as the devil on your shoulder, always fighting for your attention away from your problem domain, back to how everything is completely correct, or more swifty. And at the same time it is very unforgiving towards bigger change - ever tried to switch code from objects back to structs, or vice versa?
Objective‑C had and has nice progressive disclosure there, you can start out to your prototyping and then slowly get help from the tools to refine it when you found your sweet spot. Turn on more warnings, run the static analyzer, optimize hot spots towards C or C++.
It is 4 years in now. It is not too late to pivot and take everything that has been learned and form that into the developer experience in an evolution of Objective‑C that really caters to the goals and ideas of the platform. In my opinion, a lot of the "lofty goals" haven't been achieved, and as discussed, should even be non-goals. Just imagine a world where Objective‑C would have gotten the same amount of drive and attention Swift got from Apple? It is not a big leap to see that everyone would be better off right now. Swift just ended up being a jack of all trades, master of none. (I defer my own ideas for a Objective‑C 3.0 evolution to a potential later blog post, as this is nuanced as well.)
I would love to see a pivot like that, but to be honest, it does seem quite unlikely at this point. That's not to say that an ecosystem with swift at the heart will not produce something one can be productive and content with. However, for me at least, it definitely does not fall into the delightful category of the language/framework combo's I mentioned at the beginning. This, to me, is as a sad and unnecessary regression.
So given my clear opposition to Swift, where does my personal journey go now? Luckily there are a few promising avenues to explore:
- Rust – a strongly typed language with a clear direction and purpose. I don't like the strong type system world very much in general, but for lower level hard problems it clearly serves a purpose. I both want to get a clear grasp of those benefits, and wander more in the lower level world.
- Elixir/Phoenix – this looks like the perfect mind meld of rails and erlang to me. Hope it is as good as it sounds.
- I won't leave the Apple eco-system completely. Even with all their flaws, Macs and iOS devices are still and will be best of breed in many ways. However, I will have a strict no-Swift policy on any of my own future software projects and hope that this is still feasible.