Notice
Recent Posts
Recent Comments
Link
일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | 2 | 3 | 4 | 5 | 6 | 7 |
8 | 9 | 10 | 11 | 12 | 13 | 14 |
15 | 16 | 17 | 18 | 19 | 20 | 21 |
22 | 23 | 24 | 25 | 26 | 27 | 28 |
29 | 30 | 31 |
Tags
- 리팩토링
- HIG
- Refactoring
- rxswift
- UICollectionView
- Xcode
- Protocol
- swiftUI
- Clean Code
- uiscrollview
- SWIFT
- map
- UITextView
- ios
- ribs
- 리펙토링
- 애니메이션
- tableView
- 리펙터링
- collectionview
- 클린 코드
- swift documentation
- Human interface guide
- clean architecture
- RxCocoa
- MVVM
- combine
- Observable
- uitableview
- 스위프트
Archives
- Today
- Total
김종권의 iOS 앱 개발 알아가기
[iOS - swift] KeyPath, WritableKeyPath, ReferenceWritableKeyPath, DynamicMemberLookup 개념 본문
iOS 기본 (swift)
[iOS - swift] KeyPath, WritableKeyPath, ReferenceWritableKeyPath, DynamicMemberLookup 개념
jake-kim 2022. 1. 9. 21:44KeyPath
- 특정 속성에 대한 path정보를 가지고 있는 key값 (KeyPath 인스턴스를 통해 해당 값에 접근이 가능)
-
// KeyPath 문법: `\`키워드 + 유형 + 프로퍼티 이름 let nameKeyPath: KeyPath<Person, String> = \Person.name let person = Person(age: 12, name: "jake") print(person[keyPath: nameKeyPath]) // jake
- KeyPath 활용 - RxSwift의 Observable 구독 할때 특정 property를 가져오기 위해 map에서 사용 (간결성 향상)
struct Person { var age: Int var name: String } var personObservable: Observable<Person> { Observable.create { observer in observer.onNext(.init(age: 12, name: "jake")) observer.onCompleted() return Disposables.create() } } ... override func viewDidLoad() { super.viewDidLoad() personObservable .map(\.age) // <- KeyPath 사용 .subscribe { print($0) } .disposed(by: self.disposeBag) }
writableKeyPath, ReferenceWritableKeyPath
- writableKeyPath는 struct와 같은 Value-Type 전용이고, ReferenceWritableKeyPath는 class와 같은 Referece-Type에서 사용
- (Struct와 같은 Value-Type에 적용) KeyPath는 읽기 전용이지만, writableKeyPath는 쓰기도 가능
- KeyPath를 선언할 때 타입을 입력하지 않으면 writableKeyPath으로 되는것을 주의
// KeyPath 문법: `\`키워드 + 유형 + 프로퍼티 이름
let nameWritableKeyPath = \Person.name // WritableKeyPath<Person, String>
var person = Person(age: 12, name: "jake")
person[keyPath: nameWritableKeyPath] = "abc"
print(person) // abc
dynamicMemberLookup
- 정의: 명백한 member (프로퍼티) 표현이 아닐 때, 컴파일러가 subscript(dynamicMember):)를 보고 해당 멤버임을 받아들이게 하는 방법
- 사용처
- dictionary 값에 접근 할 때 key값을 문자열로 접근하는게 아닌, 일반 프로퍼티처럼 접근 가능
- subscript에 KeyPath를 인수로 받으면 Wrapper로써 기능 사용 가능
(원래 A.B.property로 접근해야 하지만, A.property로 접근 가능)
- 사용 방법
- @dydnamicMemberLookup 키워드를 struct와 같은 유형 위에 선언
- subscript(dydnamicMember:) 메소드 구현
ex1) 문자열로 접근하는게 아닌 일반 프로퍼티처럼 접근 기능
- someDictionary.name으로 접근
- someDictionary.value.name도 아니라 곧바로 name으로 접근
- name은 문자열이지만 프로퍼티처럼 접근 가능
@dynamicMemberLookup
struct SomeDictionary {
let value = [
"name": "jake",
"age": "12"
]
subscript(dynamicMember member: String) -> String? {
return value[member]
}
}
let someDictionary = SomeDictionary()
print(someDictionary.name ?? "") // jake
print(someDictionary.abc ?? "") // ""
print(someDictionary.age ?? "") // 12
- KeyPath를 이용하면 Wrapper 타입처럼 사용 가능
ex2) subscript에 KeyPath를 인수로 받으면 Wrapper로써 기능 사용 가능
- ContainerStruct 생성자에 다른 Struct를 넣고, 안에 있는 struct를 마치 프로퍼티처럼 접근
- KeyPath와 같이 사용
@dynamicMemberLookup
struct MyContainer<Root> {
private let root: Root
init(_ root: Root) {
self.root = root
}
subscript<Value>(dynamicMember keyPath: WritableKeyPath<Root, Value>) -> Value {
return root[keyPath: keyPath]
}
}
struct MyStruct {
var name: String
var age: Int
}
let myStruct = MyStruct(name: "jake", age: 12)
let myContainer = MyContainer(myStruct)
print(myContainer.name) // <- 그냥 일반 프로퍼티처럼 접근
- Rx에서 map 키워드를 생략하여 프로퍼티 접근 가능하도록 설정
// Observable
// @dynamicMemberLookup 키워드는 extension에 선언하지 못하므로 따로 protocol 선언 -> Observable에서 해당 프로토콜 준수하여 구현
@dynamicMemberLookup
protocol DynamicMemberLookupObservableType {
associatedtype Root
/// Value 타입 - Observable<Value>
subscript<Value>(dynamicMember keyPath: KeyPath<Root, Value>) -> Observable<Value> { get }
}
extension Observable: DynamicMemberLookupObservableType {
subscript<Value>(dynamicMember keyPath: KeyPath<Element, Value>) -> Observable<Value> {
self.map { $0[keyPath: keyPath] }
}
}
struct Person3 {
var name: String
var gender: String
}
let person3 = self.exampleObservableDynamicMember()
let name = person3.name.asObservable().subscribe { print($0) } // next(jake)
// person3.map { $0.name } 처럼 접근 안해도 되는 간편성
* 참고
https://github.com/apple/swift-evolution/blob/master/proposals/0252-keypath-dynamic-member-lookup.md
'iOS 기본 (swift)' 카테고리의 다른 글
Comments