본문 바로가기
iOS/Swift

[Swift] KeyPath 이해하기

by 6cess 2024. 7. 11.

객체 속성에 접근할 때 타입 안전하게 접근하는 방법 : KeyPath

  • Swift4에서 도입
  • 객체의 특정 속성에 대한 경로를 표현하는데 사용됨
  • KeyPath를 통해 객체의 속성에 접근하면 컴파일 타임에 타입 체크가 이루어지기 때문에 안전한 코드 작성 가능
  • KVO(Key-Value Observing)과 함께 사용되는 케이스가 있음
struct Person {
    var name: String
    var age: Int
}

let nameKeyPath = \Person.name
let ageKeyPath = \Person.age

let person = Person(name: "John", age: 30)
print(person[keyPath: nameKeyPath]) // "John"
print(person[keyPath: ageKeyPath])  // 30

// Root 타입은 생략이 가능
print(person[keyPath: \.age])  // 30

용례

  1. 함수형 프로그래밍 스타일
    : 직접 프로퍼티를 호출하지 않고 KeyPath를 통해 비교 기준 지정 가능
let people = [
    Person(name: "Alice", age: 24),
    Person(name: "Bob", age: 27),
    Person(name: "Charlie", age: 22)
]

let sortedByName = people.sorted(by: \.name)
let sortedByAge = people.sorted(by: \.age)
  1. KVO
class ObservablePerson: NSObject {
    @objc dynamic var name: String
    @objc dynamic var age: Int

    init(name: String, age: Int) {
        self.name = name
        self.age = age
    }
}

let observablePerson = ObservablePerson(name: "Eve", age: 28)
let observation = observablePerson.observe(\.name, options: [.old, .new]) { person, change in
    print("Name changed from \(change.oldValue!) to \(change.newValue!)")
}

observablePerson.name = "Eva"
  1. KeyPath를 직접 정의하여 사용
struct Person {
    var name: String
    var age: Int
}

struct Class {
  var yuk: Person
  
  // KeyPath로 접근하지 않는 경우
  func getYuk() -> Person {
    return self.yuk
  }
}

let yuk = PersonInfo(name: "yuk", age: 31)
let class = Class(yuk: yuk)

// Class의 메서드를 통해 갖고 오고 싶은 경우 getYuk를 구현 후 호출
print(class.getYuk())

// KeyPath를 통해 갖고 오고 싶은 경우 extension 구현
extension Class {
  func getClass(keyPath: KeyPath<Self, Person>) -> Person {
    self[keyPath: keyPath]
  }
}

print(class.getClass(keyPath: \.yuk))