안녕하세요! 미니입니다. 이번에는 TCA로 프로젝트를 진행하면서 학습한 내용을 정리해보려고 합니다. 많은 것을 정리하는 것은 아니고, 개념적인 내용에 대해서 설명하려고 합니다. TCA를 조금 써보면 ViewStore라는 친구를 바로 만나게 되는데 내부적으로 어떤 동작이 일어나는 지 궁금해져서 학습한 내용을 공유해보려고 합니다.
우선 공식 문서 부터 조져보자구욧!
ViewStore는 상태의 변화와 액션 발생에 대해서 관찰할 수 있는 객체이다. 일반적으로 SwiftUI의 뷰나 UIView, UIViewController 타입에서 사용할 수 있다. 하지만, 상태를 관찰하고 액션을 전달하는 것이 적합한 곳이면 어디든 사용할 수 있다.
상태를 관찰할 수 있다는 말에서 Combine과 SwiftUI에서 많이 보던 친구라는 것이 느껴지시나요? 저는 ObservableObject 라는 친구가 생각 났습니다.
이렇게 여쭤본 이유는 이 친구이기 때문이에요. ViewStore는 다른 친구가 아니라 ObservableObject를 채택한 상속 불가능한 참조 타입입니다.
@dynamicMemberLookup
public final class ViewStore<ViewState, ViewAction>: ObservableObject {
public init<State>(
_ store: Store<State, ViewAction>,
observe toViewState: @escaping (_ state: State) -> ViewState,
removeDuplicates isDuplicate: @escaping (_ lhs: ViewState, _ rhs: ViewState) -> Bool
) {
self._send = { store.send($0, originatingFrom: nil) }
self._state = CurrentValueRelay(toViewState(store.state.value))
self._isInvalidated = store._isInvalidated
self.viewCancellable = store.state
.map(toViewState)
.removeDuplicates(by: isDuplicate)
.sink { [weak objectWillChange = self.objectWillChange, weak _state = self._state] in
guard let objectWillChange = objectWillChange, let _state = _state else { return }
objectWillChange.send()
_state.value = $0
}
}
}
ObservableObject라는 것은 납득이 가능하지만, dynamicMemberLookup이라는 어노테이션을 모르기에 찾아보려고 합니다.
dynamicMemberLookup
영어는 무적권 직독직해!! 동적 / 멤버 / 조회
라는 해석을 할 수 있겠죠? 좀 더 유식하게 말을 바꿔보시죠! 동적으로 값들을 조회할 수 있다.
와 같이 해석할 수 있습니다. 실제로 저희가 많이 사용하던 Subscript를 문자열이나 key값을 대괄호안에 작성해서 값을 얻게 되지만, dynamicMemberLookup
을 활용하게 되면, 도트를 통해서 접근할 수 있게 됩니다.
@dynamicMemberLookup
struct Person {
var name: String
var age: Int
subscript(key: String) -> String {
switch key {
case "info":
return "\(name) : \(age)"
default:
return "nothing"
}
}
subscript(dynamicMember key: String) -> String {
switch key {
case "member":
return "\(name) : \(age)"
default:
return "nothing"
}
}
}
var p = Person(name:"Sweet", age:17)
p["info"]
p.member
그렇다는 건 ViewStore는 내부적으로 키값을 생성하지 않고 도트를 통해서 속성에 접근하거나 값을 얻을 수 있다는 이야기가 되겠군요.
self.viewCancellable = store.state
.map(toViewState)
.removeDuplicates(by: isDuplicate)
.sink { [weak objectWillChange = self.objectWillChange, weak _state = self._state] in
guard let objectWillChange = objectWillChange, let _state = _state else { return }
objectWillChange.send()
_state.value = $0
}
생성자 내에서 이부분을 보게 되면, 어떻게 상태가 변경되었을 때 알려주는 거지?
와 같은 궁금증을 풀 수 있습니다. store에 생성되어 있는 state는 Publisher 타입이기 때문에 sink를 통해서 변경에 대한 이벤트를 받을 수 있습니다. 이를 통해서 내부적으로 값이 변경되었다는 이벤트를 다시 한번 방출 시키고 변경된 값을 기존의 값으로 업데이트하게 됩니다. 이런 방식을 통해서 저희는 View에서 액션을 전달하고, 상태가 변경될 때마다 뷰를 다시 그릴 수 있는 것입니다.
오늘은 글이 많이 짧지만, 다음에는 TCA를 통한 예제를 가지고 오려고 합니다. 기대해주세욧!!
'IOS > SwiftUI' 카테고리의 다른 글
TCA - 네트워크 요청하기 (0) | 2023.10.18 |
---|---|
TCA 기본 (0) | 2023.09.26 |
Data Flow Through SwiftUI (0) | 2023.09.07 |