[SwiftUI] $hoge.fuga는 $(hoge.fuga)가 아니다
$hoge.fuga
라고 하는 식은 $(hoge.fuga)
는 아니다, 라고 하는 이야기를 합니다.예
우선은 기술 예를 든다.
struct Hoge {
var fuga: String = ""
}
struct MyContentView: View {
@State private var hoge = Hoge()
var body: some View {
TextField("入力してね", text: $hoge.fuga)
.textFieldStyle(RoundedBorderTextFieldStyle())
}
}
매우 간단합니다.
그럼, 다음과 같이 써 봅시다. 오류가 발생합니다.
//Error: '$' is not an identifier; use backticks to escape it
TextField("入力してね", text: $(hoge.fuga))
그럼 이것도 안돼.
//Error: Referencing subscript 'subscript(dynamicMember:)' requires wrapper 'Binding<Hoge>'
//Error: Value of type 'Hoge' has no dynamic member '$fuga' using key path from root type 'Hoge'
TextField("入力してね", text: hoge.$fuga)
이것으로 일단 $hoge.fuga
가 $(hoge.fuga)
가 아닌 것은 알 수 있었던 것 같습니다. 그러나 그럼 $hoge.fuga
란 무엇입니까?
정체
원래 $
를 이용한 액세스는 propertyWrapper
의 기능에 의한 것입니다. State<T>
의 projectedValue
는 Binding<T>
입니다. 그러므로 $hoge
는 Binding<Hoge>
의 값입니다.
그러나 그렇게 하면 Binding<Hoge>
에 속성 fuga
가 존재하는 것이 이상하게 보일 것입니다. 이것을 실현하고 있는 것이 dynamicMemberLookup
입니다. Swift5.1 이후에 KeyPath
를 사용한 Lookup이 가능하게 되었기 때문에, 아마 Binding
가 다음과 같이 정의되고 있습니다.
@dynamicMemberLookup
struct Binding<T> {
subscript<U>(dynamicMember keyPath: WritableKeyPath<T, U>) -> Binding<U> {
//処理
}
}
이것만으로는 조금 이해하기 어렵기 때문에, 더 간단한 샘플을 보자.
@dynamicMemberLookup
private struct Wrapper<T> {
init(wrappedValue: T) {
self.wrappedValue = wrappedValue
}
private var wrappedValue: T
subscript<U>(dynamicMember keyPath: WritableKeyPath<T, U>) -> Wrapper<U> {
get {
return Wrapper<U>(wrappedValue: wrappedValue[keyPath: keyPath])
}
}
}
이렇게 하면 value
는 Wrapper
형식의 값임에도 불구하고 String
가 가지고 있어야 하는 속성에 액세스할 수 있습니다. 물론 정의한 대로 Wrapped<Int>
의 값입니다.
이상으로 $hoge.fuga
의 정체는 설명이 붙습니다. $hoge
가 State
라고 하는 propertyWrapper
의 projectedValue
이며, 그 값은 Binding<Hoge>
형입니다. Binding<Hoge>
형은 dynamicMemberLookup
를 서포트하고 있어 이것을 통해서 hoge
의 프로퍼티인 fuga
를 Binding<String>
로 변환해 돌려주는 것입니다.
보충
이상뿐이라고 혹시라면 $hoge[0]
와 같은 기법이 가능한 것이 이상하게 보일지도 모릅니다. 실제로 다음은 완전히 올바르게 움직입니다.
struct MyContentView: View {
@State private var hoge = ["A", "B", "C"]
var body: some View {
TextField("入力してね", text: $hoge[0])
.textFieldStyle(RoundedBorderTextFieldStyle())
}
}
실은 이것도 dynamicMemberLookup
로 실현되고 있습니다. 놀랄 정도로 기분 나쁘지만, subscript
에의 KeyPath
라는 것이 존재하는 덕분입니다.
let keyPath: WritableKeyPath<[Int], Int> = \.[0]
이상으로 대체로 망라 할 수 있었던 것이 아닐까 생각합니다.
참고
Reference
이 문제에 관하여([SwiftUI] $hoge.fuga는 $(hoge.fuga)가 아니다), 우리는 이곳에서 더 많은 자료를 발견하고 링크를 클릭하여 보았다
https://qiita.com/ensan_hcl/items/216e245f65d31d22266f
텍스트를 자유롭게 공유하거나 복사할 수 있습니다.하지만 이 문서의 URL은 참조 URL로 남겨 두십시오.
우수한 개발자 콘텐츠 발견에 전념
(Collection and Share based on the CC Protocol.)
struct Hoge {
var fuga: String = ""
}
struct MyContentView: View {
@State private var hoge = Hoge()
var body: some View {
TextField("入力してね", text: $hoge.fuga)
.textFieldStyle(RoundedBorderTextFieldStyle())
}
}
//Error: '$' is not an identifier; use backticks to escape it
TextField("入力してね", text: $(hoge.fuga))
//Error: Referencing subscript 'subscript(dynamicMember:)' requires wrapper 'Binding<Hoge>'
//Error: Value of type 'Hoge' has no dynamic member '$fuga' using key path from root type 'Hoge'
TextField("入力してね", text: hoge.$fuga)
원래
$
를 이용한 액세스는 propertyWrapper
의 기능에 의한 것입니다. State<T>
의 projectedValue
는 Binding<T>
입니다. 그러므로 $hoge
는 Binding<Hoge>
의 값입니다.그러나 그렇게 하면
Binding<Hoge>
에 속성 fuga
가 존재하는 것이 이상하게 보일 것입니다. 이것을 실현하고 있는 것이 dynamicMemberLookup
입니다. Swift5.1 이후에 KeyPath
를 사용한 Lookup이 가능하게 되었기 때문에, 아마 Binding
가 다음과 같이 정의되고 있습니다.@dynamicMemberLookup
struct Binding<T> {
subscript<U>(dynamicMember keyPath: WritableKeyPath<T, U>) -> Binding<U> {
//処理
}
}
이것만으로는 조금 이해하기 어렵기 때문에, 더 간단한 샘플을 보자.
@dynamicMemberLookup
private struct Wrapper<T> {
init(wrappedValue: T) {
self.wrappedValue = wrappedValue
}
private var wrappedValue: T
subscript<U>(dynamicMember keyPath: WritableKeyPath<T, U>) -> Wrapper<U> {
get {
return Wrapper<U>(wrappedValue: wrappedValue[keyPath: keyPath])
}
}
}
이렇게 하면
value
는 Wrapper
형식의 값임에도 불구하고 String
가 가지고 있어야 하는 속성에 액세스할 수 있습니다. 물론 정의한 대로 Wrapped<Int>
의 값입니다.이상으로
$hoge.fuga
의 정체는 설명이 붙습니다. $hoge
가 State
라고 하는 propertyWrapper
의 projectedValue
이며, 그 값은 Binding<Hoge>
형입니다. Binding<Hoge>
형은 dynamicMemberLookup
를 서포트하고 있어 이것을 통해서 hoge
의 프로퍼티인 fuga
를 Binding<String>
로 변환해 돌려주는 것입니다.보충
이상뿐이라고 혹시라면 $hoge[0]
와 같은 기법이 가능한 것이 이상하게 보일지도 모릅니다. 실제로 다음은 완전히 올바르게 움직입니다.
struct MyContentView: View {
@State private var hoge = ["A", "B", "C"]
var body: some View {
TextField("入力してね", text: $hoge[0])
.textFieldStyle(RoundedBorderTextFieldStyle())
}
}
실은 이것도 dynamicMemberLookup
로 실현되고 있습니다. 놀랄 정도로 기분 나쁘지만, subscript
에의 KeyPath
라는 것이 존재하는 덕분입니다.
let keyPath: WritableKeyPath<[Int], Int> = \.[0]
이상으로 대체로 망라 할 수 있었던 것이 아닐까 생각합니다.
참고
Reference
이 문제에 관하여([SwiftUI] $hoge.fuga는 $(hoge.fuga)가 아니다), 우리는 이곳에서 더 많은 자료를 발견하고 링크를 클릭하여 보았다
https://qiita.com/ensan_hcl/items/216e245f65d31d22266f
텍스트를 자유롭게 공유하거나 복사할 수 있습니다.하지만 이 문서의 URL은 참조 URL로 남겨 두십시오.
우수한 개발자 콘텐츠 발견에 전념
(Collection and Share based on the CC Protocol.)
struct MyContentView: View {
@State private var hoge = ["A", "B", "C"]
var body: some View {
TextField("入力してね", text: $hoge[0])
.textFieldStyle(RoundedBorderTextFieldStyle())
}
}
let keyPath: WritableKeyPath<[Int], Int> = \.[0]
Reference
이 문제에 관하여([SwiftUI] $hoge.fuga는 $(hoge.fuga)가 아니다), 우리는 이곳에서 더 많은 자료를 발견하고 링크를 클릭하여 보았다 https://qiita.com/ensan_hcl/items/216e245f65d31d22266f텍스트를 자유롭게 공유하거나 복사할 수 있습니다.하지만 이 문서의 URL은 참조 URL로 남겨 두십시오.
우수한 개발자 콘텐츠 발견에 전념 (Collection and Share based on the CC Protocol.)