KeyPath: concetti chiave e accesso tipizzato

Autore: IT Sectr Pubblicato: 2026-06-18 Tempo di lettura: 10 min

KeyPath è un percorso tipizzato verso una proprietà di un oggetto in Swift, rappresentato come un valore di prima classe. A differenza delle chiavi stringa in KVC (Key-Value Coding), KeyPath viene verificato dal compilatore in fase di compilazione: il compilatore conosce il tipo dell’oggetto radice, il tipo della proprietà e può garantire che il percorso esista. KeyPath viene utilizzato in Combine per il binding reattivo, in SwiftUI per il binding bidirezionale e nella libreria standard per ordinare e filtrare le collezioni. Secondo Apple Developer, 2025, KeyPath è un elemento fondamentale per la programmazione funzionale in Swift.

Punti chiave

  • KeyPath — un percorso tipizzato verso una proprietà, verificato dal compilatore
  • WritableKeyPath — un sottotipo di KeyPath per proprietà con accesso in scrittura
  • ReferenceWritableKeyPath — per proprietà mutabili di tipi riferimento
  • Combine usa KeyPath per legare i publisher alle proprietà degli oggetti
  • AnyKeyPath — una variante type-erased per memorizzare KeyPath in collezioni

Cos’è KeyPath in Swift?

KeyPath è un tipo della libreria standard di Swift che rappresenta un percorso verso una proprietà di un oggetto. A differenza dell’accesso manuale a una proprietà con notazione punto (object.property) o una chiave stringa (value(forKey:)), KeyPath è un oggetto di prima classe: può essere passato come argomento, memorizzato in una variabile e composto.

Sintassi di KeyPath

Un KeyPath viene scritto con una barra rovesciata seguita dal percorso verso la proprietà: \Person.name. Il percorso può essere annidato: \Person.address.city. Il compilatore verifica che Person abbia una proprietà name e che name sia effettivamente un String. Se la proprietà non esiste o il tipo è sbagliato, il codice non compila.

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

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

L’accesso a una proprietà tramite KeyPath avviene attraverso il subscript object[keyPath: path]. Per le proprietà di sola lettura, viene usato KeyPath. Se la proprietà è mutabile (var), WritableKeyPath è disponibile. Per i tipi riferimento, ReferenceWritableKeyPath consente di modificare la proprietà direttamente tramite KeyPath.

Gerarchia KeyPath: WritableKeyPath e ReferenceWritableKeyPath

KeyPath ha una gerarchia di sottotipi che riflette il livello di accesso alla proprietà. La gerarchia di base è: AnyKeyPath → PartialKeyPath → KeyPath → WritableKeyPath → ReferenceWritableKeyPath. Ogni sottotipo aggiunge capacità.

Livelli di accesso KeyPath

KeyPath — sola lettura. WritableKeyPath — lettura e scrittura per tipi valore (struct). ReferenceWritableKeyPath — lettura e scrittura per tipi riferimento (class). Swift seleziona automaticamente il sottotipo corretto in base al contesto.

TipoAccessoTipo oggettoMutazione
KeyPathSola letturaQualsiasiNo
WritableKeyPathLettura e scritturaTipo valore (inout)Tramite contesto mutating
ReferenceWritableKeyPathLettura e scritturaTipo riferimentoAssegnazione diretta
swift
// WritableKeyPath per tipo valore
var mutablePerson = Person(name: "Bob", age: 25, isActive: false)
let writablePath: WritableKeyPath<Person, Bool> = \Person.isActive
mutablePerson[keyPath: writablePath] = true

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

Una differenza importante: per i tipi valore, l’impostazione tramite KeyPath richiede var (contesto inout), mentre per i tipi riferimento è sufficiente una proprietà mutabile. Ciò è in linea con la semantica generale di Swift: i tipi valore vengono passati per valore, quindi la mutazione richiede la mutabilità del contenitore.

KeyPath in Combine e SwiftUI

KeyPath gioca un ruolo centrale in Combine e SwiftUI. In Combine, l’operatore assign utilizza KeyPath per legare il valore di un publisher a una proprietà di un oggetto. In SwiftUI, Binding usa KeyPath per il collegamento bidirezionale tra modello e vista.

Assign in Combine

L’operatore assign(to:on:) accetta un ReferenceWritableKeyPath e un oggetto. Quando il publisher emette un valore, viene automaticamente scritto nella proprietà specificata. Questo è un modo dichiarativo per gestire lo stato senza assegnazioni manuali.

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 usa KeyPath per connettere una vista padre e figlia. Il padre passa un Binding, creato tramite Binding(get:set:) o la proiezione ($). KeyPath consente a SwiftUI di tracciare automaticamente i cambiamenti e aggiornare l’interfaccia senza sforzo aggiuntivo.

Usare KeyPath per ordinare e filtrare

KeyPath consente di scrivere funzioni di ordinamento e filtraggio flessibili e type-safe. Invece di passare una closure ogni volta, puoi passare un KeyPath alla proprietà in base a cui vuoi ordinare la collezione. Questo rende il codice più pulito e riduce la duplicazione.

Funzione generica di ordinamento per KeyPath

La funzione sorted accetta KeyPath dove Value: Comparable. Ciò garantisce che la proprietà di ordinamento supporti il confronto. Il compilatore lo verifica in fase di compilazione.

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)

Questa implementazione ordina per qualsiasi proprietà Comparable senza scrivere closure. Per ordinare per cognome, basta cambiare KeyPath: \Person.lastName. Se è necessario ordinare per una chiave personalizzata (ad esempio, lunghezza del nome), viene usato map internamente — lo sviluppatore può comunque scrivere la propria closure per casi non standard.

AnyKeyPath type-erased

AnyKeyPath è il tipo base nella gerarchia KeyPath che cancella le informazioni sui tipi specifici di radice e valore. AnyKeyPath non sa su quale tipo di oggetto viene chiamato né quale tipo di valore restituisce. Questo è utile per memorizzare KeyPaths eterogenei in collezioni e per la riflessione.

Utilizzo di AnyKeyPath

AnyKeyPath viene utilizzato quando è necessario memorizzare un elenco di percorsi verso diverse proprietà di diversi tipi. Ad esempio, in un configuratore di UI, dove ogni percorso configura una proprietà specifica e i tipi di proprietà possono differire. AnyKeyPath consente di lavorare uniformemente con tutti i percorsi.

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 { }
// Consente di configurare proprietà tramite AnyKeyPath

AnyKeyPath richiede cast di tipo durante il recupero del valore, poiché il tipo specifico è stato cancellato. Per la massima sicurezza dei tipi, un KeyPath tipizzato è sempre preferibile. AnyKeyPath è uno strumento per casi in cui i tipi sono realmente sconosciuti in fase di compilazione, come serializzazione o riflessione.

Prestazioni di KeyPath vs chiavi stringa

KeyPath è significativamente più efficiente delle chiavi stringa KVC (value(forKey:)). Le chiavi stringa vengono risolte attraverso il meccanismo runtime di Objective-C, inclusa l’analisi della stringa, la ricerca in runtime e il cast di tipo. KeyPath è un meccanismo statico basato su strutture native Swift.

Confronto delle prestazioni

Secondo i benchmark, KeyPath è in media 10-20 volte più veloce di value(forKey:) per l’accesso alle proprietà. La differenza è dovuta all’assenza di ricerca in runtime e dispatch dinamico. KeyPath accede direttamente all’offset della proprietà in memoria con una sufficiente ottimizzazione del compilatore.

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

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

KVC non è solo più lento, ma anche non type-safe: la stringa “name” potrebbe non esistere e il cast as! String potrebbe causare un crash. KeyPath garantisce l’esistenza della proprietà e la correttezza del tipo in fase di compilazione. Per le sezioni critiche per le prestazioni (animazioni, elenchi con migliaia di elementi), KeyPath è l’unica scelta corretta.

Domande frequenti

Cos’è KeyPath in Swift in parole semplici?

KeyPath è un puntatore a una proprietà di un oggetto che può essere passato come un valore normale. Invece di scrivere person.name, crei un percorso \Person.name e lo usi per leggere o scrivere la proprietà su qualsiasi istanza di Person.

Qual è la differenza tra KeyPath, WritableKeyPath e ReferenceWritableKeyPath?

KeyPath — sola lettura. WritableKeyPath — per leggere e scrivere tipi valore (struct) tramite contesto inout. ReferenceWritableKeyPath — per leggere e scrivere tipi riferimento (class) senza condizioni aggiuntive.

Dove viene usato KeyPath in Swift?

KeyPath è ampiamente utilizzato in Combine (assign(to:on:)), SwiftUI (Binding, FocusState), ordinamento di collezioni (sorted(by:)), Core Data (NSSortDescriptor) e per l’accesso sicuro a proprietà annidate.

KeyPath è più sicuro di value(forKey:)?

, KeyPath è completamente type-safe. Un errore nel percorso (refuso, tipo sbagliato) viene rilevato in fase di compilazione, non in fase di esecuzione. value(forKey:) usa stringhe e può crashare con NSUndefinedKeyException se la chiave non esiste.

Si può creare un KeyPath dinamicamente?

Non direttamente — Swift non supporta la creazione dinamica di KeyPath da una stringa. Tuttavia, puoi usare AnyKeyPath per memorizzare e passare KeyPath ottenuti staticamente. Per l’accesso dinamico alle proprietà tramite stringa, usa value(forKey:) con il runtime Objective-C.

Riepilogo

  • KeyPath — un percorso tipizzato verso una proprietà, rappresentato come tipo valore di prima classe
  • Gerarchia di KeyPath include WritableKeyPath e ReferenceWritableKeyPath per diversi livelli di accesso
  • Combine e SwiftUI utilizzano attivamente KeyPath per binding reattivi
  • Ordinamento e filtraggio tramite KeyPath migliora la leggibilità e la sicurezza dei tipi del codice
  • AnyKeyPath — una variante type-erased per memorizzare KeyPaths eterogenei in collezioni
  • Prestazioni di KeyPath sono 10-20 volte superiori a value(forKey:)

Svilupperemo un'applicazione mobile chiavi in mano

IT Sectr crea applicazioni iOS e Android per startup e aziende dal 2017. Ti consulteremo e ti proporremo la soluzione migliore.

Discuti il progetto

Leggi anche