KeyPath: conceitos-chave e acesso tipado

Autor: IT Sectr Publicado: 2026-06-18 Tempo de leitura: 10 min

KeyPath é um caminho tipado para uma propriedade de um objeto em Swift, representado como um valor de primeira classe. Ao contrário das chaves de string em KVC (Key-Value Coding), KeyPath é verificado pelo compilador em tempo de compilação: o compilador conhece o tipo do objeto raiz, o tipo da propriedade e pode garantir que o caminho existe. KeyPath é usado no Combine para ligação reativa, no SwiftUI para ligação bidirecional e na biblioteca padrão para ordenar e filtrar coleções. De acordo com Apple Developer, 2025, KeyPath é um bloco fundamental para programação funcional em Swift.

Principais pontos

  • KeyPath — um caminho tipado para uma propriedade, verificado pelo compilador
  • WritableKeyPath — um subtipo de KeyPath para propriedades com acesso de escrita
  • ReferenceWritableKeyPath — para propriedades mutáveis de tipos de referência
  • Combine usa KeyPath para ligar publishers a propriedades de objetos
  • AnyKeyPath — uma variante type-erased para armazenar KeyPath em coleções

O que é KeyPath em Swift?

KeyPath é um tipo da biblioteca padrão do Swift que representa um caminho para uma propriedade de um objeto. Ao contrário do acesso manual a uma propriedade com notação de ponto (object.property) ou uma chave de string (value(forKey:)), KeyPath é um objeto de primeira classe: pode ser passado como argumento, armazenado em uma variável e composto.

Sintaxe KeyPath

Um KeyPath é escrito com uma barra invertida seguida pelo caminho para a propriedade: \Person.name. O caminho pode ser aninhado: \Person.address.city. O compilador verifica que Person tem uma propriedade name e que name é de fato um String. Se a propriedade não existir ou o tipo estiver errado, o código não compila.

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

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

O acesso a uma propriedade via KeyPath é feito através do subscrito object[keyPath: path]. Para propriedades somente leitura, KeyPath é usado. Se a propriedade for mutável (var), WritableKeyPath está disponível. Para tipos de referência, ReferenceWritableKeyPath permite modificar a propriedade diretamente através do KeyPath.

Hierarquia KeyPath: WritableKeyPath e ReferenceWritableKeyPath

KeyPath tem uma hierarquia de subtipos refletindo o nível de acesso à propriedade. A hierarquia base é: AnyKeyPath → PartialKeyPath → KeyPath → WritableKeyPath → ReferenceWritableKeyPath. Cada subtipo adiciona capacidades.

Níveis de acesso do KeyPath

KeyPath — somente leitura. WritableKeyPath — leitura e escrita para tipos de valor (struct). ReferenceWritableKeyPath — leitura e escrita para tipos de referência (class). Swift seleciona automaticamente o subtipo correto com base no contexto.

TipoAcessoTipo de objetoMutação
KeyPathSomente leituraQualquerNão
WritableKeyPathLeitura e escritaTipo de valor (inout)Através de contexto mutating
ReferenceWritableKeyPathLeitura e escritaTipo de referênciaAtribuição direta
swift
// WritableKeyPath para tipo de valor
var mutablePerson = Person(name: "Bob", age: 25, isActive: false)
let writablePath: WritableKeyPath<Person, Bool> = \Person.isActive
mutablePerson[keyPath: writablePath] = true

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

Uma diferença importante: para tipos de valor, a atribuição via KeyPath requer var (contexto inout), enquanto para tipos de referência apenas uma propriedade mutável é necessária. Isso está alinhado com a semântica geral do Swift: tipos de valor são passados por valor, então a mutação requer mutabilidade do contêiner.

KeyPath no Combine e SwiftUI

KeyPath desempenha um papel central no Combine e SwiftUI. No Combine, o operador assign usa KeyPath para ligar o valor de um publisher à propriedade de um objeto. No SwiftUI, Binding usa KeyPath para a ligação bidirecional entre modelo e visão.

Assign no Combine

O operador assign(to:on:) aceita um ReferenceWritableKeyPath e um objeto. Quando o publisher emite um valor, ele é automaticamente escrito na propriedade especificada. Esta é uma forma declarativa de gerenciar estado sem atribuições manuais.

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

No SwiftUI, @Binding usa KeyPath para conectar uma visão pai e filha. O pai passa um Binding, criado via Binding(get:set:) ou a projeção ($). KeyPath permite que SwiftUI rastreie automaticamente mudanças e atualize a interface sem esforço extra.

Usando KeyPath para ordenar e filtrar

KeyPath permite escrever funções de ordenação e filtragem flexíveis e type-safe. Em vez de passar um closure cada vez, você pode passar um KeyPath para a propriedade pela qual deseja ordenar a coleção. Isso torna o código mais limpo e reduz a duplicação.

Função genérica de ordenação por KeyPath

A função sorted aceita KeyPath onde Value: Comparable. Isso garante que a propriedade de ordenação suporta comparação. O compilador verifica isso em tempo de compilação.

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 implementação ordena por qualquer propriedade Comparable sem escrever closures. Para ordenar por sobrenome, basta mudar o KeyPath: \Person.lastName. Se for necessário ordenar por uma chave personalizada (por exemplo, comprimento do nome), um map é usado internamente — o desenvolvedor pode escrever seu próprio closure para casos não padronizados.

AnyKeyPath type-erased

AnyKeyPath é o tipo base na hierarquia KeyPath que apaga informações sobre os tipos específicos de raiz e valor. AnyKeyPath não sabe em qual tipo de objeto é chamado nem qual tipo de valor retorna. Isso é útil para armazenar KeyPaths heterogêneos em coleções e para reflexão.

Usando AnyKeyPath

AnyKeyPath é usado quando você precisa armazenar uma lista de caminhos para diferentes propriedades de diferentes tipos. Por exemplo, em um configurador de UI, onde cada caminho configura uma propriedade específica, e os tipos de propriedade podem diferir. AnyKeyPath permite trabalhar uniformemente com todos os caminhos.

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 { }
// Permite configurar propriedades via AnyKeyPath

AnyKeyPath requer conversão de tipos ao recuperar o valor, já que o tipo específico foi apagado. Para total segurança de tipos, um KeyPath tipado é sempre preferível. AnyKeyPath é uma ferramenta para casos onde os tipos são realmente desconhecidos em tempo de compilação, como serialização ou reflexão.

Desempenho do KeyPath vs chaves de string

KeyPath é significativamente mais eficiente que as chaves de string do KVC (value(forKey:)). As chaves de string são resolvidas através do mecanismo de runtime do Objective-C, incluindo análise de string, busca em runtime e conversão de tipos. KeyPath é um mecanismo estático baseado em estruturas nativas do Swift.

Comparação de desempenho

De acordo com benchmarks, KeyPath é em média 10-20 vezes mais rápido que value(forKey:) para acesso a propriedades. A diferença se deve à ausência de busca em runtime e despacho dinâmico. KeyPath acessa diretamente o offset da propriedade na memória com otimização suficiente do compilador.

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

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

KVC não só é mais lento, mas também não é seguro em termos de tipos: a string “name” pode não existir e a conversão as! String pode falhar. KeyPath garante a existência da propriedade e a correção do tipo em tempo de compilação. Para seções críticas de desempenho (animações, listas com milhares de itens), KeyPath é a única escolha correta.

Perguntas frequentes

O que é KeyPath em Swift em palavras simples?

KeyPath é um ponteiro para uma propriedade de um objeto que pode ser passado como um valor normal. Em vez de escrever person.name, você cria um caminho \Person.name e o usa para ler ou escrever a propriedade em qualquer instância de Person.

Qual é a diferença entre KeyPath, WritableKeyPath e ReferenceWritableKeyPath?

KeyPath — somente leitura. WritableKeyPath — para leitura e escrita de tipos de valor (struct) através de contexto inout. ReferenceWritableKeyPath — para leitura e escrita de tipos de referência (class) sem condições adicionais.

Onde KeyPath é usado em Swift?

KeyPath é amplamente usado em Combine (assign(to:on:)), SwiftUI (Binding, FocusState), ordenação de coleções (sorted(by:)), Core Data (NSSortDescriptor) e para acesso seguro a propriedades aninhadas.

KeyPath é mais seguro que value(forKey:)?

Sim, KeyPath é completamente type-safe. Um erro no caminho (erro de digitação, tipo errado) é detectado em tempo de compilação, não em tempo de execução. value(forKey:) usa strings e pode falhar com NSUndefinedKeyException se a chave não existir.

Pode-se criar um KeyPath dinamicamente?

Não diretamente — Swift não suporta criação dinâmica de KeyPath a partir de uma string. No entanto, você pode usar AnyKeyPath para armazenar e passar KeyPaths obtidos estaticamente. Para acesso dinâmico a propriedades por string, use value(forKey:) com o runtime do Objective-C.

Resumo

  • KeyPath — um caminho tipado para uma propriedade, representado como um tipo de valor de primeira classe
  • Hierarquia KeyPath inclui WritableKeyPath e ReferenceWritableKeyPath para diferentes níveis de acesso
  • Combine e SwiftUI usam ativamente KeyPath para ligações reativas
  • Ordenação e filtragem via KeyPath melhora a legibilidade e segurança de tipos do código
  • AnyKeyPath — uma variante type-erased para armazenar KeyPaths heterogêneos em coleções
  • Desempenho do KeyPath é 10-20 vezes superior ao value(forKey:)

Vamos desenvolver um aplicativo móvel chave na mão

A IT Sectr cria aplicativos para iOS e Android para startups e empresas desde 2017. Nós vamos aconselhá-lo e propor a melhor solução.

Discutir o projeto

Leia também