Swift 5.2 - what's new?

The first beta of Swift 5.2 has just appeared in Xcode 11.4 beta, and there have been changes in the language, along with a reduction in the size of the code and used memory, as well as a new diagnostic system that will allow you to quickly detect errors.

Using KeyPath Expressions as Functions


We define the following structure:

struct User {
    let name: String
    let age: Int
    let bestFriend: String?

    var canVote: Bool {
        age >= 18
    }
}

Let's create some instances of our structure and put them in an array:

let eric = User(name: "Eric Effiong", age: 18, bestFriend: "Otis Milburn")
let maeve = User(name: "Maeve Wiley", age: 19, bestFriend: nil)
let otis = User(name: "Otis Milburn", age: 17, bestFriend: "Eric Effiong")
let users = [eric, maeve, otis]

Now attention: if you need to get an array of names of all users, you can do it as follows:

let userNames = users.map(\.name)
print(userNames)

Previously, we needed to use a closure:

let oldUserNames = users.map { $0.name }

In the same new way, you can get all users who can vote:

let voters = users.filter(\.canVote)

And here we get everyone who has a best friend:

let bestFriends = users.compactMap(\.bestFriend)

Values โ€‹โ€‹for user-defined types


Create a Dice structure with lowerBound and upperBound properties, and then add the callAsFunction function . Thus, every time we get the dice value, we will get a random value:

struct Dice {
    var lowerBound: Int
    var upperBound: Int

    func callAsFunction() -> Int {
        (lowerBound...upperBound).randomElement()!
    }
}

let d6 = Dice(lowerBound: 1, upperBound: 6)
let roll1 = d6()
print(roll1)

Here we get a random number from 1 to 6, and this is completely identical to a direct call to callAsFunction (). We could do the same thing like this:

let d12 = Dice(lowerBound: 1, upperBound: 12)
let roll2 = d12.callAsFunction()
print(roll2)

Swift automatically selects the correct call based on how callAsFunction () is defined. For example, you can add several parameters, change the return value, and even, if necessary, mark the method as mutating.

Here we will create a StepCounter structure that captures the number of steps taken and signals whether the number of steps completed has reached 10,000:

struct StepCounter {
    var steps = 0

    mutating func callAsFunction(count: Int) -> Bool {
        steps += count
        print(steps)
        return steps > 10_000
    }
}

var steps = StepCounter()
let targetReached = steps(count: 10)

callAsFunction () also supports throws and rethrows, and you can define several callAsFunction () methods, as with regular overloading.

Subscript can declare default arguments


When adding subscripts to a type, you can use the default arguments. For example, if we have a PoliceForce structure with a custom subscript to list officers in the unit, we can add a default parameter to return it if the array is read outside its borders:

struct PoliceForce {
    var officers: [String]

    subscript(index: Int, default default: String = "Unknown") -> String {
        if index >= 0 && index < officers.count {
            return officers[index]
        } else {
            return `default`
        }
    }
}

let force = PoliceForce(officers: ["Amy", "Jake", "Rosa", "Terry"])
print(force[0])
print(force[5])

Here we get โ€œAmyโ€ and then โ€œUnknownโ€ in the output, since we do not have an array element with index 5.

Since we wrote default default, we can set a custom value, like:

print(force[-1, default: "The Vulture"])

New and improved diagnostics


Swift 5.2 introduces a new diagnostic architecture whose goal is to improve the quality and accuracy of Xcode development error messages. This is especially noticeable when working with SwiftUI code, where Swift often generates false positive error messages.

For example, consider the following code:

struct ContentView: View {
    @State private var name = 0

    var body: some View {
        VStack {
            Text("What is your name?")
            TextField("Name", text: $name)
                .frame(maxWidth: 300)
        }
    }
}

Here we are trying to associate view TextField with an integer Stateproperty that is wrong. In this case, Swift 5.1 will generate an error for the frame () modifier 'Int' is not convertible to 'CGFloat?' but Swift 5.2 and later correctly recognize the error in the binding of $ name: Cannot convert value of type 'Binding' to expected argument type 'Binding'.

Source: https://habr.com/ru/post/undefined/


All Articles