What’s New in Swift 5.2
Swift 5.2 is now available as part of Xcode 11.4. In this article, you’ll get an overview of the changes you’ll see moving to Swift 5.2. By Bill Morefield.
Sign up/Sign in
With a free Kodeco account you can download source code, track your progress, bookmark, personalise your learner profile and more!
Create accountAlready a member of Kodeco? Sign in
Sign up/Sign in
With a free Kodeco account you can download source code, track your progress, bookmark, personalise your learner profile and more!
Create accountAlready a member of Kodeco? Sign in
Sign up/Sign in
With a free Kodeco account you can download source code, track your progress, bookmark, personalise your learner profile and more!
Create accountAlready a member of Kodeco? Sign in
Contents
What’s New in Swift 5.2
20 mins
- Improved Diagnostics and Error Messages
- Easier Troubleshooting
- Not Just SwiftUI
- Syntactic Sugar Additions
- Calling Types as Functions
- Cleaner Syntax
- Machine Learning Application
- Key Path Expressions as Functions
- Subscripts With Default Arguments
- Major Bug Fixes
- Lazy Filters are Called in Order
- Default Values From Outer Scopes
- Warning When Passing Dangling Pointers
- Overridden Methods Can’t Use Incorrect Generics
- Class-Constrained Protocol Extensions
- Disambiguate Functions with Named Parameters
- Where to Go From Here
Cleaner Syntax
You could achieve the same result of this feature by adding a traditional method on this type. But the new syntax is cleaner, especially where there’s a single, obvious action for a value. In this example, the obvious action for the Quadratic
type would be calculating the value of the equation for a given x
. Similarly, a Parser
will most often parse input as its primary function.
Machine Learning Application
This feature seems particularly focused on machine learning. You see this in the original proposal as it specifically mentions cleaning up the syntax for neural network applications. And it’s similar to Python and cross-compatible. All of this makes Swift even better suited for ML developers.
Key Path Expressions as Functions
With Swift 5.2, the language now allows \Root.value
key path expressions wherever you could already use (Root) -> Value
functions.
In code terms, where you could write:
orders.map { $0.email }
…you can now also write:
orders.map(\.email)
The current implementation limits this feature to key path literal expressions. You cannot currently write the following, though the discussion for this feature suggests future implementation:
let kp = \.email
users.map(kp)
However, you can accomplish something similar with different syntax:
let asEmail: (Order) -> String = \Order.email
orders.map(asEmail)
The first two features bring better functional abilities to Swift. You can now use functions in places that previously required blocks or closures. Since you now can pass a key path as a function, you can also pass a key path to a value that implements the new callAsFunction()
.
Subscripts With Default Arguments
With Swift 5.2, you can now declare default arguments for custom subscripts when adding them for a type. For example, here’s a simple type that uses subscripts for multiplication:
struct Multiplier {
subscript(x: Int, y: Int = 1) -> Int {
x * y
}
}
let multiplier = Multiplier()
This addition allows you to write code that specifies any number of available subscripts. Swift uses the provided default for any subscript not specified:
multiplier[2, 3]
multiplier[4]
Major Bug Fixes
While new features get most of the attention in new releases, bug fixes are also critical. You’ll learn about these next.
Lazy Filters are Called in Order
When chaining calls to filter(_:) on a lazy sequence or collection, the filtering predicates are now called in the same order as eager filters. This bug fix is the one most likely to break existing code. For most collections, Swift calls filtering predicates in order. So this code:
let array = ["1", "2", "3"]
let filtered = array
.filter { _ in
print("A")
return true
}
.filter { _ in
print("B")
return true
}
_ = Array(filtered)
…will print:
A
A
A
B
B
B
Before Swift 5.2, evaluating a lazy sequence or collection evaluated in the reverse order. Take this example:
let lazyFiltered = array.lazy
.filter { _ in
print("A")
return true
}
.filter { _ in
print("B")
return true
}
_ = Array(lazyFiltered)
You would expect it to print:
A
B
A
B
A
B
…but it actually prints:
B
A
B
A
B
A
When compiled with Swift 5.2, the results print in the expected order. If you’ve written code that counts on the reversed execution, you’ll need to update your code to reflect the fixed behavior.
The remaining bug fixes in Swift 5.2 have less of an impact on existing code but are worth noting. You’ll want to know about these changes if you have worked around these issues in the past.
Default Values From Outer Scopes
The compiler now supports local functions whose default arguments capture values from outer scopes. This allows code such as this:
func outer(x: Int) -> (Int, Int) {
func inner(y: Int = x) -> Int {
return y
}
return (inner(), inner(y: 0))
}
Before Swift 5.2, the code above would not work.
Warning When Passing Dangling Pointers
The compiler will now show a warning when you try to pass a temporary pointer argument that attempts to outlive the call. This would include code such as:
func generatePointer() {
var number: Int8 = 0
let pointer = UnsafePointer(&number)
}
This will result in a warning stating:
warning: initialization of 'UnsafePointer<Int8>' results in a dangling pointer<int8>
For more information about this bug fix, see this article at Swift.org: SR-2790: Reject UnsafePointer initialization via implicit pointer conversion
Overridden Methods Can’t Use Incorrect Generics
Previously, you could write override a method with a generic signature incompatible with the base method’s generic signature. For example, you could write this:
protocol P { }
class Base {
func doWork<T>(input: T) { }
}
class Derived: Base {
override func doWork <T: P>(input: T) { }
}
In Swift 5.2, this code no longer compiles and will now throw an error.
Class-Constrained Protocol Extensions
A class-constrained protocol extension, where the extended protocol does not impose a class constraint, will now infer the constraint implicitly. Consider the following code:
protocol Foo {}
class Bar: Foo {
var someProperty: Int = 0
}
extension Foo where Self: Bar {
var anotherProperty: Int {
get { return someProperty }
set { someProperty = newValue }
}
}
Here Foo
does not impose a class constraint. But the compiler
infers this due to the Self: Bar
constraint on Foo
. This results in the setter becoming implicitly non-mutating, just as if Foo
had a class constraint.