Search
Duplicate

(Swift) 이름은 모르지만 어쨌든 "\." 에 대한 포스팅 (feat. KeyPath)

간단소개
Swift의 keyPath 에 대해서 알아보자
팔만코딩경 컨트리뷰터
ContributorNotionAccount
주제 / 분류
Swift
Scrap
태그
9 more properties
SwiftUI 튜토리얼을 따라하다가 아래와 같은 코드를 보게되었다.
struct HikeGraph: View { var hike: Hike var path: KeyPath<Hike.Observation, Range<Double>> var color: Color { switch path { case \.elevation: return .gray case \.heartRate: return Color(hue: 0, saturation: 0.5, brightness: 0.7) case \.pace: return Color(hue: 0.7, saturation: 0.4, brightness: 0.7) default: return .black } } }
Swift
복사
...? 대체 \. 가 뭐지?
용어를 몰라서 어떻게 검색할지 고민하다가 처음 보는 키워드인 KeyPath에 대해 검색하게 되었다.
그런데!! \. 도 KeyPath와 연관이 있었다!! 그렇다면 KeyPath에 대해 공부해보자!

Key-Value Coding(KVC)란?

KeyPath를 공부하기 전에 우선 Key-Value Coding(KVC)에 대해 알아야 한다.
아직은 어떤식으로 데이터를 가져온다는지 이해가 잘 되지 않는다! swift에서 제공하는 KeyPath 클래스와 예시를 보면서 이해해보자.

KeyPath란?

KeyPath는 값에 대한 참조가 아닌 프로퍼티에 대한 참조이다. 즉, 객체 내부의 값에 직접적으로 참조하는 것이 아니라 값의 이름(프로퍼티)를 참조하는 것이다.
간단하게 아래의 코드를 살펴보자.
struct Swift { var propertyOne: Int var propertyTwo: Int }
Swift
복사
이렇게 만든 Swift 객체를 참조할 때, 흔히 아래와 같이 사용한다.
let swift: Swift = Swift() Print(swift.propertyOne) // swift 인스턴스의 propertyOne 값에 접근
Swift
복사
이렇게 접근하는 것이 바로 값에 대한 직접 참조이다. 그렇다면 프로퍼티에 대한 참조는 어떻게 하는 것일까?? 아래 나오는 연습 예제를 보면서 이해해보자!

KeyPath의 종류

우선 Swift에서 제공하는 KeyPath에는 5가지 종류가 있다. 이런게 있구나 하고 넘어간 뒤, 나중에 보면 이해가 될 것이다!
AntyKeyPath : 타입이 지워진 KeyPath
PartialKeyPath : 부분적으로 타입이 지워진 KeyPath
KeyPath : 읽기 전용
WritableKeyPath : 읽기 및 쓰기 가능
ReferenceWritableKeyPath : 클래스의 인스턴스에 사용 가능. 변경 가능한 모든 프로퍼티에 대한 read & write access 제공.
종류가 많아서 어렵게 느껴지겠지만 기본적으로는 KeyPath를 사용하며, keypath를 받아올때의 타입에 따라서 자동으로 타입캐스팅(?)이 되는 것을 확인할 수 있다.
관련된 내용은 연습 코드 이후에 적혀있다. 먼저 확인하려면 여기를 누르자.

연습

KeyPath를 사용하는 함수, 연산 프로퍼티 만들어보기

import UIKit struct Cluster { var name: String } struct Cadet { let cluster1: Cluster let cluster2: Cluster }
Swift
복사
Cluster 구조체는 name 프로퍼티를 가지고있고,
Cadet 구조체는 cluster1 와 cluster2 프로퍼티를 가지고 있다.
let clusterG = Cluster(name: "개포") let clusterS = Cluster(name: "서초") let cadet = Cadet(cluster1: clusterG, cluster2: clusterS)
Swift
복사
cadet 이라는 변수로 개포클러스터와 서초 클러스터를 넣어주었다.
이제 cadet 변수의 cluster1 값과 cluster2값을 구하는 함수를 만들어보자. 일반적인 경우라면 아래와 같이 함수를 2개 만들어야할것이다.
// cadet 변수의 cluster1 를 반환하는 함수 func getCluster1(cadet: Cadet) -> Cluster { return cadet.cluster1 } // cadet 변수의 cluster2 를 반환하는 함수 func getCluster2(cadet: Cadet) -> Cluster { return cadet.cluster2 }
Swift
복사
그러나 keyPath 를 활용하여, 아래와같이 함수 1개로 구현가능하다.
// KeyPath를 활용해서 Address를 반환하는 함수 func getCluster(cadet: Cadet, keypath: KeyPath<Cadet, Cluster>) -> Cluster { return cadet[keyPath: keypath] }
Swift
복사
받은 keypath 값에 따라서 cadet 이라는 변수의 프로퍼티들에 자유롭게 접근 할 수 있는 것이다!
이때 keyPath의 타입을 보면, KeyPath<Cadet, Cluster> 로 되어있다.
이것은 Cadet 구조체의 Cluster 타입인 속성만 받겠다는것이다.
print(getCluster(cadet: cadet, keypath: \.cluster1).name) print(getCluster(cadet: cadet, keypath: \.cluster2).name) // 출력결과 // 개포 // 서초
Swift
복사
\.cluster1 을 통해서 Cadet 구조체의 Cluster 타입인 cluster1 속성을 지정해주고 있다.
같은 cadet 이지만, \.cluster1 또는 \.cluster2 에 따라서 그 결과가 다른것을 확인 할 수 있다.
마찬가지로 KeyPath를 활용해서 연산 프로퍼티로 사용할 수 도 있다.
// KeyPath를 활용해서 Address를 가져오는 연산 프로퍼티 struct ClusterNick { var cadet: Cadet var path: KeyPath<Cadet, Cluster> var name: Address { switch path { case \.cluster1: return "개포퐁" case \.clutser2: return "서초롱" case } } }
Swift
복사
name 은 연산 프로퍼티로, path에 따라서 값이 달라지게 하였다.
path를 KeyPath 타입으로 두어, 프로퍼티 그 자체를 비교하여 결과값이 달라지게 한다.
cluster1Nick = ClusterNick(cadet: cadet, path: \.cluster1) cluster2Nick = ClusterNick(cadet: cadet, path: \.cluster2) print(cluster1Nick.name) print(cluster2Nick.name) // 출력결과 // 개포퐁 // 서초롱
Swift
복사
마찬가지로, 같은 cadet 이어도 path값에 따라서 결과가 다르게 출력된다.

KeyPath 와 WritableKeyPath

getCluster 함수에 아래 출력문을 추가해서 keypath의 타입을 출력해보자.
print(type(of: keypath))
Swift
복사
그러면 아래 그림처럼 WritableKeyPath<Person, Address> 타입으로 출력될 것이다. 분명 처음 선언할 때에는 KeyPath로 선었했는데, 접근하는 프로퍼티가 var로 선언되어 있기 때문에 자동으로 타입이 변경된 것이다.
KeyPath타입으로 바꾸려면 varlet으로 바꿔주기만 하면된다. KeyPath타입은 읽기 전용으로 수정이 불가능하다.

Reference