KeyPath — är en typad sökväg till en egenskap hos ett objekt i Swift, representerad som ett förstklassigt värde. Till skillnad från textnycklar i KVC (Key-Value Coding) kontrolleras KeyPath av kompilatorn under kompileringsfasen: kompilatorn känner till rotobjektets typ, egenskapens typ och kan garantera att sökvägen finns. KeyPath används i Combine för reaktiv bindning, i SwiftUI för tvåvägsbindning och i standardbiblioteket för sortering och filtrering av samlingar. Enligt Apple Developer, 2025 är KeyPath en grundläggande byggsten för funktionell programmering i Swift.
Huvudpunkter
KeyPath — är en typ från Swift-standardbiblioteket som representerar en sökväg till en egenskap hos ett objekt. Till skillnad från manuell åtkomst till en egenskap via punkt (object.property) eller en textnyckel (value(forKey:)), är KeyPath ett förstklassigt objekt: det kan skickas som argument, lagras i en variabel och kombineras.
KeyPath skrivs med ett bakåtstreck och sökvägen till egenskapen: \Person.name. Sökvägen kan vara nästlad: \Person.address.city. Kompilatorn kontrollerar att Person har en egenskap name och att name verkligen är av typen String. Om egenskapen inte finns eller typen är felaktig kompileras inte koden.
struct Person: Codable {
let name: String
let age: Int
var isActive: Bool
}
// KeyPath (endast läsning)
let namePath: KeyPath<Person, String> = \Person.name
let person = Person(name: "Alice", age: 30, isActive: true)
let name = person[keyPath: namePath]
Åtkomst till en egenskap via KeyPath sker via subscriptet object[keyPath: path]. För ReadOnly-egenskaper används KeyPath. Om egenskapen är föränderlig (var) är WritableKeyPath tillgänglig. För referenstyper — ReferenceWritableKeyPath, som tillåter ändring av egenskapen via själva KeyPath.
KeyPath har en hierarki av undertyper som återspeglar åtkomstnivån till egenskapen. Bashierarkin: AnyKeyPath → PartialKeyPath → KeyPath → WritableKeyPath → ReferenceWritableKeyPath. Varje undertyp lägger till funktioner.
KeyPath — endast läsning. WritableKeyPath — läsning och skrivning för värdetyper (struct). ReferenceWritableKeyPath — läsning och skrivning för referenstyper (class). Swift väljer automatiskt rätt undertyp baserat på kontexten.
| Typ | Åtkomst | Objekttyp | Ändring |
|---|---|---|---|
| KeyPath | ReadOnly | Vilken som helst | Nej |
| WritableKeyPath | ReadWrite | Värdetyp (inout) | Via mutating-kontext |
| ReferenceWritableKeyPath | ReadWrite | Referenstyp | Direkt tilldelning |
// WritableKeyPath för värdetyp
var mutablePerson = Person(name: "Bob", age: 25, isActive: false)
let writablePath: WritableKeyPath<Person, Bool> = \Person.isActive
mutablePerson[keyPath: writablePath] = true
// ReferenceWritableKeyPath för klass
class User: NSObject {
@objc dynamic var name: String = ""
}
let user = User()
let refPath: ReferenceWritableKeyPath<User, String> = \User.name
user[keyPath: refPath] = "Charlie"
Viktig skillnad: för värdetyper kräver inställning via KeyPath var (inout-kontext), och för referenstyper — en föränderlig egenskap. Detta överensstämmer med Swifts allmänna semantik: värdetyper skickas efter värde, så ändring kräver mutabilitet hos behållaren.
KeyPath spelar en central roll i Combine och SwiftUI. I Combine använder operatorn assign KeyPath för att binda en utgivares värde till en objektegenskap. I SwiftUI använder Binding KeyPath för tvåvägsbindning av modell och vy.
Operatorn assign(to:on:) tar emot en ReferenceWritableKeyPath och ett objekt. När utgivaren skickar ett värde skrivs det automatiskt till den angivna egenskapen. Detta är ett deklarativt sätt att hantera tillstånd utan manuella tilldelningar.
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)
}
}
I SwiftUI använder @Binding KeyPath för att koppla samman överordnad och underordnad View. Föräldern skickar ett Binding
KeyPath gör det möjligt att skriva flexibla och typsäkra sorterings- och filtreringsfunktioner. Istället för att skicka en closure varje gång kan du skicka en KeyPath till den egenskap efter vilken du vill sortera samlingen. Detta gör koden renare och minskar duplicering.
Funktionen 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)
En sådan implementering sorterar efter vilken Comparable-egenskap som helst utan att skriva closures. För sortering efter efternamn räcker det att ändra KeyPath: \Person.lastName. Om du behöver sortera efter en anpassad nyckel (t.ex. namnens längd) används map inuti — utvecklaren kan fortfarande skriva sin egen closure för icke-standardiserade fall.
AnyKeyPath — är basetypen i KeyPath-hierarkin som raderar information om de specifika typerna av roten och värdet. AnyKeyPath vet inte på vilken objekttyp det anropas och vilken värdetyp det returnerar. Detta är användbart för lagring av heterogena KeyPaths i samlingar och för reflektion.
AnyKeyPath används när du behöver lagra en lista över sökvägar till olika egenskaper av olika typer. Till exempel i en UI-konfigurator, där varje sökväg konfigurerar en specifik egenskap och egenskapstyperna kan skilja sig åt. AnyKeyPath möjliggör enhetligt arbete med alla sökvägar.
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 { }
// Möjliggör konfigurering av egenskaper via AnyKeyPath
AnyKeyPath kräver typkonvertering vid hämtning av värdet, eftersom den specifika typen har raderats. För fullständig typsäkerhet är typad KeyPath
KeyPath är betydligt mer prestandaeffektivt än KVC:s (value(forKey:)) textnycklar. Textnycklar beräknas via Objective-C:s runtime-mekanism, inklusive tolkning av text, sökning i runtime och typkonvertering. KeyPath är en statisk mekanism baserad på inbyggda Swift-strukturer.
Enligt tester är KeyPath i genomsnitt 10-20 gånger snabbare än value(forKey:) för åtkomst till egenskaper. Skillnaden beror på avsaknaden av runtime-sökning och dynamisk dispatch. KeyPath får direkt åtkomst till egenskapens offset i minnet med tillräcklig optimering av kompilatorn.
func readWithKeyPath(person: Person) -> String {
person[keyPath: \Person.name]
}
func readWithKVC(person: Person) -> String {
person.value(forKey: "name") as! String
}
KVC är inte bara långsammare, utan också osäkert när det gäller typer: texten “name” kanske inte finns, och konverteringen as! String kan krascha. KeyPath garanterar egenskapens existens och typens korrekthet under kompileringen. För prestandakritiska delar (animationer, listor med tusentals element) är KeyPath det enda rätta valet.
Vanliga frågor
KeyPath — är en pekare till en egenskap hos ett objekt som kan skickas som ett vanligt värde. Istället för att skriva person.name skapar du sökvägen \Person.name och använder den för att läsa eller skriva egenskapen i valfri Person-instans.
KeyPath — endast läsning. WritableKeyPath — för läsning och skrivning av värdetyper (struct) via inout-kontext. ReferenceWritableKeyPath — för läsning och skrivning av referenstyper (class) utan ytterligare villkor.
KeyPath används i stor utsträckning i Combine (assign(to:on:)), SwiftUI (Binding, FocusState), sortering av samlingar (sorted(by:)), Core Data (NSSortDescriptor) och för säker åtkomst till nästlade egenskaper.
Ja, KeyPath är helt typsäkert. Ett fel i sökvägen (stavfel, felaktig typ) upptäcks under kompileringen, inte i runtime. value(forKey:) använder text och kan krascha med NSUndefinedKeyException om nyckeln inte finns.
Direkt — nej, Swift stöder inte dynamisk skapande av KeyPath från text. Du kan dock använda AnyKeyPath för lagring och överföring av statiskt erhållna KeyPaths. För dynamisk åtkomst till egenskaper via text, använd value(forKey:) med Objective-C runtime.
Sammanfattning
Vi utvecklar en mobil applikation nyckelfärdigt
IT Sectr skapar iOS- och Android-applikationer för startups och företag sedan 2017. Vi ger dig råd och föreslår den bästa lösningen.
Läs också