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.

Leave a rating/review
Download materials
Save for later
Share

Swift 5.2 is now available as part of Xcode 11.4. This article presents an overview of the changes you’ll see in this latest version.

Overall, Swift 5.2 is a minor release which is not necessarily a bad thing. It does bring many tweaks and minor improvements that will help the workflow of Swift developers. In this release, you’ll find:

  • Better diagnostics with more helpful error messaging, especially for SwiftUI.
  • New features that will simplify certain tasks.
  • Major bug fixes.

In many of the sections below, you’ll see references to the Swift Evolution proposals, like SE-0253, or the Swift bug reports, like SR-11298. You can explore these in greater depth using the links provided.

Also, follow along with the Playgrounds created for this article. Use the Download Materials button at the top or bottom to download them.

To start, you’ll explore the highest-profile feature: improvements to the error messages.

Improved Diagnostics and Error Messages

Do you always write perfect code on the first try? If not, you’ll love the improvements to the diagnostics engine in Swift 5.2! When you have an error in your code, the compiler gives you a more accurate description of the error and its location.

To be fair, the Swift compiler already did a good job reporting most errors in your code. The code below:

var str = "10"
let total = str + 5

…produced a clear error message:

A clear error from earlier Swift versions

The error message told you that you cannot use the ‘+’ operator to join a String and an Int. Depending on your intent, it also pointed you toward the action to fix the problem. If the goal was to add 5 to the number represented by the string, then you could fix the error like this:

var str = "10"
let total = Double(str)! + 5

At other times, however, the error messages weren’t all that helpful. This was especially true with errors related to type checking. Swift 5.2 addressed this issue by focusing on improvements to the type checker.

Easier Troubleshooting

Consider the following code for SwiftUI:

struct SquareView : View {
  @State var angle = 0.0
  
  var body: some View {
    VStack {
      TextField("Angle:", text: $angle)
      Rectangle()
        .rotation(Angle(degrees: angle))
        .frame(width: 100.0, height: 100.0)
    }
  }
}

Prior to Xcode 11.4, the error message you would have seen was this:

An ambiguous error in SwiftUI from older Swift versions

An ambiguous error in SwiftUI from older Swift versions

Not very helpful, eh? It was common to see unclear error messages such as this when working with SwiftUI. This made it that much harder to learn to use SwiftUI. Finding even the simplest typos became a process of tediously re-reading, removing or commenting out code to isolate the actual error.

Look again at the code block. Can you spot the actual problem? As a hint, it has nothing to do with converting a Double to a CGFloat!

Now open swiftui.playground in the starter folder. You’ll see that the compiler gives you a much more useful and actionable error message:

The improved error from Swift 5.2 and above

The improved error from Swift 5.2 and above

The error message points you to the correct line with the error and tells you the problem: You’re passing a binding to Double to a method that expects a binding to String.

Now that you know what to do to fix this error, replace the line indicated with this:

TextField("Angle", value: $angle, formatter: NumberFormatter.decimalFormatter)

Not Just SwiftUI

While you’ll notice better error messaging most often in SwiftUI, you’ll also see improvements in other Swift code. Open swift52.playground in the final project and you’ll find this code commented out:

let x: [Int] = [1, 2, 3, 4]
let w: UInt = 4

let filtered = x.filter { ($0 + w)  > 42 }

This code attempts to add an Int to a UInt without a cast and will not compile. Prior to Swift 5.2, the compiler displayed the following error:

error: binary operator '+' cannot be applied to operands of type 'Int' and 'UInt'

Now uncomment the code and you’ll see a more precise and helpful error message:

error: new-features.playground:34:22: error: cannot convert value of type 'UInt' to expected argument type 'Int'
_ = x.filter { ($0 + y) > 42 }
^
Int( )
Note: For a more detailed description of the improvements to the diagnostic engine, check out this blog article: New Diagnostic Architecture Overview.

Syntactic Sugar Additions

Swift already has quite a bit of syntactic sugar built into the language. Swift 5.2 adds to this by bringing two new features that certain developers will find very handy: Calling types as functions and using key path expressions as functions.

Note: Syntactic sugar changes the syntax of the language to make it easier to understand or more concise.

Calling Types as Functions

This new feature introduces statically callable values to Swift. But what does that mean? Well, it means that you can call classes or other structures as if they were functions.

To implement this feature in your code, you add a method named callAsFunction(_:) to the type. That’s it! There’s no step two.

With this addition, you can now call the value as a function. For example, take this type that represents a quadratic equation — a common mathematical function in the form of ax^2 + bx + c:

struct Quadratic {
  var a: Double
  var b: Double
  var c: Double

  func callAsFunction(_ x: Double) -> Double {
    a * pow(x, 2) + b * x + c
  }
}

You define your type as with any other value:

let f = Quadratic(a: 4, b: 4, c: 3)

Note that adding callAsFunction(_:) doesn’t prevent you from using the default init() method.

The choice of a mathematical type for this example is not a coincidence. Using syntax similar to mathematical notation provides the primary motivation for this feature.

let y = f(5)

As with any method, you can have multiple overrides for callAsFunction(_:) in your type. If you wanted to also calculate values for an array of Doubles and return the result as a new array, you could add a second implementation of callAsFunction(_:) like this:

func callAsFunction(_ xs: [Double]) -> [Double] {
  xs.map { callAsFunction($0) }
}

This code takes an array of Doubles and uses map() and the previous implementation of callAsFunction(_:) to produce an array with the results. Swift determines the appropriate method to call as it would for any other overridden function.

let z = f([5, 7, 9])