What’s incipient in Swift 5.2
Swift 5.2 is finally released!!
Apple released Xcode 11.4 which comes with swift 5.2.
This release is going to be a treat for developers since it focuses on improving the developer experience. With it’s new and improved diagnostic, resolving errors would be faster. Code completion now works better. Along with that, there is a reduction in code size and memory usage.
This article will be a quick tour of the changes, language offers in its latest version. Let’s get commenced.
Key Path Expression as Function
SE-0249 introduced a shortcut that sanctions us to utilize keypath in concrete circumstances.
It introduces the ability to use the key path expression \Root.value
wherever functions of (Root) -> Value
are allowed.
Let’s understand it through an example, so here is a struct User with two properties.
struct User {
let firstName: String
let lastName: String
}
We could create some instances of that struct and put them into an array, like this:
let harry = User(firstName: "Harry", lastName: "Potter")
let hermione = User(firstName: "Hermione", lastName: "Granger")
let ron = User(firstName: "Ron", lastName: "Weasley")
let users = [harry, hermione, ron]
Now if we optate to get an array of all the user’s first name, we utilize to do so homogeneously to this:
let oldFirstNames = users.map { $0.firstName }
But with Swift 5.2 Keypath we can do so like this:
let newFirstNames = users.map(\.firstName)
We can also use it on compactMap
and filter, like this
let lastNames = users.compactMap(\.lastName)
Callable values of user-defined nominal types
SE-0253 introduces “statically” callable values to Swift. Callable values are the values that are defined with function-like behavior and can be called using function call syntax.
Let’s understand it through an example:
struct Adder {
var base: Intfunc callAsFunction(_ x: Int) -> Int {
return x + base
}
}var adder = Adder(base: 3)
adder(10) // returns 13
adder.callAsFunction(10) // returns 13
As we can see in the example we call a value directly if its type implements a method named callAsFunction()
We can add as many parameters as we want, we can control the return value, and we can even mark methods as mutating if needed. We can even define multiple callAsFunction methods on a single type.
NOTE: callAsFunction is different from @dynamicCallable, which was introduced in Swift 5, in the following way:
When we declare a call-as-function method, we specify the number of arguments, and each argument’s type and label. But while declaring dynamicCallable attribute’s methods, we only specify the type used to hold the array of arguments.
Subscripts can now declare default arguments
When adding custom subscripts to a type, we can now use default arguments for any of the parameters.
For example, if we have struct Hogwarts and we are defining custom subscript to get students, we can pass a default value if someone is trying to access an out of bounds index.
struct Hogwarts {
var students: [String]subscript(index: Int, default default: String = "Unknown") -> String {
if index >= 0 && index < students.count {
return students[index]
} else {
return `default`
}
}
}let school = Hogwarts(students: ["Harry", "Hermione", "Ron"])
print(school[0])
print(school[5])
This will print Harry which is at index 0 and Unknown because there is no student at index 5.
So, because I use default default
in my subscript, I can use a custom value like this:
print(school[-1, default: "Draco"])
Additional restrictions on inheritance for subclasses defined outside the module
There is a small change in swift 5.2 that might break previous functionality.
Now a subclass can no longer automatically inherit convenience initializers from their superclass if superclass is defined in other modules and has a non-public designated initializer.
To restore this automatic inheritance behavior, the base class must ensure that all of its designated initializers are public
or open
.
Lazy filtering order is now reversed
Another change that can break previous functionality includes lazy filtering. If we use a lazy sequence such as an array and apply multiple filters to it, those filters are now run in the reverse order.
For example, let’s say we have an array of names and we apply the first filter to select names starting with H and then apply the second filter to get names that return true.
let people = ["Harry", "Hermione", "Ron"]
.lazy
.filter { $0.hasPrefix("H") }
.filter { print($0); return true }
print(people.count)
In Swift 5.2 and later that will print “Harry” and “Hermione” because after the first filter runs those are the only names that remain to go into the second filter. But before Swift 5.2 it would have returned all three names because the second filter would have been run before the first one.
This is because of lazy filter, if we remove lazy then it will always print “Harry” and “Hermione” regardless of swift version.
Default values from outer scopes.
Afore Swift 5.2, the compiler used to crash when inner function uses outer function argument as the default argument(SR-2189).
With the release of Swift 5.2, you can successfully run the following code, which would otherwise return unsafeMutableAddressor error on compilation
func outer(x: Int) -> (Int, Int) {
func inner(y: Int = x) -> Int {
return y
}
return (inner(), inner(y: 0))
}
New and improved diagnostics
Swift 5.2 drastically improved the quality and precision of error messages in the Swift compiler.
For example:
import SwiftUIstruct RoomDetails: View {
@State var roomName: String
@State var imageName: Stringvar body: some View {
VStack {
TextField("Room Name")// It should be TextField("Name", text: $name)Image(imageName)
.frame(maxWidth: 300)
}
}
}
That attempts to bind a TextField
view to a roomName @State
property. In Swift 5.1 this caused an error for the frame()
modifier saying 'Int' is not convertible to 'CGFloat?’, but in Swift 5.2 and later this correctly identifies the error is the missing argument for parameter text in the call.
Code completion improvement
Code completion results have improved the type of information. Results will display opaque result types (for example some View)
when possible, and preserve typealiases. Results will no longer show nonessential parent types.
For example, in Swift 5.1.3 (Xcode 11.3.1):
In Swift 5.2 (Xcode 11.4) this is now displayed as:
Conclusion
Mainly, Swift 5.2 has amendments to a dining experience to explore its extensive menu. Apart from a new diagnostics engine for compiler errors, there are lots of new testing and debugging features as well.
You can read more about this through these links.