KeyPath ist ein typisierter Pfad zu einer Eigenschaft eines Objekts in Swift, dargestellt als First-Class-Wert. Im Gegensatz zu String-Schlüsseln in KVC (Key-Value Coding) wird KeyPath vom Compiler zur Übersetzungszeit überprüft: Der Compiler kennt den Typ des Stammobjekts, den Typ der Eigenschaft und kann garantieren, dass der Pfad existiert. KeyPath wird in Combine für reaktive Bindung, in SwiftUI für bidirektionale Bindung und in der Standardbibliothek zum Sortieren und Filtern von Sammlungen verwendet. Laut Apple Developer, 2025 ist KeyPath ein grundlegender Baustein für funktionale Programmierung in Swift.
Wichtige Punkte
KeyPath ist ein Typ aus der Swift-Standardbibliothek, der einen Pfad zu einer Eigenschaft eines Objekts darstellt. Im Gegensatz zum manuellen Zugriff auf eine Eigenschaft mit Punktnotation (object.property) oder einem String-Schlüssel (value(forKey:)) ist KeyPath ein First-Class-Objekt: Es kann als Argument übergeben, in einer Variable gespeichert und kombiniert werden.
Ein KeyPath wird mit einem Backslash gefolgt vom Pfad zur Eigenschaft geschrieben: \Person.name. Der Pfad kann verschachtelt sein: \Person.address.city. Der Compiler prüft, dass Person eine Eigenschaft name hat und dass name tatsächlich ein String ist. Wenn die Eigenschaft nicht existiert oder der Typ falsch ist, wird der Code nicht kompiliert.
struct Person: Codable {
let name: String
let age: Int
var isActive: Bool
}
// KeyPath (schreibgeschützt)
let namePath: KeyPath<Person, String> = \Person.name
let person = Person(name: "Alice", age: 30, isActive: true)
let name = person[keyPath: namePath]
Der Zugriff auf eine Eigenschaft über KeyPath erfolgt über den Subscript object[keyPath: path]. Für schreibgeschützte Eigenschaften wird KeyPath verwendet. Wenn die Eigenschaft veränderlich (var) ist, ist WritableKeyPath verfügbar. Für Referenztypen ermöglicht ReferenceWritableKeyPath das direkte Ändern der Eigenschaft über den KeyPath.
KeyPath hat eine Subtyp-Hierarchie, die die Zugriffsebene auf die Eigenschaft widerspiegelt. Die Basishierarchie ist: AnyKeyPath → PartialKeyPath → KeyPath → WritableKeyPath → ReferenceWritableKeyPath. Jeder Subtyp fügt Fähigkeiten hinzu.
KeyPath — schreibgeschützt. WritableKeyPath — Lesen und Schreiben für Wertetypen (struct). ReferenceWritableKeyPath — Lesen und Schreiben für Referenztypen (class). Swift wählt automatisch den richtigen Subtyp basierend auf dem Kontext aus.
| Typ | Zugriff | Objekttyp | Änderung |
|---|---|---|---|
| KeyPath | Schreibgeschützt | Beliebig | Nein |
| WritableKeyPath | Lesen und Schreiben | Wertetyp (inout) | Durch mutating-Kontext |
| ReferenceWritableKeyPath | Lesen und Schreiben | Referenztyp | Direkte Zuweisung |
// WritableKeyPath für Wertetyp
var mutablePerson = Person(name: "Bob", age: 25, isActive: false)
let writablePath: WritableKeyPath<Person, Bool> = \Person.isActive
mutablePerson[keyPath: writablePath] = true
// ReferenceWritableKeyPath für Klasse
class User: NSObject {
@objc dynamic var name: String = ""
}
let user = User()
let refPath: ReferenceWritableKeyPath<User, String> = \User.name
user[keyPath: refPath] = "Charlie"
Ein wichtiger Unterschied: Bei Wertetypen erfordert das Setzen über KeyPath var (inout-Kontext), während bei Referenztypen nur eine veränderliche Eigenschaft benötigt wird. Dies entspricht der allgemeinen Semantik von Swift: Wertetypen werden by value übergeben, daher erfordert Mutation die Veränderbarkeit des Containers.
KeyPath spielt eine zentrale Rolle in Combine und SwiftUI. In Combine verwendet der assign-Operator KeyPath, um den Wert eines Publishers an eine Objekteigenschaft zu binden. In SwiftUI verwendet Binding KeyPath für die bidirektionale Verknüpfung zwischen Modell und Ansicht.
Der Operator assign(to:on:) akzeptiert einen ReferenceWritableKeyPath und ein Objekt. Wenn der Publisher einen Wert sendet, wird er automatisch in die angegebene Eigenschaft geschrieben. Dies ist eine deklarative Art, den Zustand ohne manuelle Zuweisungen zu verwalten.
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)
}
}
In SwiftUI verwendet @Binding KeyPath, um eine Eltern- und Kind-Ansicht zu verbinden. Die Eltern-Ansicht übergibt ein Binding
KeyPath ermöglicht das Schreiben flexibler und typsicherer Sortier- und Filterfunktionen. Anstatt jedes Mal einen Closure zu übergeben, können Sie einen KeyPath zu der Eigenschaft übergeben, nach der Sie die Sammlung sortieren möchten. Dies macht den Code sauberer und reduziert Redundanz.
Die Funktion sorted
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)
Diese Implementierung sortiert nach jeder Comparable-Eigenschaft, ohne Closures schreiben zu müssen. Zum Sortieren nach Nachname ändern Sie einfach den KeyPath: \Person.lastName. Wenn nach einem benutzerdefinierten Schlüssel sortiert werden soll (z. B. Namenslänge), wird intern map verwendet — der Entwickler kann für nicht standardmäßige Fälle immer noch seinen eigenen Closure schreiben.
AnyKeyPath ist der Basistyp in der KeyPath-Hierarchie, der Informationen über die spezifischen Wurzel- und Wertetypen löscht. AnyKeyPath weiß nicht, auf welchem Objekttyp es aufgerufen wird oder welchen Werttyp es zurückgibt. Dies ist nützlich zum Speichern heterogener KeyPaths in Sammlungen und für Reflektion.
AnyKeyPath wird verwendet, wenn Sie eine Liste von Pfaden zu verschiedenen Eigenschaften verschiedener Typen speichern müssen. Zum Beispiel in einem UI-Konfigurator, wo jeder Pfad eine bestimmte Eigenschaft konfiguriert und die Eigenschaftstypen unterschiedlich sein können. AnyKeyPath ermöglicht ein einheitliches Arbeiten mit allen Pfaden.
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 { }
// Ermöglicht das Konfigurieren von Eigenschaften über AnyKeyPath
AnyKeyPath erfordert Typumwandlung beim Abrufen des Werts, da der spezifische Typ gelöscht wurde. Für vollständige Typsicherheit ist ein typisierter KeyPath
KeyPath ist deutlich effizienter als KVC-String-Schlüssel (value(forKey:)). String-Schlüssel werden über den Objective-C-Runtime-Mechanismus aufgelöst, einschließlich String-Parsing, Runtime-Lookup und Typumwandlung. KeyPath ist ein statischer Mechanismus, der auf nativen Swift-Strukturen basiert.
Laut Benchmarks ist KeyPath im Durchschnitt 10-20 Mal schneller als value(forKey:) für den Zugriff auf Eigenschaften. Der Unterschied ist auf das Fehlen von Runtime-Lookup und dynamischem Dispatch zurückzuführen. KeyPath greift bei ausreichender Compiler-Optimierung direkt auf den Property-Offset im Speicher zu.
func readWithKeyPath(person: Person) -> String {
person[keyPath: \Person.name]
}
func readWithKVC(person: Person) -> String {
person.value(forKey: "name") as! String
}
KVC ist nicht nur langsamer, sondern auch typunsicher: Der String „name“ könnte nicht existieren und die as! String-Umwandlung könnte abstürzen. KeyPath garantiert die Existenz der Eigenschaft und die Typkorrektheit zur Kompilierzeit. Für leistungskritische Bereiche (Animationen, Listen mit Tausenden von Elementen) ist KeyPath die einzig richtige Wahl.
Häufig gestellte Fragen
KeyPath ist ein Zeiger auf eine Objekteigenschaft, der als normaler Wert übergeben werden kann. Anstatt person.name zu schreiben, erstellen Sie einen Pfad \Person.name und verwenden ihn, um die Eigenschaft auf jeder Person-Instanz zu lesen oder zu schreiben.
KeyPath — schreibgeschützt. WritableKeyPath — zum Lesen und Schreiben von Wertetypen (struct) über inout-Kontext. ReferenceWritableKeyPath — zum Lesen und Schreiben von Referenztypen (class) ohne zusätzliche Bedingungen.
KeyPath wird umfassend in Combine (assign(to:on:)), SwiftUI (Binding, FocusState), Sammlungssortierung (sorted(by:)), Core Data (NSSortDescriptor) und für sicheren Zugriff auf verschachtelte Eigenschaften verwendet.
Ja, KeyPath ist vollständig typsicher. Ein Fehler im Pfad (Tippfehler, falscher Typ) wird zur Kompilierzeit erkannt, nicht zur Laufzeit. value(forKey:) verwendet Strings und kann mit NSUndefinedKeyException abstürzen, wenn der Schlüssel nicht existiert.
Nicht direkt — Swift unterstützt keine dynamische KeyPath-Erstellung aus einem String. Sie können jedoch AnyKeyPath verwenden, um statisch erhaltene KeyPaths zu speichern und zu übergeben. Für dynamischen Eigenschaftszugriff per String verwenden Sie value(forKey:) mit dem Objective-C-Runtime.
Zusammenfassung
Wir entwickeln eine mobile Applikation schlüsselfertig
IT Sectr entwickelt seit 2017 iOS- und Android-Apps für Startups und Unternehmen. Wir beraten Sie und schlagen die beste Lösung vor.
Lesen Sie auch