KeyPath: Key Concepts and Typed Access

Author: IT Sectr Published: 2026-06-18 Reading time: 10 min

KeyPath is a typed path to an object’s property in Swift, represented as a first-class value. Unlike string keys in KVC (Key-Value Coding), KeyPath is checked by the compiler at build time: the compiler knows the root object type, the property type, and can guarantee that the path exists. KeyPath is used in Combine for reactive binding, in SwiftUI for two-way binding, and in the standard library for sorting and filtering collections. According to Apple Developer, 2025, KeyPath is a fundamental building block for functional programming in Swift.

Key Takeaways

  • KeyPath — a typed path to a property, verified by the compiler
  • WritableKeyPath — a subtype of KeyPath for writable properties
  • ReferenceWritableKeyPath — for mutable properties of reference types
  • Combine uses KeyPath to bind publishers to object properties
  • AnyKeyPath — a type-erased variant for storing KeyPath in collections

What is KeyPath in Swift?

KeyPath is a type from the Swift standard library that represents a path to an object’s property. Unlike manually accessing a property with dot notation (object.property) or a string key (value(forKey:)), KeyPath is a first-class object: it can be passed as an argument, stored in a variable, and composed.

KeyPath Syntax

A KeyPath is written with a backslash followed by the path to the property: \Person.name. The path can be nested: \Person.address.city. The compiler verifies that Person has a name property and that name is indeed a String. If the property does not exist or the type is wrong, the code does not compile.

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

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

Accessing a property via KeyPath is done through the subscript object[keyPath: path]. For read-only properties, KeyPath is used. If the property is mutable (var), WritableKeyPath is available. For reference types, ReferenceWritableKeyPath allows modifying the property directly through the KeyPath.

KeyPath Hierarchy: WritableKeyPath and ReferenceWritableKeyPath

KeyPath has a subtype hierarchy reflecting the access level to the property. The base hierarchy is: AnyKeyPath → PartialKeyPath → KeyPath → WritableKeyPath → ReferenceWritableKeyPath. Each subtype adds capabilities.

KeyPath Access Levels

KeyPath — read-only. WritableKeyPath — read and write for value types (struct). ReferenceWritableKeyPath — read and write for reference types (class). Swift automatically selects the correct subtype based on context.

TypeAccessObject TypeMutation
KeyPathReadOnlyAnyNo
WritableKeyPathReadWriteValue type (inout)Through mutating context
ReferenceWritableKeyPathReadWriteReference typeDirect assignment
swift
// WritableKeyPath for value type
var mutablePerson = Person(name: "Bob", age: 25, isActive: false)
let writablePath: WritableKeyPath<Person, Bool> = \Person.isActive
mutablePerson[keyPath: writablePath] = true

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

An important distinction: for value types, setting via KeyPath requires var (inout context), while for reference types, only a mutable property is needed. This aligns with Swift’s general semantics: value types are passed by value, so mutation requires container mutability.

KeyPath in Combine and SwiftUI

KeyPath plays a central role in Combine and SwiftUI. In Combine, the assign operator uses KeyPath to bind a publisher’s value to an object’s property. In SwiftUI, Binding uses KeyPath for two-way linking between model and view.

Assign in Combine

The assign(to:on:) operator accepts a ReferenceWritableKeyPath and an object. When the publisher emits a value, it is automatically written to the specified property. This is a declarative way to manage state without manual assignments.

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

In SwiftUI, @Binding uses KeyPath to connect a parent and child View. The parent passes a Binding, created via Binding(get:set:) or the projection ($). KeyPath allows SwiftUI to automatically track changes and update the UI without extra effort.

Using KeyPath for Sorting and Filtering

KeyPath enables writing flexible and type-safe sorting and filtering functions. Instead of passing a closure each time, you can pass a KeyPath to the property by which you want to sort the collection. This makes the code cleaner and reduces duplication.

Generic Sort Function by KeyPath

The function sorted accepts KeyPath where Value: Comparable. This guarantees that the sorting property supports comparison. The compiler checks this at build time.

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)

This implementation sorts by any Comparable property without writing closures. To sort by last name, just change the KeyPath: \Person.lastName. If sorting by a custom key (e.g., name length), a map is used inside — the developer can still write their own closure for non-standard cases.

Type-erased AnyKeyPath

AnyKeyPath is the base type in the KeyPath hierarchy that erases information about the specific root and value types. AnyKeyPath does not know which object type it is called on or what value type it returns. This is useful for storing heterogeneous KeyPaths in collections and for reflection.

Using AnyKeyPath

AnyKeyPath is used when you need to store a list of paths to different properties of different types. For example, in a UI configurator, where each path configures a specific property, and the property types may differ. AnyKeyPath allows working uniformly with all paths.

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 { }
// Allows configuring properties via AnyKeyPath

AnyKeyPath requires type casting when retrieving the value, since the specific type is erased. For full type safety, a typed KeyPath is always preferred. AnyKeyPath is a tool for cases where types are truly unknown at compile time, such as serialization or reflection.

KeyPath Performance vs String Keys

KeyPath is significantly more performant than KVC string keys (value(forKey:)). String keys are resolved through the Objective-C runtime mechanism, including string parsing, runtime lookup, and type casting. KeyPath is a static mechanism based on native Swift structures.

Performance Comparison

According to benchmarks, KeyPath is on average 10-20 times faster than value(forKey:) for property access. The difference is due to the absence of runtime lookup and dynamic dispatch. KeyPath directly accesses the property offset in memory with sufficient compiler optimization.

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

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

KVC is not only slower but also type-unsafe: the string “name” may not exist, and the as! String cast may crash. KeyPath guarantees the existence of the property and type correctness at compile time. For performance-critical sections (animations, lists with thousands of items), KeyPath is the only correct choice.

Frequently Asked Questions

What is KeyPath in Swift in simple words?

KeyPath is a pointer to an object’s property that can be passed as a regular value. Instead of writing person.name, you create a path \Person.name and use it to read or write the property on any Person instance.

What is the difference between KeyPath, WritableKeyPath and ReferenceWritableKeyPath?

KeyPath is read-only. WritableKeyPath is for reading and writing value types (struct) through inout context. ReferenceWritableKeyPath is for reading and writing reference types (class) without additional conditions.

Where is KeyPath used in Swift?

KeyPath is widely used in Combine (assign(to:on:)), SwiftUI (Binding, FocusState), collection sorting (sorted(by:)), Core Data (NSSortDescriptor), and for safe access to nested properties.

Is KeyPath safer than value(forKey:)?

Yes, KeyPath is fully type-safe. An error in the path (typo, wrong type) is caught at compile time, not at runtime. value(forKey:) uses strings and can crash with NSUndefinedKeyException if the key does not exist.

Can a KeyPath be created dynamically?

Not directly — Swift does not support dynamic KeyPath creation from a string. However, you can use AnyKeyPath to store and pass statically obtained KeyPaths. For dynamic property access by string, use value(forKey:) with the Objective-C runtime.

Summary

  • KeyPath — a typed path to a property, represented as a first-class value type
  • Hierarchy of KeyPath includes WritableKeyPath and ReferenceWritableKeyPath for different access levels
  • Combine and SwiftUI actively use KeyPath for reactive bindings
  • Sorting and filtering via KeyPath improves code readability and type safety
  • AnyKeyPath — a type-erased variant for storing heterogeneous KeyPaths in collections
  • Performance of KeyPath is 10-20 times higher than value(forKey:)

We will develop a mobile application turnkey

IT Sectr creates iOS and Android applications for startups and businesses since 2017. We will advise you and propose the best solution.

Discuss the project

Read also