Swift 5.2. Aperçu de toutes les modifications

Fin mars, Swift 5.2 est sorti pour Xcode 11.4. Il a amélioré le diagnostic des erreurs, l'analyse des dépendances et les fonctionnalités étendues de SwiftPM. Un aperçu de certains changements a déjà été publié sur Habré , dans le même article l'évolution de la langue elle-même avec des exemples d'utilisation possibles est considérée.


 

SE-0249 KeyPath en tant que fonction


Le raccourci place keyPath dans une fonction, où le paramètre d'entrée est l'objet lui-même et le résultat est sa propriété.

Regardons un exemple. Créez un tableau de modèles simples:

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

Filtrez le tableau par la propriété isHidden. Voici 3 exemples avec le même résultat:

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

La fonctionnalité déclarée ne fonctionne pas dans l'exemple suivant:

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

De plus, contrairement à keyPath, la saisie semi-automatique ne fonctionne pas.

Il est pratique à utiliser pour travailler avec des tableaux dans des fonctions telles que filtrer, mapper, réduire, trier, etc.

Les indices SR-6118 peuvent contenir des paramètres par défaut


Tous les paramètres de fonction peuvent être réglés sur la valeur par défaut. Créez une structure Box qui contient un tableau d'éléments et une fonction d'indice pour y accéder.

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

Maintenant, pour accéder au premier élément, vous pouvez omettre l'index:

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

Les fonctions locales SR-2189 prennent en charge les paramètres par défaut de la visibilité externe


Dans la pratique quotidienne, les fonctions locales sont rarement utilisées. Il peut être difficile de justifier leur utilisation. Néanmoins, j'avoue que, dans certains cas, cela peut être utile.

Par exemple, nous allons créer une fonction à l'intérieur de laquelle nous décrirons une fonction locale:

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

En tant que paramètre par défaut de la fonction CalculateWidth, nous pouvons utiliser les valeurs de la fonction getXPosition.

SE-0253 Utilisation de valeurs comme fonctions


La fonctionnalité, similaire à @dynamicCallable, vous permet d'utiliser une valeur en tant que fonction. Mais c'est une implémentation de "l'appel statique".

En pratique, tout est extrêmement simple: pour appeler des fonctions, vous pouvez faire référence aux valeurs comme des méthodes.

Créer une structure de joueur:

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

Créez une instance de Player et faites-y référence en tant que fonction:

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

Dans le même temps, il est interdit de cast, et donc de passer un objet en fonction:

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

Vous pouvez ajouter autant de méthodes que callAsFunction à une classe, une structure ou un protocole:

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

L'application est possible dans les valeurs qui sont des fonctions mathématiques, des expressions complexes ou dans les cas où la valeur a une fonction dominante. Néanmoins, vous ne devez pas abuser de cette fonctionnalité, car elle peut être trompeuse.

SR-4206 Correction d'un bug avec la fonction remplaçant par un paramètre générique


Maintenant, en remplaçant la fonction, vous ne pouvez pas modifier ou ajouter des restrictions sur le type générique. Par exemple, créez une classe CarBuilder et héritez-en en remplaçant la méthode 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 L'extension d'un protocole sans restrictions de classe hérite de cette restriction


Dans le cas de restrictions sur Self, l'extension appliquera la restriction.

Par exemple, créez le protocole Menu et extension avec une restriction 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 }
  }
}

En même temps, le passeur n'est pas mutant, comme si le protocole avait une restriction de classe.

SR-11429 Casting pour fonctions avec étiquettes


Lors du transtypage, les fonctions de saisie des libellés sont désormais supprimées. Grâce à cela, vous pouvez caster des fonctions avec des étiquettes. Auparavant, cette fonctionnalité ne fonctionnait que dans les fonctions sans étiquettes:

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

Cependant, les valeurs par défaut ne seront pas enregistrées:

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

Cela s'applique également aux génériques, donc ce code n'est plus valide.

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 La fonction filter (_ :) est appelée dans l'ordre attendu dans les collections paresseuses


Créez une collection paresseuse et appelez la méthode count:

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

Avant, le résultat de l'appel était le contraire: B A.

SR-2790 Un certain nombre d'initialiseurs de type de la famille UnsafePointer / UnsafeBufferPointer émettent désormais un avertissement


La restriction s'applique aux chaînes, aux tableaux et aux paramètres d'entrée qui n'existent pas en dehors de l'appel de fonction.

Créez une structure qui prend UnsafePointer comme paramètre lors de l'initialisation:

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

Il y avait 9 changements dans cette version, cependant, ils ont définitivement introduit de nouvelles fonctionnalités. Je suppose que le plus utilisé des actuels sera KeyPath en tant que fonction.

Et nous attendons avec impatience la prochaine version. Apparemment, il prendra en charge Swift sur Windows et des fonctionnalités intéressantes apparaîtront, telles que l'utilisation de self dans les fermetures d'échappement sans accéder via self. * ( SE-0269 ) et la fonctionnalité des génériques ( SE-0267 ) se développera .

La source

All Articles