В конце марта вышел релиз Swift 5.2 для Xcode 11.4. В нём улучшена диагностика ошибок, анализ зависимостей, расширен функционал SwiftPM. Обзор некоторых изменений уже был опубликован на Хабре, в этой же статье рассмотрена эволюция самого языка с возможными примерами использования.

Шорткат кастит keyPath в функцию, где входящий параметр — сам объект, а результат — его свойство.
Давайте смотреть на примере. Создадим массив из простых моделей:
Отфильтруем массив по свойству isHidden. Ниже приведены 3 примера с одинаковым результатом:
Заявленная функциональность не работает в следующем примере:
Также, в отличие от keyPath, не работает автокомплит.
Удобно использовать для работы с массивами в таких функциях, как filter, map, reduce, sort и аналогичных.
Всем параметрам функции можно задать значение по умолчанию. Создадим структуру Box, которая содержит массив элементов, и функцию subscript для обращения к ним.
Теперь для обращения к первому элементу можно опустить индекс:
В повседневной практике локальные функции используются редко. Оправдать их использование бывает сложно. Тем не менее, допускаю, что в определённых случаях это может пригодиться.
Для примера создадим функцию, внутри которой опишем локальную:
В качестве параметра по умолчанию для функции calculateWidth можем использовать значения в границах функции getXPosition.
Функциональность, аналогично @dynamicCallable, позволяет использовать значение в качестве функции. Но является реализацией «статического вызова».
На практике всё крайне просто: для вызова функций можно обращаться к значениям как к методам.
Создадим структуру Player:
Создадим экземпляр Player и обратимся к нему как к функции:
При этом запрещено кастить, а значит, и передавать объект как функцию:
Можно добавить сколько угодно методов с названием callAsFunction к классу, структуре или протоколу:
Применение возможно в значениях, которые представляют собой математические функции, сложные выражения или же в случаях, где значение имеет одну доминирующую функцию. Всё же не стоит злоупотреблять данным функционалом, так как он может вводить в заблуждение.
Теперь, переопределяя функцию, нельзя изменить или добавить ограничения на generic-тип. В качестве примера создадим класс CarBuilder и наследуемся от него, переопределив метод add:
В случае наложения ограничения на Self, расширение применит ограничение.
К примеру, создадим протокол Menu и extension с ограничением по классу:
При этом сеттер — nonmutating, как будто протокол имеет ограничение на класс.
При кастинге функции к типу лейблы теперь убираются. Благодаря этому можно кастить функции с лейблами. Прежде данная функциональность работала только в функциях без лейблов:
При этом значения по умолчанию сохранить не выйдет:
Это применимо и к generic-ам, поэтому данный код больше не валиден.
Создадим lazy-коллекцию и вызовем метод count:
Прежде результат вызова был обратный: B A.
Ограничение действует на строки, массивы и inout-параметры, которые не существуют за пределами вызова функции.
Создадим структуру, принимающую UnsafePointer в качестве параметра при инициализации:
Всего изменений в этой версии было 9, тем не менее, они определённо внесли новую функциональность. Предполагаю, что самым используемым из текущих будет KeyPath как функция.
А мы с нетерпением ждём следующую версию. Судя по всему, в ней будет поддержка Swift на Windows и появятся интересные фичи, такие как использование self в escaping-замыканиях без обращения через self.* (SE-0269) и расширится функциональность generic-ов (SE-0267).
И��точник

SE-0249 KeyPath как функция
Шорткат кастит keyPath в функцию, где входящий параметр — сам объект, а результат — его свойство.
Давайте смотреть на примере. Создадим массив из простых моделей:
struct Model { let isHidden: Bool } let modelArray = [Model(isHidden: true), Model(isHidden: false)]
Отфильтруем массив по свойству isHidden. Ниже приведены 3 примера с одинаковым результатом:
// 1 let funcIsHidden: (Model) -> Bool = \.isHidden modelArray.filter(funcIsHidden) // 2 modelArray.filter(\.isHidden as (Model) -> Bool) // 3 modelArray.filter(\.isHidden)
Заявленная функциональность не работает в следующем примере:
// Валидный код let selfFunc: (Model) -> Model = \.self modelArray.compactMap(selfFunc) // ERROR: Expression type '(Model) -> Model' is ambiguous without more context modelArray.compactMap(\.self as (Model) -> Model) // ERROR: Key path value type 'Optional<_>' cannot be converted to contextual type '_' modelArray.compactMap(\.self)
Также, в отличие от keyPath, не работает автокомплит.
Удобно использовать для работы с массивами в таких функциях, как filter, map, reduce, sort и аналогичных.
SR-6118 Subscripts может содержать параметры по умолчанию
Всем параметрам функции можно задать значение по умолчанию. Создадим структуру Box, которая содержит массив элементов, и функцию subscript для обращения к ним.
struct Box { let items: [String] subscript(_ index: Int = 0) -> String { items[index] } }
Теперь для обращения к первому элементу можно опустить индекс:
let box = Box(items: ["laptop, , mobile phone"]) let firstItem = box[] // "laptop"
SR-2189 Локальные функции поддерживают параметры по умолчанию из внешнего предела видимости
В повседневной практике локальные функции используются редко. Оправдать их использование бывает сложно. Тем не менее, допускаю, что в определённых случаях это может пригодиться.
Для примера создадим функцию, внутри которой опишем локальную:
func getXPosition() { func calculateWidth(x: CGFloat = minX) -> CGFloat { ... } let minX: CGFloat = 0 ... }
В качестве параметра по умолчанию для функции calculateWidth можем использовать значения в границах функции getXPosition.
SE-0253 Использование значений в качестве функций
Функциональность, аналогично @dynamicCallable, позволяет использовать значение в качестве функции. Но является реализацией «статического вызова».
На практике всё крайне просто: для вызова функций можно обращаться к значениям как к методам.
Создадим структуру Player:
struct Player { private(set) var isRunning = false mutating func callAsFunction() { isRunning.toggle() } }
Создадим экземпляр Player и обратимся к нему как к функции:
var player = Player() print(player.isRunning) // false player() print(player.isRunning) // true
При этом запрещено кастить, а значит, и передавать объект как функцию:
// ERROR: Cannot convert value of type 'Player' to type '() -> Void' in coercion let playerAsFunc = player as () -> Void
Можно добавить сколько угодно методов с названием callAsFunction к классу, структуре или протоколу:
extension Player { func callAsFunction(isRunning: Bool = false) throws { ... } func callAsFunction() -> Bool { ... } }
Применение возможно в значениях, которые представляют собой математические функции, сложные выражения или же в случаях, где значение имеет одну доминирующую функцию. Всё же не стоит злоупотреблять данным функционалом, так как он может вводить в заблуждение.
SR-4206 Исправлен баг с переопределением функции с generic-параметром
Теперь, переопределяя функцию, нельзя изменить или добавить ограничения на generic-тип. В качестве примера создадим класс CarBuilder и наследуемся от него, переопределив метод add:
protocol PowerfullEngine { } class CarBuilder { func add<T>(engine: T) {} } class MercedesBuilder: CarBuilder { // Валидный код override func add<T>(engine: T) {} // ERROR: Overridden method 'add' has generic signature <T where T : PowerfullEngine> which is incompatible with base method's generic signature <T>; expected generic signature to be <T> override func add<T: PowerfullEngine>(engine: T) {} }
SR-11298 Расширение протокола без ограничений на класс наследует это ограничение
В случае наложения ограничения на Self, расширение применит ограничение.
К примеру, создадим протокол Menu и extension с ограничением по классу:
protocol Menu {} class SideMenu: Menu { var sectionHeight = 0 } extension Menu where Self: SideMenu { var menuHeight: Int { get { sectionHeight * 20 } set { sectionHeight = newValue / 20 } } }
При этом сеттер — nonmutating, как будто протокол имеет ограничение на класс.
SR-11429 Кастинг для функций с лейблами
При кастинге функции к типу лейблы теперь убираются. Благодаря этому можно кастить функции с лейблами. Прежде данная функциональность работала только в функциях без лейблов:
func setX(x: Int) {} (setX as (Int) -> Void)(5)
При этом значения по умолчанию сохранить не выйдет:
func setPoint(x: Int, y: Int = 0) {} (setPoint as (Int, Int) -> Void)(5, 1)
Это применимо и к generic-ам, поэтому данный код больше не валиден.
typealias Point<T> = T func set(x: Int) {} // Валидный код (set as Point)(5) // ERROR: Extraneous argument label 'x:' in call (set as Point)(x: 5)
SR-11841 Функция filter(_:) вызывается в ожидаемом порядке в lazy-коллекциях
Создадим lazy-коллекцию и вызовем метод count:
let lazyArray = [0] .lazy .filter { _ in print("A"); return true } .filter { _ in print("B"); return true } // Результат A B _ = lazyArray.count
Прежде результат вызова был обратный: B A.
SR-2790 Ряд инициализаторов типов семейства UnsafePointer / UnsafeBufferPointer теперь выдаёт предупреждение
Ограничение действует на строки, массивы и inout-параметры, которые не существуют за пределами вызова функции.
Создадим структуру, принимающую UnsafePointer в качестве параметра при инициализации:
struct Game { let info: UnsafePointer<Int8> } func createGame() { var i: Int8 = 0 // WARNING: Initialization of 'UnsafePointer<Int8>' results in a dangling pointer _ = Game(info: .init(&i)) // WARNING: Passing '[Int8]' to parameter, but argument 'character' should be a pointer that outlives the call to 'init(character:)' _ = Game(info: [1, 2]) // WARNING: Passing 'String' to parameter, but argument 'character' should be a pointer that outlives the call to 'init(character:) _ = Game(info: "") }
Всего изменений в этой версии было 9, тем не менее, они определённо внесли новую функциональность. Предполагаю, что самым используемым из текущих будет KeyPath как функция.
А мы с нетерпением ждём следующую версию. Судя по всему, в ней будет поддержка Swift на Windows и появятся интересные фичи, такие как использование self в escaping-замыканиях без обращения через self.* (SE-0269) и расширится функциональность generic-ов (SE-0267).
И��точник
