Swift 5.2. Visão geral de todas as alterações

No final de março, o Swift 5.2 foi lançado para o Xcode 11.4. Melhorou o diagnóstico de erros, a análise de dependências e a funcionalidade expandida do SwiftPM. Uma visão geral de algumas mudanças já foi publicada no Habré , no mesmo artigo é considerada a evolução da própria linguagem com possíveis exemplos de uso.


 

SE-0249 KeyPath como uma função


O atalho coloca keyPath em uma função, em que o parâmetro de entrada é o próprio objeto e o resultado é sua propriedade.

Vejamos um exemplo. Crie uma matriz de modelos simples:

struct Model {
    let isHidden: Bool
}
 
let modelArray = [Model(isHidden: true), Model(isHidden: false)]

Filtre a matriz pela propriedade isHidden. Abaixo estão 3 exemplos com o mesmo resultado:

// 1
let funcIsHidden: (Model) -> Bool = \.isHidden
modelArray.filter(funcIsHidden)
 
// 2
modelArray.filter(\.isHidden as (Model) -> Bool)
 
// 3
modelArray.filter(\.isHidden)

A funcionalidade declarada não funciona no seguinte exemplo:

//   
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)

Além disso, ao contrário do keyPath, o preenchimento automático não funciona.

É conveniente usar para trabalhar com matrizes em funções como filtrar, mapear, reduzir, classificar e similares.

Subscritos SR-6118 podem conter configurações padrão


Todos os parâmetros de função podem ser configurados para o valor padrão. Crie uma estrutura Box que contenha uma matriz de elementos e uma função subscrita para acessá-los.

struct Box {
    let items: [String]
    
    subscript(_ index: Int = 0) -> String {
        items[index]
    }
}

Agora, para acessar o primeiro elemento, você pode omitir o índice:

let box = Box(items: ["laptop, , mobile phone"])
let firstItem = box[] // "laptop"

As funções locais SR-2189 suportam parâmetros padrão da visibilidade externa


Na prática cotidiana, as funções locais raramente são usadas. Pode ser difícil justificar seu uso. No entanto, admito que, em certos casos, isso pode ser útil.

Por exemplo, criaremos uma função dentro da qual descreveremos uma local:

func getXPosition() {
    func calculateWidth(x: CGFloat = minX) -> CGFloat { ... }
    let minX: CGFloat = 0
    ...
}

Como parâmetro padrão para a função calculWidth, podemos usar os valores na função getXPosition.

SE-0253 Usando valores como funções


A funcionalidade, semelhante a @dynamicCallable, permite que você use um valor como uma função. Mas é uma implementação da "chamada estática".

Na prática, tudo é extremamente simples: para chamar funções, você pode se referir a valores como métodos.

Crie uma estrutura do Player:

struct Player {
    private(set) var isRunning = false
    
    mutating func callAsFunction() {
        isRunning.toggle()
    }
}

Crie uma instância do Player e faça referência a ela como uma função:

var player = Player()
print(player.isRunning) // false
player()
print(player.isRunning) // true

Ao mesmo tempo, é proibido converter e, portanto, transmitir um objeto como uma função:

// ERROR: Cannot convert value of type 'Player' to type '() -> Void' in coercion
let playerAsFunc = player as () -> Void

Você pode adicionar tantos métodos quanto callAsFunction a uma classe, estrutura ou protocolo:

extension Player {
    func callAsFunction(isRunning: Bool = false) throws { ... }
    func callAsFunction() -> Bool { ... }
}

A aplicação é possível em valores que são funções matemáticas, expressões complexas ou nos casos em que o valor tem uma função dominante. No entanto, você não deve abusar dessa funcionalidade, pois ela pode ser enganosa.

SR-4206 Bug corrigido com substituição de função com parâmetro genérico


Agora, substituindo a função, você não pode alterar ou adicionar restrições ao tipo genérico. Como exemplo, crie uma classe CarBuilder e a herde substituindo o método 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 A extensão de um protocolo sem restrições de classe herda essa restrição


No caso de restrições no Self, a extensão aplicará a restrição.

Por exemplo, crie o menu e o protocolo de extensão com uma restrição de classe:

protocol Menu {}
 
class SideMenu: Menu {
  var sectionHeight = 0
}
 
extension Menu where Self: SideMenu {
  var menuHeight: Int {
    get { sectionHeight * 20 }
    set { sectionHeight = newValue / 20 }
  }
}

Ao mesmo tempo, o setter não muda, como se o protocolo tivesse uma restrição de classe.

SR-11429 Fundição para funções com etiquetas


Ao converter funções para digitar etiquetas, agora são removidas. Graças a isso, você pode converter funções com etiquetas. Anteriormente, essa funcionalidade funcionava apenas em funções sem rótulos:

func setX(x: Int) {}
(setX as (Int) -> Void)(5)

No entanto, os valores padrão não serão salvos:

func setPoint(x: Int, y: Int = 0) {}
(setPoint as (Int, Int) -> Void)(5, 1)

Isso também se aplica aos genéricos, portanto esse código não é mais válido.

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 A função filter (_ :) é chamada na ordem esperada em coleções lentas


Crie uma coleção lenta e chame o método count:

let lazyArray = [0]
    .lazy
    .filter { _ in print("A"); return true }
    .filter { _ in print("B"); return true }
 
//  A B
_ = lazyArray.count

Antes, o resultado da ligação era o oposto: B A.

SR-2790 Vários inicializadores de tipo da família UnsafePointer / UnsafeBufferPointer agora emitem um aviso


A restrição se aplica a cadeias, matrizes e parâmetros inout que não existem fora da chamada de função.

Crie uma estrutura que tome UnsafePointer como parâmetro durante a inicialização:

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: "")
}

Houve 9 alterações nesta versão, no entanto, elas definitivamente introduziram novas funcionalidades. Suponho que os mais usados ​​atualmente serão o KeyPath como uma função.

E estamos ansiosos para a próxima versão. Aparentemente, ele oferecerá suporte ao Swift no Windows e recursos interessantes aparecerão, como o uso de self em escapamentos sem acesso através de self. * ( SE-0269 ) e a funcionalidade de genéricos ( SE-0267 ) será expandida .

Fonte

All Articles