KeyPath: conceptos clave y acceso tipado

Autor: IT Sectr Publicado: 2026-06-18 Tiempo de lectura: 10 min

KeyPath es una ruta tipada a una propiedad de un objeto en Swift, representada como un valor de primera clase. A diferencia de las claves de cadena en KVC (Key-Value Coding), KeyPath es verificado por el compilador en tiempo de compilación: el compilador conoce el tipo del objeto raíz, el tipo de la propiedad y puede garantizar que la ruta existe. KeyPath se utiliza en Combine para enlaces reactivos, en SwiftUI para enlaces bidireccionales y en la biblioteca estándar para ordenar y filtrar colecciones. Según Apple Developer, 2025, KeyPath es un bloque fundamental para la programación funcional en Swift.

Puntos clave

  • KeyPath — una ruta tipada a una propiedad, verificada por el compilador
  • WritableKeyPath — un subtipo de KeyPath para propiedades con acceso de escritura
  • ReferenceWritableKeyPath — para propiedades mutables de tipos de referencia
  • Combine usa KeyPath para vincular editores a propiedades de objetos
  • AnyKeyPath — una variante type-erased para almacenar KeyPath en colecciones

¿Qué es KeyPath en Swift?

KeyPath es un tipo de la biblioteca estándar de Swift que representa una ruta a una propiedad de un objeto. A diferencia del acceso manual a una propiedad mediante notación de punto (object.property) o una clave de cadena (value(forKey:)), KeyPath es un objeto de primera clase: se puede pasar como argumento, almacenar en una variable y componer.

Sintaxis de KeyPath

Un KeyPath se escribe con una barra invertida seguida de la ruta a la propiedad: \Person.name. La ruta puede ser anidada: \Person.address.city. El compilador verifica que Person tiene una propiedad name y que name es realmente un String. Si la propiedad no existe o el tipo es incorrecto, el código no compila.

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]

El acceso a una propiedad a través de KeyPath se realiza mediante el subíndice object[keyPath: path]. Para propiedades de solo lectura, se usa KeyPath. Si la propiedad es mutable (var), está disponible WritableKeyPath. Para tipos de referencia, ReferenceWritableKeyPath permite modificar la propiedad directamente a través del KeyPath.

Jerarquía de KeyPath: WritableKeyPath y ReferenceWritableKeyPath

KeyPath tiene una jerarquía de subtipos que refleja el nivel de acceso a la propiedad. La jerarquía base es: AnyKeyPath → PartialKeyPath → KeyPath → WritableKeyPath → ReferenceWritableKeyPath. Cada subtipo añade capacidades.

Niveles de acceso de KeyPath

KeyPath — solo lectura. WritableKeyPath — lectura y escritura para tipos de valor (struct). ReferenceWritableKeyPath — lectura y escritura para tipos de referencia (class). Swift selecciona automáticamente el subtipo correcto según el contexto.

TipoAccesoTipo de objetoMutación
KeyPathReadOnlyCualquieraNo
WritableKeyPathReadWriteTipo de valor (inout)Mediante contexto mutating
ReferenceWritableKeyPathReadWriteTipo de referenciaAsignación directa
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"

Una diferencia importante: para tipos de valor, la asignación mediante KeyPath requiere var (contexto inout), mientras que para tipos de referencia solo se necesita una propiedad mutable. Esto se alinea con la semántica general de Swift: los tipos de valor se pasan por valor, por lo que la mutación requiere mutabilidad del contenedor.

KeyPath en Combine y SwiftUI

KeyPath desempeña un papel central en Combine y SwiftUI. En Combine, el operador assign utiliza KeyPath para vincular el valor de un editor a una propiedad de un objeto. En SwiftUI, Binding usa KeyPath para la vinculación bidireccional entre el modelo y la vista.

Assign en Combine

El operador assign(to:on:) acepta un ReferenceWritableKeyPath y un objeto. Cuando el editor emite un valor, se escribe automáticamente en la propiedad especificada. Esta es una forma declarativa de gestionar el estado sin asignaciones manuales.

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

En SwiftUI, @Binding usa KeyPath para conectar una vista padre y una vista hija. El padre pasa un Binding, creado mediante Binding(get:set:) o la proyección ($). KeyPath permite a SwiftUI rastrear cambios automáticamente y actualizar la interfaz sin esfuerzo adicional.

Uso de KeyPath para ordenar y filtrar

KeyPath permite escribir funciones de ordenación y filtrado flexibles y type-safe. En lugar de pasar un closure cada vez, puedes pasar un KeyPath a la propiedad por la que deseas ordenar la colección. Esto hace que el código sea más limpio y reduce la duplicación.

Función genérica de ordenación por KeyPath

La función sorted acepta KeyPath donde Value: Comparable. Esto garantiza que la propiedad de ordenación admite comparación. El compilador lo verifica en tiempo de compilación.

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)

Esta implementación ordena por cualquier propiedad Comparable sin escribir closures. Para ordenar por apellido, solo cambia el KeyPath: \Person.lastName. Si se necesita ordenar por una clave personalizada (por ejemplo, longitud del nombre), se usa map internamente — el desarrollador puede escribir su propio closure para casos no estándar.

AnyKeyPath type-erased

AnyKeyPath es el tipo base en la jerarquía KeyPath que borra la información sobre los tipos específicos de raíz y valor. AnyKeyPath no sabe en qué tipo de objeto se invoca ni qué tipo de valor devuelve. Esto es útil para almacenar KeyPaths heterogéneos en colecciones y para reflexión.

Uso de AnyKeyPath

AnyKeyPath se usa cuando necesitas almacenar una lista de rutas a diferentes propiedades de diferentes tipos. Por ejemplo, en un configurador de UI, donde cada ruta configura una propiedad específica y los tipos de propiedades pueden diferir. AnyKeyPath permite trabajar de manera uniforme con todas las rutas.

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 requiere conversión de tipos al recuperar el valor, ya que el tipo específico se ha borrado. Para seguridad total de tipos, siempre se prefiere un KeyPath tipado. AnyKeyPath es una herramienta para casos donde los tipos realmente se desconocen en tiempo de compilación, como serialización o reflexión.

Rendimiento de KeyPath vs claves de cadena

KeyPath es significativamente más eficiente que las claves de cadena de KVC (value(forKey:)). Las claves de cadena se resuelven mediante el mecanismo de runtime de Objective-C, que incluye análisis de cadenas, búsqueda en runtime y conversión de tipos. KeyPath es un mecanismo estático basado en estructuras nativas de Swift.

Comparación de rendimiento

Según pruebas comparativas, KeyPath es en promedio 10-20 veces más rápido que value(forKey:) para acceder a propiedades. La diferencia se debe a la ausencia de búsqueda en runtime y despacho dinámico. KeyPath accede directamente al offset de la propiedad en memoria con suficiente optimización del compilador.

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

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

KVC no solo es más lento, sino también no es seguro en cuanto a tipos: la cadena “name” puede no existir y la conversión as! String puede fallar. KeyPath garantiza la existencia de la propiedad y la corrección del tipo en tiempo de compilación. Para secciones críticas de rendimiento (animaciones, listas con miles de elementos), KeyPath es la única opción correcta.

Preguntas frecuentes

¿Qué es KeyPath en Swift en palabras simples?

KeyPath es un puntero a una propiedad de un objeto que se puede pasar como un valor normal. En lugar de escribir person.name, creas una ruta \Person.name y la usas para leer o escribir la propiedad en cualquier instancia de Person.

¿Cuál es la diferencia entre KeyPath, WritableKeyPath y ReferenceWritableKeyPath?

KeyPath es solo de lectura. WritableKeyPath es para lectura y escritura de tipos de valor (struct) mediante contexto inout. ReferenceWritableKeyPath es para lectura y escritura de tipos de referencia (class) sin condiciones adicionales.

¿Dónde se usa KeyPath en Swift?

KeyPath se usa ampliamente en Combine (assign(to:on:)), SwiftUI (Binding, FocusState), ordenación de colecciones (sorted(by:)), Core Data (NSSortDescriptor) y para acceso seguro a propiedades anidadas.

¿Es KeyPath más seguro que value(forKey:)?

, KeyPath es completamente type-safe. Un error en la ruta (error tipográfico, tipo incorrecto) se detecta en tiempo de compilación, no en tiempo de ejecución. value(forKey:) usa cadenas y puede fallar con NSUndefinedKeyException si la clave no existe.

¿Se puede crear un KeyPath dinámicamente?

No directamente — Swift no admite la creación dinámica de KeyPath a partir de una cadena. Sin embargo, puedes usar AnyKeyPath para almacenar y pasar KeyPaths obtenidos estáticamente. Para acceso dinámico a propiedades por cadena, usa value(forKey:) con el runtime de Objective-C.

Resumen

  • KeyPath — una ruta tipada a una propiedad, representada como un tipo de valor de primera clase
  • Jerarquía de KeyPath incluye WritableKeyPath y ReferenceWritableKeyPath para diferentes niveles de acceso
  • Combine y SwiftUI utilizan activamente KeyPath para enlaces reactivos
  • Ordenación y filtrado mediante KeyPath mejora la legibilidad y la seguridad de tipos del código
  • AnyKeyPath — una variante type-erased para almacenar KeyPaths heterogéneos en colecciones
  • Rendimiento de KeyPath es 10-20 veces superior a value(forKey:)

Desarrollaremos una aplicación móvil llave en mano

IT Sectr crea aplicaciones para iOS y Android para startups y empresas desde 2017. Le asesoraremos y le propondremos la mejor solución.

Discutir el proyecto

Lea también