Первая бета Swift 5.2 только что появилась в Xcode 11.4 beta, и в ней произошли изменения в языке, наряду с сокращением размера кода и используемой памяти, а также появилась новая система диагностики, которая позволит быстрее выявлять ошибки.
Определим такую структуру:
Создадим несколько экземпляров нашей структуры и положим их в массив:
Теперь внимание: если вам нужно получить массив имён всех пользователей, вы можете сделать это следующим образом:
Ранее нам нужно было использовать замыкание:
Таким же новым образом можно получить всех пользователей, которые могут голосовать:
А здесь мы получим всех, у кого есть лучший друг:
Создадим структуру Dice со свойствами lowerBound и upperBound, а затем добавим функцию callAsFunction. Таким образом, всякий раз при получении значения dice, мы будем получать случайное значение:
Здесь мы получим случайное число от 1 до 6, и это совершенно идентично прямому вызову callAsFunction(). То же самое мы могли бы сделать так:
Swift автоматически подбирает верный вызов на основании того, как именно определена callAsFunction(). Например, вы можете добавить несколько параметров, менять возвращаемое значение, и даже, если нужно, помечать метод как mutating.
Здесь мы создадим структуру StepCounter, которая фиксирует число пройденных шагов и сигнализирует, достигло ли количество пройденных шагов 10,000:
callAsFunction() поддерживает также throws и rethrows, и вы можете определить несколько callAsFunction() методов, как при обычной перегрузке.
При добавлении subscripts к типу можно использовать аргументы по умолчанию. Например, если у нас есть структура PoliceForce с пользовательским subscript чтобы перечислять офицеров в подразделении, мы можем добавить параметр по умолчанию, чтобы возвращать его в случае чтения массива вне его границ:
Здесь мы получим на выводе “Amy”, а затем “Unknown”, так как у нас нет элемента массива с индексом 5.
Так как мы написали default default, мы можем задавать пользовательское значение, вроде:
Swift 5.2 представляет новую диагностическую архитектуру, цель которой — улучшить качество и точность сообщений XCode об ошибках при разработке. Это особенно заметно при работе с кодом SwiftUI, где Swift часто выдает ложноположительные сообщения об ошибках.
Для примера рассмотрим такой код:
Здесь мы пытаемся связать view TextField c целым State свойством, что неверно. Swift 5.1 выдаст в таком случае ошибку для модификатора frame() 'Int' is not convertible to 'CGFloat?’, но Swift 5.2 и последующие верно распознают ошибку в биндинге $name: Cannot convert value of type 'Binding' to expected argument type 'Binding’.
Используем KeyPath выражения как функции
Определим такую структуру:
struct User {
let name: String
let age: Int
let bestFriend: String?
var canVote: Bool {
age >= 18
}
}
Создадим несколько экземпляров нашей структуры и положим их в массив:
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]
Теперь внимание: если вам нужно получить массив имён всех пользователей, вы можете сделать это следующим образом:
let userNames = users.map(\.name)
print(userNames)
Ранее нам нужно было использовать замыкание:
let oldUserNames = users.map { $0.name }
Таким же новым образом можно получить всех пользователей, которые могут голосовать:
let voters = users.filter(\.canVote)
А здесь мы получим всех, у кого есть лучший друг:
let bestFriends = users.compactMap(\.bestFriend)
Значения у определяемых пользователем типов
Создадим структуру Dice со свойствами lowerBound и upperBound, а затем добавим функцию callAsFunction. Таким образом, всякий раз при получении значения dice, мы будем получать случайное значение:
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)
Здесь мы получим случайное число от 1 до 6, и это совершенно идентично прямому вызову callAsFunction(). То же самое мы могли бы сделать так:
let d12 = Dice(lowerBound: 1, upperBound: 12)
let roll2 = d12.callAsFunction()
print(roll2)
Swift автоматически подбирает верный вызов на основании того, как именно определена callAsFunction(). Например, вы можете добавить несколько параметров, менять возвращаемое значение, и даже, если нужно, помечать метод как mutating.
Здесь мы создадим структуру StepCounter, которая фиксирует число пройденных шагов и сигнализирует, достигло ли количество пройденных шагов 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() поддерживает также throws и rethrows, и вы можете определить несколько callAsFunction() методов, как при обычной перегрузке.
У Subscript можно объявить аргументы по умолчанию
При добавлении subscripts к типу можно использовать аргументы по умолчанию. Например, если у нас есть структура PoliceForce с пользовательским subscript чтобы перечислять офицеров в подразделении, мы можем добавить параметр по умолчанию, чтобы возвращать его в случае чтения массива вне его границ:
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])
Здесь мы получим на выводе “Amy”, а затем “Unknown”, так как у нас нет элемента массива с индексом 5.
Так как мы написали default default, мы можем задавать пользовательское значение, вроде:
print(force[-1, default: "The Vulture"])
Новая и улучшенная диагностика
Swift 5.2 представляет новую диагностическую архитектуру, цель которой — улучшить качество и точность сообщений XCode об ошибках при разработке. Это особенно заметно при работе с кодом SwiftUI, где Swift часто выдает ложноположительные сообщения об ошибках.
Для примера рассмотрим такой код:
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)
}
}
}
Здесь мы пытаемся связать view TextField c целым State свойством, что неверно. Swift 5.1 выдаст в таком случае ошибку для модификатора frame() 'Int' is not convertible to 'CGFloat?’, но Swift 5.2 и последующие верно распознают ошибку в биндинге $name: Cannot convert value of type 'Binding' to expected argument type 'Binding’.
Krypt
Не нравится мне, куда Swift идёт в своём развитии. (впрочем, как язык он не нравился мне никогда): Вместо добавления новых фич добавляется дублирующий функционал. лучше бы допилили рефлексию до рабочего состояния: всё рабочее, что есть в языке — унаследовано от ObjC и реализовано в ObjC Runtime.