KeyPath — is een getypeerd pad naar een eigenschap van een object in Swift, weergegeven als een eersteklas waarde. In tegenstelling tot tekst sleutels in KVC (Key-Value Coding), wordt KeyPath gecontroleerd door de compiler tijdens de compilatiefase: de compiler kent het type van het root-object, het type van de eigenschap en kan garanderen dat het pad bestaat. KeyPath wordt gebruikt in Combine voor reactieve binding, in SwiftUI voor tweerichtingsbinding en in de standaardbibliotheek voor het sorteren en filteren van collecties. Volgens Apple Developer, 2025 is KeyPath een fundamenteel bouwblok voor functioneel programmeren in Swift.
Belangrijkste punten
KeyPath — is een type uit de Swift-standaardbibliotheek dat een pad naar een eigenschap van een object vertegenwoordigt. In tegenstelling tot handmatige toegang tot een eigenschap via een punt (object.property) of een tekst sleutel (value(forKey:)), is KeyPath een eersteklas object: het kan worden doorgegeven als argument, opgeslagen in een variabele en worden gecombineerd.
KeyPath wordt geschreven met een backslash en het pad naar de eigenschap: \Person.name. Het pad kan genest zijn: \Person.address.city. De compiler controleert dat Person een eigenschap name heeft en dat name daadwerkelijk van het type String is. Als de eigenschap niet bestaat of het type onjuist is, wordt de code niet gecompileerd.
struct Person: Codable {
let name: String
let age: Int
var isActive: Bool
}
// KeyPath (alleen lezen)
let namePath: KeyPath<Person, String> = \Person.name
let person = Person(name: "Alice", age: 30, isActive: true)
let name = person[keyPath: namePath]
Toegang tot een eigenschap via KeyPath gebeurt via de subscript object[keyPath: path]. Voor ReadOnly-eigenschappen wordt KeyPath gebruikt. Als de eigenschap wijzigbaar is (var), is WritableKeyPath beschikbaar. Voor referentietypes — ReferenceWritableKeyPath, waarmee de eigenschap via de KeyPath zelf kan worden gewijzigd.
KeyPath heeft een hiërarchie van subtypes die het toegangsniveau tot de eigenschap weerspiegelt. De basis hiërarchie: AnyKeyPath → PartialKeyPath → KeyPath → WritableKeyPath → ReferenceWritableKeyPath. Elk subtype voegt mogelijkheden toe.
KeyPath — alleen lezen. WritableKeyPath — lezen en schrijven voor waardetypes (struct). ReferenceWritableKeyPath — lezen en schrijven voor referentietypes (class). Swift kiest automatisch het juiste subtype op basis van de context.
| Type | Toegang | Objecttype | Wijziging |
|---|---|---|---|
| KeyPath | ReadOnly | Elk | Nee |
| WritableKeyPath | ReadWrite | Waardetype (inout) | Via mutating context |
| ReferenceWritableKeyPath | ReadWrite | Referentietype | Directe toewijzing |
// WritableKeyPath voor waardetype
var mutablePerson = Person(name: "Bob", age: 25, isActive: false)
let writablePath: WritableKeyPath<Person, Bool> = \Person.isActive
mutablePerson[keyPath: writablePath] = true
// ReferenceWritableKeyPath voor klasse
class User: NSObject {
@objc dynamic var name: String = ""
}
let user = User()
let refPath: ReferenceWritableKeyPath<User, String> = \User.name
user[keyPath: refPath] = "Charlie"
Belangrijk verschil: voor waardetypes vereist instellen via KeyPath var (inout-context), en voor referentietypes — een wijzigbare eigenschap. Dit komt overeen met de algemene semantiek van Swift: waardetypes worden op waarde doorgegeven, dus wijziging vereist mutabiliteit van de container.
KeyPath speelt een centrale rol in Combine en SwiftUI. In Combine gebruikt de operator assign KeyPath om de waarde van een uitgever aan een eigenschap van een object te binden. In SwiftUI gebruikt Binding KeyPath voor tweerichtingsbinding van model en weergave.
De operator assign(to:on:) accepteert een ReferenceWritableKeyPath en een object. Wanneer de uitgever een waarde verzendt, wordt deze automatisch naar de opgegeven eigenschap geschreven. Dit is een declaratieve manier om status te beheren zonder handmatige toewijzingen.
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 gebruikt @Binding KeyPath om de bovenliggende en onderliggende View te verbinden. De ouder geeft een Binding
KeyPath maakt het mogelijk flexibele en typeveilige sorteer- en filterfuncties te schrijven. In plaats van telkens een closure door te geven, kunt u een KeyPath naar de eigenschap doorgeven waarop u de collectie wilt sorteren. Dit maakt de code schoner en vermindert duplicatie.
De functie 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)
Een dergelijke implementatie sorteert op elke Comparable-eigenschap zonder closures te schrijven. Voor sorteren op achternaam volstaat het om KeyPath te wijzigen: \Person.lastName. Als u op een aangepaste sleutel moet sorteren (bijv. lengte van de naam), wordt map binnenin gebruikt — de ontwikkelaar kan nog steeds zijn eigen closure schrijven voor niet-standaard gevallen.
AnyKeyPath — is het basistype in de KeyPath hiërarchie dat informatie over de specifieke typen van de root en waarde wist. AnyKeyPath weet niet op welk objecttype het wordt aangeroepen en welk waardetype het retourneert. Dit is handig voor het opslaan van heterogene KeyPaths in collecties en voor reflectie.
AnyKeyPath wordt gebruikt wanneer u een lijst met paden naar verschillende eigenschappen van verschillende typen moet opslaan. Bijvoorbeeld in een UI-configurator, waar elk pad een specifieke eigenschap configureert en de eigenschapstypen kunnen verschillen. AnyKeyPath maakt uniform werken met alle paden mogelijk.
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 { }
// Maakt het configureren van eigenschappen via AnyKeyPath mogelijk
AnyKeyPath vereist typeconversie bij het ophalen van de waarde, omdat het specifieke type is gewist. Voor volledige typeveiligheid heeft getypeerd KeyPath
KeyPath is aanzienlijk performanter dan tekst sleutels van KVC (value(forKey:)). Tekst sleutels worden berekend via het runtime-mechanisme van Objective-C, inclusief het parseren van tekst, zoeken in runtime en typeconversie. KeyPath is een statisch mechanisme gebaseerd op native Swift-structuren.
Volgens tests is KeyPath gemiddeld 10-20 keer sneller dan value(forKey:) voor toegang tot eigenschappen. Het verschil is te wijten aan het ontbreken van runtime-zoekopdracht en dynamische dispatch. KeyPath heeft rechtstreeks toegang tot de offset van de eigenschap in het geheugen met voldoende optimalisatie van de compiler.
func readWithKeyPath(person: Person) -> String {
person[keyPath: \Person.name]
}
func readWithKVC(person: Person) -> String {
person.value(forKey: "name") as! String
}
KVC is niet alleen langzamer, maar ook onveilig wat betreft typen: de tekst „name“ bestaat mogelijk niet en de conversie as! String kan mislukken. KeyPath garandeert het bestaan van de eigenschap en de juistheid van het type tijdens de compilatie. Voor prestatiekritieke delen (animaties, lijsten met duizenden elementen) is KeyPath de enige juiste keuze.
Veelgestelde vragen
KeyPath — is een verwijzing naar een eigenschap van een object die kan worden doorgegeven als een gewone waarde. In plaats van person.name te schrijven, maakt u het pad \Person.name en gebruikt u het om de eigenschap in elk exemplaar van Person te lezen of te schrijven.
KeyPath — alleen lezen. WritableKeyPath — voor lezen en schrijven van waardetypes (struct) via inout-context. ReferenceWritableKeyPath — voor lezen en schrijven van referentietypes (class) zonder extra voorwaarden.
KeyPath wordt veel gebruikt in Combine (assign(to:on:)), SwiftUI (Binding, FocusState), het sorteren van collecties (sorted(by:)), Core Data (NSSortDescriptor) en voor veilige toegang tot geneste eigenschappen.
Ja, KeyPath is volledig typeveilig. Een fout in het pad (typefout, onjuist type) wordt gedetecteerd tijdens de compilatie, niet in runtime. value(forKey:) gebruikt tekst en kan mislukken met NSUndefinedKeyException als de sleutel niet bestaat.
Direct — nee, Swift ondersteunt geen dynamische creatie van KeyPath uit tekst. U kunt echter AnyKeyPath gebruiken voor het opslaan en doorgeven van statisch verkregen KeyPaths. Voor dynamische toegang tot eigenschappen via tekst gebruikt u value(forKey:) met Objective-C runtime.
Samenvatting
We ontwikkelen een mobiele applicatie turnkey
IT Sectr creëert sinds 2017 iOS- en Android-applicaties voor startups en bedrijven. We adviseren u en stellen de beste oplossing voor.
Lees ook