The Decade of Swift
Even before it started, WWDC 2014 was a very special event for me, personally. Not only was it the first (and at the time of writing, the only) time that I was lucky enough to be able to attend the conference in person, it was also my very first encounter with the wider Apple developer community — which had a profound impact on me in terms of how I’d come to view my work, and myself as a member of that community.
But this is not a story about me. Instead, it’s the story of how approximately one hour and forty-five minutes into the WWDC 2014 keynote, the world of iOS and Mac development fundamentally changed — when Craig Federighi announced that ”We have a new programming language”. This is weekly article number 150 — looking back at what, for me and many others, has been The Decade of Swift.
Type safety and expressiveness
If you open up your macOS terminal app of choice and type man swift
to bring up the manual entry for the Swift compiler, it’s described as a “safe, fast, and expressive general-purpose programming language”.
While the idea of type safety and utilizing the compiler to more strictly verify the code that it’s processing isn’t something that’s unique to Swift, the combination of a very robust type system and a highly expressive syntax is one of the key aspects that instantly made Swift feel so refreshing to me and many others.
A core part of building any kind of software — whether it’s an app, a framework, or a compiler for a programming language — is constantly trying to find the right balance between various trade-offs. The question we have to keep asking ourselves again and again is: what potential downsides or obstacles are we willing to accept in order to achieve our goals, and how can we best utilize the strengths of our chosen approach?
What’s interesting about Swift’s strict, static type system is that it’s arguably both a trade-off and an asset at the same time. A strength in that it encourages us to write code that’s less prone to runtime errors, but also a potential weakness in that it requires us to spend more time designing our code up-front, which — at least initially — can have a negative impact on productivity and speed of iteration.
That’s why Swift’s expressive and very lightweight syntax (especially when compared to its predecessor, Objective-C), played such an important role since the very beginning, and why it’s increasingly becoming a very defining factor of the language as a whole. Take enums for example. Not only do they let us model and handle finite sets of values using dedicated, strongly verified types — they also make the available values much more discoverable, and enable us to write highly expressive dot syntax-based call sites:
let favorites = articles(in: .favorites)
From the very first 1.0 release, to the great standard library API redesign of Swift 3, to the introduction of Codable
and key paths in Swift 4, to how conformances to protocols like Equatable
and Hashable
can now be auto-synthesized as of Swift 4.1, to Swift 5.1’s new DSL-focused features — every new release of Swift, so far, has improved and tweaked the language to enable new kinds of expressions and patterns to be constructed.
New patterns and conventions
That constant flow of new features and patterns has also resulted in a ton of new ideas, both from Apple and third party developers. While the rapid speed of iteration that we’ve seen over Swift’s first five and a half years might’ve occasionally been problematic — especially in the early days, when several APIs and language features were drastically changed from one version to the next — it’s also been a great source of inspiration and excitement for the community as a whole.
In many ways, a new programming language means a new beginning, and an opportunity for current patterns and conventions to be rethought and changed to leverage the new language’s features and strengths. While Swift was initially strongly based on Objective-C’s existing conventions, it didn’t take long for new best practices to start to form, and for the community to start developing a sense of what makes code “swifty”.
A big part of that is the increased use of Swift’s powerful type system to drive logic and to validate values. While Objective-C code tends to rely heavily on runtime validation using more free-form values, Swift-native patterns often focus on stronger types and relationships that can be enforced by the compiler. For example, let’s say that we wanted to write a function for sorting an array based on an element property — in the early days of Swift, we might’ve written something like this:
// We extend NSArray, rather than Swift's native Array type, to
// be able to leverage dynamic Objective-C features like Selectors:
extension NSArray {
// Since we're calling an Objective-C API under the hood, we
// can't retain type safety, and are forced to return [Any]:
func sortedArrayBasedOn(key: String) -> [Any] {
return sortedArray(using: Selector(key))
}
}
While being able to call into existing Objective-C APIs from Swift, and vice versa, was (and in many code bases, still is) incredibly important — it quickly became quite clear that Swift needed its own set of conventions and API design styles, which resulted in the official API design guidelines.
Using some of those modern Swift conventions and guidelines, here’s how we might refactor the above implementation — to use Swift’s native key paths feature rather than relying on dynamic strings, and to retain full type safety thanks to Swift’s powerful generics capabilities:
extension Array {
func sorted<T: Comparable>(
by keyPath: KeyPath<Element, T>
) -> Self {
sorted { $0[keyPath: keyPath] < $1[keyPath: keyPath] }
}
}
Using the above, we can now sort any array in a way that’s both fully type-safe, and highly expressive:
let mostPopular = articles.sorted(by: \.numberOfViews)
Changes like the one above may initially seem minor in the grand scheme of things, but step by step there’s no doubt that Swift has fundamentally changed how most Apple-platform apps are built — in that it has required us to rethink and restructure the way we write code, especially if we previously relied heavily on the very dynamic nature of Objective-C.
That trend is likely to grow even stronger, especially as Apple has now started to roll out Swift-only frameworks that uses the above type of modern conventions, such as Combine and SwiftUI — both of which favor more minimalistic API designs and make heavy use of Swift’s type system, to the point where SwiftUI uses the type system itself to determine when to re-render a given view.
A major cultural shift
However, perhaps the biggest impact that Swift has had on the Apple developer community isn’t so much a technical one — but rather a cultural one. While there was definitely a strong community surrounding Apple’s developer tools long before Swift, there’s no denying that the introduction of Swift drastically transformed and shifted that community’s focus.
Think about it, how often were the iOS and Mac developer communities referred to as “The Objective-C community”? Before Swift it seemed like the programming language being used was more of an implementation detail — while frameworks like Cocoa, and the platforms themselves, were what defined the community and what brought people together. Compare that to now, at the end of 2019, when that same community has in many ways and circles been practically renamed to “The Swift community”.
However, what’s perhaps even more interesting than how the third party developer community has changed, is how Apple has changed their approach to how their Swift-based developer tools are built and evolved. After all, there was never an “Objective-C Evolution” process that let anyone pitch ideas and changes to the language and its standard library. There was never a dedicated Objective-C forum on which both first- and third party tools could be discussed with Apple engineers.
The way Swift is (for the most part) designed and developed in the open is a huge shift compared to the very isolated and secretive ways in which Apple typically works — and that shift has likely played a major part in making the community feel so attached to, and excited about, Swift as a language.
What might the future have in store?
Looking back at the 12 Swift updates that have been released since version 1.0, it’s quite clear that language-level ergonomics and user-facing features have been a major focus. The syntax has been streamlined and fine-tuned, the standard library has been made much more capable, generics are now a lot more powerful, and major sources of boilerplate have been eliminated. That in addition to achieving ABI and module stability, and much more.
However, going forward, I expect that focus to slightly change more towards the overall stability and speed of the Swift compiler itself. There’s no doubt that there’s a ton of potential improvements to be made within this area, which wouldn’t just make the process of writing and running Swift code much more enjoyable, but also continue to unlock new use cases for Swift — for example to build just-in-time compiled plugins, for scripting, and to expand how Swift can be used for server-side development.
While Swift will most likely always be slower to compile compared to languages that perform far fewer compile-time checks and inference, more stable and capable tooling would definitely be welcome — and we’ve already seen tools like the llbuild
build system drastically improve compile times and the overall developer experience. A trend which I hope, and think, will continue.
A new diagnostics engine is already being developed for Swift 5.2, which aims to improve the type checker and the warnings and error messages that the Swift compiler generates when an expression failed to compile — which should be particularly useful when writing code that makes heavy use of generics, such as when building views using SwiftUI.
Besides improved tooling, I’m also really looking forward to the day when Swift includes some form of language-level support for asynchronous programming. While frameworks like Grand Central Dispatch and Combine are incredibly powerful, being able to express asynchronous operations using native language features (such as async/await
, or any of the other approaches outlined by Chris Lattner in his “Swift Concurrency Manifesto”) would most likely result in much simpler and more expressive asynchronous code.
Conclusion
Starting with the iPad, iPhone 4, and Grand Central Dispatch in 2010, and ending with SwiftUI and Combine in 2019 — it’s been an incredibly exciting decade to be a developer for Apple’s platforms. Seeing so many new people joining our community from all around the world, and constantly seeing new ideas, patterns and conventions being shared through articles, videos, podcasts and conferences is absolutely wonderful.
There’s no doubt that both Swift and SwiftUI have been incredible energizers for the community at large, enabling new developers to write their very first app, for new patterns and tools to be invented, and for existing code bases to be improved. It may just be a programming language, but the way it has influenced our community, and fundamentally changed my work and my career, there’s no doubt in my mind that this has been The Decade of Swift.
I can’t wait to see what the next decade will bring, and how both Apple and the community will continue to improve, evolve and invent new tools that’ll help us build even better software — both for our existing devices and for brand new ones. I’ll of course continue to cover Swift and its related technologies on this site and on the podcast for as long as I possibly can, hopefully all throughout the 2020s, and I hope that you’ll join me for that ride.
Thanks for reading, and Happy New Year! 🚀