🔰Key Path

Keypaths allow you to refer to properties without invoking them – you hold a reference to the property itself, rather than reading its value.

Key paths 主要三種變體(還有其他)

  • KeyPath: read-only access to a property.

  • WritableKeyPath: readwrite access to a mutable property with value semantics (so the instance in question also needs to be mutable for writes to be allowed).

  • ReferenceWritableKeyPath: can only be used with reference types, and provides readwrite access to any mutable property.

Keypaths in Swift have a few more types, which mostly revolve around type-erasure, like with Any. When you combine or allow multiple keypaths, for example in an array, you can use the PartialKeyPath and AnyKeyPath to construct types that fit multiple keypaths.

Examples

👉 replit

// ⭐ 下面兩種型別雖然都有「可以當作 ID」的屬性,但屬性名稱不一樣。

struct Person {
    // social security number
    var ssn: String         // ⭐ id key for `Person`
    var name: String
}

struct Book {
    var isbn: String        // ⭐ id key for `Book`
    var title: String
}

/* -------- HasID protocol -------- */

// ⭐ 用 `HasID` 協定來統一規範這種有「ID 屬性」的類別。
// ⭐ 這種做法的好處是:「ID 屬性」不需要真的叫 `id`,叫其他名稱也可以。
protocol HasID {
    // ⭐ 2. 用 `IDType` 來稱呼此「ID 屬性」的類別
    associatedtype IDType
    // ⭐ 1. 用 keypath 來指定哪個屬性是「ID 屬性」
    static var idKey: WritableKeyPath<Self, IDType> { get }
}

/* -------- protocol extension (default behaviors) -------- */

extension HasID {
    func printID(){
        print(self[keyPath: Self.idKey])
    }
}

/* -------- protocol conformances -------- */

extension Person: HasID {
    static let idKey = \Person.ssn  // ⭐ WritableKeyPath<Person, String>
}

extension Book: HasID {
    static let idKey = \Book.isbn   // ⭐ WritableKeyPath<Book, String>
}

/* -------- main -------- */

let taylor = Person(ssn: "555-55-5555", name: "Taylor Swift")
let book = Book(isbn: "1234-5678", title: "Snow White")

taylor.printID()    // 555-55-5555
book.printID()      // 1234-5678

print(type(of: Person.idKey))       // WritableKeyPath<Person, String>
print(type(of: Book.idKey))         // WritableKeyPath<Book, String>

Last updated

Was this helpful?