
Swift 5.6 was released just this past March, but the language evolution is unstoppable. There are plenty of things we already know will be included in the next release, so let’s look at the most interesting developments.
Syntax Improvements
Syntax improvements aren’t necessarily very important, but they will quickly start affecting code style, so we’ll start with these.
if let Shorthand
All Swift developers are familiar with optional binding by means of if and guard statements:
let uuid: String? = "e517e38a-261d-4ca5-85f4-9136ace20683"
if let uuid = uuid {
// The UUID string is not optional here.
}
In Swift 5.7, optional binding will become even more concise:
let uuid: String? = "e517e38a-261d-4ca5-85f4-9136ace20683"
if let uuid {
// The UUID string is not optional here.
}
The same trick is also possible with the guard statement:
guard let uuid else {
// The UUID string is nil.
return
}
// The UUID string is not optional from now on.
This isn’t the most important or anticipated language addition, but it looks spectacular, and I’ll definitely make use of it.
Default Values for Generic Parameters
The compiler will finally accept method declarations like this:
func doSomethingWith<Values: Collection>(
_ values: Values = [1, 2, 3]
) { }
Yes, starting from Swift 5.7, we will be able to use default values with generic parameters.
The default value won’t limit the use of the generic parameter, and we’ll still be able to pass, for example, a set of strings if we’re not happy with the default argument.
Multi-Statement Closure Type Inference
The current version of Swift is good enough in inferring types of closures… as long as the closure has only one statement. This code is perfectly fine for Swift 5.6 and earlier versions:
func validateUUID<R>(using handler: (R) -> Bool) {
// ...
}
let uuids = [String]()
validateUUID { uuids.contains($0) }
However, if the closure has more than one statement, the compiler will complain with the error “Unable to infer type of a closure parameter in the current context”:
func requestUUID<R>(_ handler: (R) -> Result<R, Error>) { }
func notifyDuplicatedUUID<R>(_ uuidRepresentation: R) { }
func processUUID<R>(_ uuidString: R) -> Result<R, Error> { }
let uuids = [String]()
requestUUID { // The error is on this line.
if uuids.contains($0) {
notifyDuplicatedUUID($0)
}
return processUUID($0)
}
The compiler wants our help with type inference even if it could’ve managed without it:
requestUUID { (uuidString: String) in
if uuids.contains(uuidString) {
notifyDuplicatedUUID(uuidString)
}
return processUUID(uuidString)
}
Swift 5.7 will have improvements in closure type inference and will no longer ask for such help, meaning the previous code snippet will compile just fine.
New Types
Swift 5.7 will introduce a couple of new and interesting type families to the standard library. Let’s take a look at them.
Regex
Regex promises to become a new, simple, and powerful way of dealing with regular expressions. As a simple but impressive example, let’s say we need to parse text data into objects of this type:
struct Person {
let firstName: String
let secondName: String
}
Here’s a string to parse, with some unexpected whitespace characters:
let input = " Leo Tolstoy"
This is a Regex object, initialized from a literal that will help us deal with the string parsing (pay attention to named capturing groups):
let regex = #/\s*(?<firstName>\w+)\s*(?<secondName>\w+)\s*/#
The following code shows how we can retrieve information from the string using that Regex object:
let match = try! regex.wholeMatch(in: input)
// These are named capturing groups defined above.
let firstName = match!.firstName
let secondName = match!.secondName
let person = Person(firstName: String(firstName),
secondName: String(secondName))
Can you guess what Person the object resulted in? My jaw dropped when I saw that it’s actually Person(firstName: "Leo", secondName: "Tolstoy")! Now processing huge sheets of CSV-formatted text with pure Swift will become a piece of cake.
Clock, Instant, and Duration
These types might not look that inspiring, but they’re still important. Swift has been in need of its own clock-related abstractions to replace wrappers around C code from the Dispatch framework. And at last, it will have them.
The Clock protocol will define the concept of passing time. Two main implementations are ContiniousClock, which runs no matter what, and SuspendingClock, which doesn’t advance if the process is suspended. So, Clock determines how exactly time runs:
try await Task.sleep(until: .now + .seconds(1),
clock: .continuous)
The aforementioned .now + .seconds(1), despite its resemblance to the DispatchTime syntax, is the instance of the new type Instant. Its purpose, as you may have guessed, is to represent a moment in time.
Another use case for the Clock type is measuring the time it takes for code blocks to execute. The resolution of such a measurement is claimed to be suitable even for benchmarks:
let timeElapsed = ContinuousClock().measure { benchmarkMe() }
The type of the resulting timeElapsed is not TimeInterval, it’s an instance of another new type called Duration. The type is Comparable, has all necessary arithmetic operations defined, and, in general, is ready to humbly serve.
Concurrency
Of course, the next Swift release cannot do without additions to the new concurrency model that was introduced a couple of updates back. This time, we’ll have all sorts of groovy little door prizes.
Concurrency on the Top Level
Currently, such calls are not permitted on the top level (for instance, in the root of the main.swift file):
await doAsyncWork()
With Swift 5.7, it will become perfectly legal.
