users.map(\.name). Es una línea habitual en el código Swift moderno. Si preguntas qué es \.name, que empieza con una barra inversa, a menudo recibes la respuesta: «¿No es una sintaxis abreviada de map?». Solo es correcto a medias. Es un valor del tipo independiente KeyPath. Esta sintaxis es posible no porque map sea especial, sino porque KeyPath puede ocupar el lugar de una función.
Este es el octavo y último artículo de la serie intermedia. Veremos qué significa que KeyPath «convierta el acceso a propiedades en un valor», las diferencias entre los tres tipos de KeyPath y dónde demuestran su valor en la práctica.
Qué es KeyPath — Convertir en valor el camino hacia una propiedad
En el artículo sobre cierres vimos el poder de tratar las funciones como valores. KeyPath aplica esa idea al acceso a propiedades. A partir de la operación de acceso user.name, separa user y conserva solo «el camino hasta .name», convertido en el valor \User.name.
let path = \User.name // KeyPath<User, String>
let user = User(name: "Kim", age: 30)
let name = user[keyPath: path] // "Kim"
La firma de tipo muestra la esencia. KeyPath<User, String> es «una ruta que parte de User y llega a String». Como todavía no está ligada a ninguna instancia, puedes guardarla en una variable, pasarla a una función o reunirla en un array. La diferencia decisiva frente a los lenguajes dinámicos que acceden a propiedades mediante claves de texto, como «name», es la seguridad de tipos. Un error tipográfico como \User.nmae produce un error de compilación, y los tipos de origen y destino de la ruta se validan en tiempo de compilación. Es llevar al sistema de tipos el trabajo que hacían las claves de texto de KVC (Key-Value Coding) de Objective-C; aquí también se repite la filosofía de priorizar la seguridad del primer artículo.
Las rutas se pueden encadenar. Puedes atravesar propiedades anidadas con \User.address.city y continuar con propiedades de la biblioteca estándar mediante \User.name.count. También puedes unir dos rutas en tiempo de ejecución con appending(path:).
KeyPath en el lugar de una función — Cómo funciona map(.name)
users.map(\.name) se compila gracias a la propuesta SE-0249 de Swift Evolution. Si pasas KeyPath<Root, Value> donde se espera una función (Root) -> Value, el compilador la convierte automáticamente en el cierre { $0[keyPath: path] }. Por eso las dos líneas siguientes son equivalentes.
let names = users.map { $0.name }
let names = users.map(\.name)
¿Cuál conviene más? Para una extracción de propiedades sencilla, la opinión general favorece KeyPath. { $0.name } exige tres pasos: «leer el cierre → entender qué significa $0 → darse cuenta de que extrae una propiedad». En cambio, con \.name, «extraer name» es la propia sintaxis. Es una forma aún más condensada de declarar la intención, como vimos en el artículo sobre funciones de orden superior. Pero si aparece aunque sea un poco de lógica de transformación ($0.name.uppercased() + "님"), conviene usar un cierre. KeyPath sirve para extraer, no para transformar.
Esta conversión automática funciona no solo en map, sino en cualquier lugar que reciba una función: filter(\.isActive), compactMap(\.thumbnail), KeyPathComparator usado con sorted(by:) e incluso contains(where:). Las recetas de funciones de orden superior se vuelven aún más breves al combinarse con KeyPath.
Tres tipos de KeyPath — ¿solo lectura o también escritura?
KeyPath tiene una jerarquía. El compilador crea tipos distintos según el acceso que permita la ruta.
KeyPath<Root, Value> — solo lectura. Es una ruta hacia una propiedad let o una propiedad calculada de solo lectura.
WritableKeyPath<Root, Value> — lectura y escritura. Es una ruta hacia una propiedad almacenada var (o una propiedad calculada con setter) y permite modificar propiedades de tipos por valor mediante la ruta.
ReferenceWritableKeyPath<Root, Value> — escritura sobre un objetivo por referencia. Es una ruta hacia una propiedad var de una instancia de clase. La semántica de valor y referencia—el contenido puede cambiar aunque la referencia esté declarada con let—también se refleja en los tipos KeyPath.
Esta distinción importa en la práctica cuando escribes código que cambia un valor a través de una ruta.
func update<T, V>(_ items: inout [T], path: WritableKeyPath<T, V>, to value: V) {
for i in items.indices {
items[i][keyPath: path] = value
}
}
update(&cells, path: \.isSelected, to: false) // Anular toda la selección⟧
Pasar un KeyPath de solo lectura produce un error de compilación. El contrato «esta función modifica esa propiedad» queda incorporado en la firma, igual que throws registra la posibilidad de fallo en una firma de función.
El lugar real de KeyPath — Separar la configuración de la lógica
La abreviatura de map es solo la introducción a KeyPath. Su verdadero valor aparece al convertir en datos el diseño de «qué propiedad se debe manejar».
Externalizar los criterios de ordenación. Al crear una UI de ordenación de tablas, declara los criterios como un array de KeyPathComparator en lugar de escribir una función de ordenación para cada columna. Por ejemplo, [KeyPathComparator(\.name), KeyPathComparator(\.date, order: .reverse)]. Cambia solo los comparators según la columna elegida por el usuario y pásalos a items.sorted(using:); la lógica de ordenación queda fija en una línea y solo los criterios se mueven como datos.
Tablas de enlace y validación de formularios. Si declaras con KeyPath que «este campo corresponde a esta propiedad de User», recorrer, validar y guardar campos se convierte en código table-driven. La lógica no crece aunque aumenten los campos.
La base de SwiftUI y Observation. El parámetro id de List(users, id: \.id) es un KeyPath, y el framework Observation también usa KeyPath para rastrear «qué propiedades se han leído». En cualquier lugar donde un framework necesite saber «qué propiedad de tu tipo», KeyPath funciona como moneda común estándar.
El patrón común es claro: mantén una sola lógica e inyecta como valor la propiedad a la que se aplica. Igual que los genéricos convierten el tipo en un parámetro, KeyPath convierte la selección de propiedades en un parámetro. Puedes verlo como una versión ultraligera del patrón Strategy.
Hay que tener cuidado con el abuso. Las API genéricas que mezclan \.self con rutas de varios niveles se vuelven difíciles de entender rápidamente. Según el criterio de Progressive Disclosure, cuando la complejidad empieza a filtrarse hacia los lugares de uso, conviene volver a un cierre normal o a una función explícita.
Resumen
- KeyPath es un tipo que convierte la ruta de acceso a una propiedad en un valor.
\User.nameesKeyPath<User, String>, y los errores tipográficos y las incompatibilidades de tipos se detectan en tiempo de compilación. map(\.name)es la conversión automática de SE-0249. Usa KeyPath para extracciones sencillas y un cierre cuando haya transformación.- Hay tres capas: KeyPath de solo lectura, WritableKeyPath para escribir en tipos por valor y ReferenceWritableKeyPath para escribir en objetivos por referencia. Una función que modifica datos exige Writable y deja el contrato en su firma.
- Su verdadero valor está en parametrizar la selección de propiedades. Es la herramienta estándar para criterios de ordenación, enlace de formularios, selección de id y cualquier lugar que requiera «una sola lógica y una propiedad objetivo intercambiable».
Con esto termina la serie intermedia de ocho artículos. A continuación comienza la serie avanzada sobre Swift Concurrency. El primer artículo explicará qué problemas de los callbacks resolvió async/await y cómo, además del significado preciso del concepto de suspensión.

![Imagen de portada de [Swift intermedio #8] KeyPath de Swift: cómo funciona map(\.name)](/assets/images/posts/ed14abe4-909b-4e97-9896-8e26955ce9e6/swift-keypath-1.jpg)