KeyPath : concepts clés et accès typé

Auteur : IT Sectr Publié le : 2026-06-18 Temps de lecture : 10 min

KeyPath est un chemin typé vers une propriété d’un objet en Swift, représenté comme une valeur de première classe. Contrairement aux clés chaîne de caractères en KVC (Key-Value Coding), KeyPath est vérifié par le compilateur au moment de la compilation : le compilateur connaît le type de l’objet racine, le type de la propriété et peut garantir que le chemin existe. KeyPath est utilisé dans Combine pour la liaison réactive, dans SwiftUI pour la liaison bidirectionnelle et dans la bibliothèque standard pour trier et filtrer les collections. Selon Apple Developer, 2025, KeyPath est un bloc fondamental pour la programmation fonctionnelle en Swift.

Points clés

  • KeyPath — un chemin typé vers une propriété, vérifié par le compilateur
  • WritableKeyPath — un sous-type de KeyPath pour les propriétés avec accès en écriture
  • ReferenceWritableKeyPath — pour les propriétés mutables des types référence
  • Combine utilise KeyPath pour lier les publishers aux propriétés d’objets
  • AnyKeyPath — une variante type-erased pour stocker KeyPath dans des collections

Qu’est-ce que KeyPath en Swift ?

KeyPath est un type de la bibliothèque standard Swift qui représente un chemin vers une propriété d’un objet. Contrairement à l’accès manuel à une propriété par notation pointée (object.property) ou une clé chaîne (value(forKey:)), KeyPath est un objet de première classe : il peut être passé comme argument, stocké dans une variable et composé.

Syntaxe de KeyPath

Un KeyPath s’écrit avec un antislash suivi du chemin vers la propriété : \Person.name. Le chemin peut être imbriqué : \Person.address.city. Le compilateur vérifie que Person a une propriété name et que name est bien un String. Si la propriété n’existe pas ou que le type est incorrect, le code ne compile pas.

swift
struct Person: Codable {
    let name: String
    let age: Int
    var isActive: Bool
}

// KeyPath (lecture seule)
let namePath: KeyPath<Person, String> = \Person.name
let person = Person(name: "Alice", age: 30, isActive: true)
let name = person[keyPath: namePath]

L’accès à une propriété via KeyPath se fait via le subscript object[keyPath: path]. Pour les propriétés en lecture seule, KeyPath est utilisé. Si la propriété est mutable (var), WritableKeyPath est disponible. Pour les types référence, ReferenceWritableKeyPath permet de modifier la propriété directement via le KeyPath.

Hiérarchie KeyPath : WritableKeyPath et ReferenceWritableKeyPath

KeyPath possède une hiérarchie de sous-types reflétant le niveau d’accès à la propriété. La hiérarchie de base est : AnyKeyPath → PartialKeyPath → KeyPath → WritableKeyPath → ReferenceWritableKeyPath. Chaque sous-type ajoute des capacités.

Niveaux d’accès KeyPath

KeyPath — lecture seule. WritableKeyPath — lecture et écriture pour les types valeur (struct). ReferenceWritableKeyPath — lecture et écriture pour les types référence (class). Swift sélectionne automatiquement le bon sous-type en fonction du contexte.

TypeAccèsType d’objetMutation
KeyPathLecture seuleQuelconqueNon
WritableKeyPathLecture et écritureType valeur (inout)Via contexte mutating
ReferenceWritableKeyPathLecture et écritureType référenceAssignation directe
swift
// WritableKeyPath pour type valeur
var mutablePerson = Person(name: "Bob", age: 25, isActive: false)
let writablePath: WritableKeyPath<Person, Bool> = \Person.isActive
mutablePerson[keyPath: writablePath] = true

// ReferenceWritableKeyPath pour classe
class User: NSObject {
    @objc dynamic var name: String = ""
}
let user = User()
let refPath: ReferenceWritableKeyPath<User, String> = \User.name
user[keyPath: refPath] = "Charlie"

Une différence importante : pour les types valeur, la définition via KeyPath nécessite var (contexte inout), tandis que pour les types référence, seule une propriété mutable est nécessaire. Cela correspond à la sémantique générale de Swift : les types valeur sont passés par valeur, donc la mutation nécessite la mutabilité du conteneur.

KeyPath dans Combine et SwiftUI

KeyPath joue un rôle central dans Combine et SwiftUI. Dans Combine, l’opérateur assign utilise KeyPath pour lier la valeur d’un publisher à une propriété d’un objet. Dans SwiftUI, Binding utilise KeyPath pour la liaison bidirectionnelle entre le modèle et la vue.

Assign dans Combine

L’opérateur assign(to:on:) accepte un ReferenceWritableKeyPath et un objet. Lorsque le publisher émet une valeur, elle est automatiquement écrite dans la propriété spécifiée. C’est une façon déclarative de gérer l’état sans assignations manuelles.

swift
import Combine

class SettingsViewModel: ObservableObject {
    @Published var volume: Float = 0.5
    var cancellables = Set<AnyCancellable>()
    
    func bindSlider(publisher: AnyPublisher<Float, Never>) {
        publisher
            .assign(to: \SettingsViewModel.volume, on: self)
            .store(in: &cancellables)
    }
}

Dans SwiftUI, @Binding utilise KeyPath pour connecter une vue parent et une vue enfant. Le parent transmet un Binding, créé via Binding(get:set:) ou la projection ($). KeyPath permet à SwiftUI de suivre automatiquement les changements et de mettre à jour l’interface sans effort supplémentaire.

Utiliser KeyPath pour trier et filtrer

KeyPath permet d’écrire des fonctions de tri et de filtrage flexibles et type-safe. Au lieu de passer une closure à chaque fois, vous pouvez passer un KeyPath vers la propriété par laquelle vous souhaitez trier la collection. Cela rend le code plus propre et réduit la duplication.

Fonction générique de tri par KeyPath

La fonction sorted accepte KeyPath où Value : Comparable. Cela garantit que la propriété de tri prend en charge la comparaison. Le compilateur le vérifie au moment de la compilation.

swift
extension Sequence {
    func sorted<Value: Comparable>(
        by keyPath: KeyPath<Element, Value>,
        ascending: Bool = true
    ) -> [Element] {
        ascending
            ? self.sorted { $0[keyPath: keyPath] < $1[keyPath: keyPath] }
            : self.sorted { $0[keyPath: keyPath] > $1[keyPath: keyPath] }
    }
}

let sortedByName = people.sorted(by: \Person.name)
let sortedByAgeDesc = people.sorted(by: \Person.age, ascending: false)

Cette implémentation trie par n’importe quelle propriété Comparable sans écrire de closures. Pour trier par nom de famille, il suffit de changer le KeyPath : \Person.lastName. Si un tri par clé personnalisée est nécessaire (par exemple, longueur du nom), un map est utilisé en interne — le développeur peut toujours écrire sa propre closure pour les cas non standard.

AnyKeyPath type-erased

AnyKeyPath est le type de base dans la hiérarchie KeyPath qui efface les informations sur les types spécifiques de racine et de valeur. AnyKeyPath ne sait pas sur quel type d’objet il est appelé ni quel type de valeur il retourne. C’est utile pour stocker des KeyPaths hétérogènes dans des collections et pour la réflexion.

Utilisation d’AnyKeyPath

AnyKeyPath est utilisé lorsque vous devez stocker une liste de chemins vers différentes propriétés de différents types. Par exemple, dans un configurateur d’interface, où chaque chemin configure une propriété spécifique et les types de propriétés peuvent différer. AnyKeyPath permet de travailler uniformément avec tous les chemins.

swift
protocol Configurable {
    func apply(_ keyPath: any PartialKeyPath<Self>, value: any)
}

extension Configurable {
    func configure(_ pairs: (any KeyPath<Self, any>, any)...) {
        for (path, value) in pairs {
            apply(path, value: value)
        }
    }
}

extension Person: Configurable { }
// Permet de configurer les propriétés via AnyKeyPath

AnyKeyPath nécessite un cast de type lors de la récupération de la valeur, car le type spécifique a été effacé. Pour une sécurité de type totale, un KeyPath typé est toujours préférable. AnyKeyPath est un outil pour les cas où les types sont vraiment inconnus au moment de la compilation, comme la sérialisation ou la réflexion.

Performances de KeyPath vs clés chaîne

KeyPath est significativement plus performant que les clés chaîne KVC (value(forKey:)). Les clés chaîne sont résolues via le mécanisme d’exécution Objective-C, incluant l’analyse de la chaîne, la recherche au moment de l’exécution et le cast de type. KeyPath est un mécanisme statique basé sur les structures natives Swift.

Comparaison des performances

Selon des benchmarks, KeyPath est en moyenne 10 à 20 fois plus rapide que value(forKey:) pour l’accès aux propriétés. La différence est due à l’absence de recherche au moment de l’exécution et de dispatch dynamique. KeyPath accède directement au décalage de la propriété en mémoire avec une optimisation suffisante du compilateur.

swift
func readWithKeyPath(person: Person) -> String {
    person[keyPath: \Person.name]
}

func readWithKVC(person: Person) -> String {
    person.value(forKey: "name") as! String
}

KVC est non seulement plus lent, mais aussi non type-safe : la chaîne “name” peut ne pas exister et le cast as! String peut planter. KeyPath garantit l’existence de la propriété et la correction du type au moment de la compilation. Pour les sections critiques en termes de performances (animations, listes de milliers d’éléments), KeyPath est le seul choix correct.

Questions fréquentes

Qu’est-ce que KeyPath en Swift en mots simples ?

KeyPath est un pointeur vers une propriété d’un objet qui peut être passé comme une valeur normale. Au lieu d’écrire person.name, vous créez un chemin \Person.name et l’utilisez pour lire ou écrire la propriété sur n’importe quelle instance de Person.

Quelle est la différence entre KeyPath, WritableKeyPath et ReferenceWritableKeyPath ?

KeyPath — lecture seule. WritableKeyPath — pour lire et écrire des types valeur (struct) via un contexte inout. ReferenceWritableKeyPath — pour lire et écrire des types référence (class) sans conditions supplémentaires.

Où KeyPath est-il utilisé en Swift ?

KeyPath est largement utilisé dans Combine (assign(to:on:)), SwiftUI (Binding, FocusState), le tri de collections (sorted(by:)), Core Data (NSSortDescriptor) et pour l’accès sécurisé aux propriétés imbriquées.

KeyPath est-il plus sûr que value(forKey:) ?

Oui, KeyPath est complètement type-safe. Une erreur dans le chemin (faute de frappe, mauvais type) est détectée au moment de la compilation, pas à l’exécution. value(forKey:) utilise des chaînes et peut planter avec NSUndefinedKeyException si la clé n’existe pas.

Peut-on créer un KeyPath dynamiquement ?

Pas directement — Swift ne prend pas en charge la création dynamique de KeyPath à partir d’une chaîne. Cependant, vous pouvez utiliser AnyKeyPath pour stocker et transmettre des KeyPaths obtenus statiquement. Pour un accès dynamique aux propriétés par chaîne, utilisez value(forKey:) avec l’environnement d’exécution Objective-C.

Résumé

  • KeyPath — un chemin typé vers une propriété, représenté comme un type valeur de première classe
  • Hiérarchie de KeyPath inclut WritableKeyPath et ReferenceWritableKeyPath pour différents niveaux d’accès
  • Combine et SwiftUI utilisent activement KeyPath pour les liaisons réactives
  • Tri et filtrage via KeyPath améliore la lisibilité et la sécurité de type du code
  • AnyKeyPath — une variante type-erased pour stocker des KeyPaths hétérogènes dans des collections
  • Performances de KeyPath sont 10 à 20 fois supérieures à value(forKey:)

Nous développerons une application mobile clé en main

IT Sectr crée des applications iOS et Android pour les startups et les entreprises depuis 2017. Nous vous conseillerons et vous proposerons la meilleure solution.

Discuter du projet

Lisez aussi