What's New in Swift 5.8
Table of Contents
Swift 5.8, which was released along with Xcode 14.3, brings a lot of improvement to the Swift language.
In this article, I will highlight one that I find interesting.
I categorize features into three groups.
Preparing for Swift 6:
Better support for future features:
Other improvements:
- SE-0274: Concise magic file names
- SE-0365: Allow implicit self for weak self captures, after self is unwrapped
- SE-0369: Add CustomDebugStringConvertible conformance to AnyKeyPath
- SE-0372: Document Sorting as Stable
- SE-0373: Lift all limitations on variables in result builders
Preparing for Swift 6
I think one of the big themes of this version is preparing everyone for Swift 6.
Swift 6 contains a lot of improvements that might have a source-compatibility impact and could not be enabled by default since it will break semantic versioning.
But some of those improvements are already implemented in Swift compiler behind the scene.
Swift 5.8 allows us to opt-in for those features, which will smooth out the transition period.
Swift accomplished this using Feature Flag and SE-0362: Piecemeal adoption of upcoming language improvements.
Feature Flag
Now, every breaking Swift feature will contain an "Upcoming feature flag" field, which includes the feature's name.
We will use this to enable that feature in our code.
You will see this field in some of the new Swift 5.8 features.
But old features that aim for Swift 6.0 also got this flag.
Incrementally adopt Swift 6
SE-0362: Piecemeal adoption of upcoming language improvements bring a way to enable new features that put behind the feature flag.
To enable a feature X
, we add -enable-upcoming-feature X
to Other Swift Flags in Xcode.
You can easily support sarunw.com by checking out this sponsor.
Offline Transcription: Fast, privacy-focus way to transcribe audio, video, and podcast files. No data leaves your Mac.
Better support for future features
As Swift evolved, it introduced many new features to the language.
In the past, there was not much standard for handling new language features.
Swift 5.8 try to improve this with Conditional compilation for attributes and Function back deployment.
Conditional compilation for attributes
As Swift evolved, it introduced many new attributes to the language, e.g., @preconcurrency
introduced in Swift 5.6 (SE-0337).
However, adopting these new attributes means the source code won't compile with an older compiler that doesn't recognize the new attributes.
Before Swift 5.8, we had to use conditional compilation to compile code based on the compiler version.
#if compiler(>=5.6)
@preconcurrency
protocol P: Sendable {
func f()
func g()
}
#else
protocol P: Sendable {
func f()
func g()
}
#endif
There are two problems with this approach.
- Code duplication since we have to repeat the code multiple times.
- Checking for compiler version for attributes is error-prone.
Swift 5.8 introduced SE-0367: Conditional compilation for attributes, which allows us to check the availability of Swift attributes.
#if hasAttribute(AttributeName)
As a result, we can make the code less repetitive and more concise.
#if hasAttribute(preconcurrency)
@preconcurrency
#endif
protocol P: Sendable {
func f()
func g()
}
Function back deployment
SE-0337: Function Back Deployment add a new @backDeployed
attribute that allows Library or Framework author to backport new APIs to be used on the old deployment target.
As an example, the badge
modifier was introduced in iOS 15. SwiftUI team can make it available for older versions of iOS using the @backDeployed
attribute like this.
extension View {
@available(iOS 13.0, *)
@backDeployed(before: iOS 15.0)
public func badge(_ count: Int) -> some View {
self.overlay(Text("Fake Badge"))
}
}
You can easily support sarunw.com by checking out this sponsor.
Offline Transcription: Fast, privacy-focus way to transcribe audio, video, and podcast files. No data leaves your Mac.
Other improvements
- SE-0274: Concise magic file names
- SE-0365: Allow implicit self for weak self captures, after self is unwrapped
- SE-0369: Add CustomDebugStringConvertible conformance to AnyKeyPath
- SE-0372: Document Sorting as Stable
- SE-0373: Lift all limitations on variables in result builders
New #file behavior
SE-0274: Concise magic file names change behavior of #file
.
#file
will return the file name and the module in which it appears. And not including any path in it.
// Before Swift 5.8
print(#file)
// /Users/sarunw/Documents/MyProject/MyProject/ContentView.swift
// Swift 5.8
print(#file)
// MyProject/ContentView.swift
Allow implicit self for weak self captures
SE-0365: Allow implicit self for weak self captures, after self is unwrapped allows us to omit a self
keyword for weak self that has been unwrapped.
This change reduces visual noise and maintains consistency with SE-0269: Increase availability of implicit self in @escaping closures when reference cycles are unlikely to occur in Swift 5.3.
Before Swift 5.8
class ViewController: UIViewController {
var button: UIButton!
func setup() {
button.addAction(
UIAction { [weak self] _ in
guard let self else { return }
self.didTapButton()
},
for: .touchUpInside)
}
func didTapButton() {}
}
Swift 5.8
class ViewController: UIViewController {
var button: UIButton!
func setup() {
button.addAction(
UIAction { [weak self] _ in
guard let self else { return }
didTapButton()
},
for: .touchUpInside)
}
func didTapButton() {}
}
Better print for AnyKeyPath
SE-0369: Add CustomDebugStringConvertible conformance to AnyKeyPath improve an information return when printing out a keypath object.
Before Swift 5.8
struct User {
let firstName: String
let lastName: String
var nickName: String
}
print(\User.firstName)
// Swift.KeyPath<ModuleName.User, Swift.String>
print(\User.lastName)
// Swift.KeyPath<ModuleName.User, Swift.String>
print(\User.nickName)
// Swift.WritableKeyPath<ModuleName.User, Swift.String>
After Swift 5.8
print(\User.firstName)
// \User.firstName
print(\User.lastName)
// \User.lastName
print(\User.nickName)
// \User.nickName
Document sorting as stable
Swift's sorting algorithm was changed to be stable before Swift 5, but the documentation didn't reflect this fact.
SE-0372: Document Sorting as Stable update the documentation to align with the implementation detail.
A stable sort is a sort that keeps the original relative order for any elements that compare as equal.
var roster = [
Player(first: "Sam", last: "Coffey"),
Player(first: "Ashley", last: "Hatch"),
Player(first: "Kristie", last: "Mewis"),
Player(first: "Ashley", last: "Sanchez"),
Player(first: "Sophia", last: "Smith"),
]
roster.sort(by: { $0.first < $1.first })
// roster == [
// Player(first: "Ashley", last: "Hatch"),
// Player(first: "Ashley", last: "Sanchez"),
// Player(first: "Kristie", last: "Mewis"),
// Player(first: "Sam", last: "Coffey"),
// Player(first: "Sophia", last: "Smith"),
// ]
In this case, "Ashley Hatch" and "Ashley Sanchez" are compared as equal.
With stable sort, "Ashley Hatch" and "Ashley Sanchez" relative position remains the same ("Ashley Hatch" comes before "Ashley Sanchez").
Remove all limitations on variables in result builders
Before Swift 5.8, view builders had many limitations around declaring a local variable.
SE-0373: Lift all limitations on variables in result builders remove all of those limitations.
So, you can declare a local variable the way you want.
The following example is a valid declaration in Swift 5.8.
struct FooViewBuilder: View {
var body: some View {
var computedVar: String {
return "Computed"
}
lazy var lazyVar = compute()
let uninitializedVar: String
let defaultNilVar: String?
@AppStorage("example_string") var propertyWrapperVar = "Foo"
VStack {
Text("Foo")
}
}
func compute() -> String {
return "Foo"
}
}
There are still some updates that I didn't cover in this article. You can see all the updates in Swift 5.8 in the Swift Evolution.
Read more article about Swift, Swift 5.8, or see all available topic
Enjoy the read?
If you enjoy this article, you can subscribe to the weekly newsletter.
Every Friday, you'll get a quick recap of all articles and tips posted on this site. No strings attached. Unsubscribe anytime.
Feel free to follow me on Twitter and ask your questions related to this post. Thanks for reading and see you next time.
If you enjoy my writing, please check out my Patreon https://www.patreon.com/sarunw and become my supporter. Sharing the article is also greatly appreciated.
Become a patron Buy me a coffee Tweet ShareHow to Hide Navigation Bar on Tap in UIKit
Since iOS 8, we can easily add a tap gesture to show and hide a navigation bar and toolbar. Let's learn why and how to do it.
How to Open using Rosetta in Xcode 14.3
If you are using Xcode 14.3 or later, you might notice that the option to open Xcode in Rosetta is no longer available. Let's find out what changes.