์ถ์ฒ ๋ฐ ์ฐธ๊ณ ์ฌ์ดํธ
https://axiomatic-fuschia-666.notion.site/Chapter-3-TCA-2-c56b24efb2154dad9ed8e54139247024
Chapter 3. TCA์ ๊ธฐ๋ณธ๊ฐ๋ (2)
์์ ์ฅ์์ ์ฐ๋ฆฌ๋ ์ฑ์ ์ํ๋ฅผ ๋ํ๋ด๋ State์ ์ด๋ฅผ ๋ณ๊ฒฝํ ์๋จ์ธ Action, ๊ทธ Action์ ๊ธฐ๋ฅ์ ๊ตฌํํ๊ณ ์ํ์ ๋ณ๊ฒฝ์ ์ฒ๋ฆฌํ๋ Reducer์ ์์๋ณด๋ฉฐ, TCA์์์ ๋ฐ์ดํฐํ๋ฆ์ ๋ํด์ ์ดํด๋ณด์์ต๋
axiomatic-fuschia-666.notion.site
What is Store
- ๋ฐ ํ์๋์ Reducer์ ์ธ์คํด์ค๋ฅผ ๊ด๋ฆฌํ๋ ์ฐธ์กฐ ํ์ ๊ฐ์ฒด
- ์ฑ์ State, Action์ ๊ด๋ฆฌ
- State์ ๋ณํ ๊ฐ์ง
- ์ก์ ์ฒ๋ฆฌ
let store: Store<ProductDetailFeature.State, ProductDetailFeature.Action>
public typealias StoreOf<R: Reducer> = Store<R.State, R.Action>
let store: StoreOf<ProductDetailFeature>
typealias ์ถ์ฝ ํํ
ViewStore๊ฐ ๋ํ๋๊ฒ ๋ ๋ฐฐ๊ฒฝ
- Store: ์ฑ์ ์ํ ๋ณํ ๊ด๋ฆฌ
- ViewStore: View์ ํ์ํ ์ํ๋ง ๊ตฌ๋ ํ๊ณ ์ ๋ฐ์ดํธ
- View์ ํ์ํ์ง ์์ ์ํ์ ๋ณ๊ฒฝ์ผ๋ก ์ธํ ๋ถํ์ํ View ์ ๋ฐ์ดํธ๋ฅผ ๋ฐฉ์ง
- MultiStore
- ์์ View์ Store๋ ํ์ View์ ํ์๋ก ํ๋ ์ํ ์ผ๋ถ๋ฅผ ์์ ํ๊ณ ์๊ณ , ํ์ View์ ์ด ์ผ๋ถ๋ฅผ ์์ ํ๋ ๋ณ๋์ ์คํ ์ด๋ฅผ ์ฐ๊ฒฐํ๊ฒ ํจ
- ์์ View์์ ์ก์ ์ ๋ฐ์ผ๋ฉด ์ด๋ฅผ ๋ถ๋ชจ ์คํ ์ด์ ์ ๋ฌ
- ์ก์ ์ ๋ฐ์ ๋ถ๋ชจ ์คํ ์ด๋ ์์ฒด ๋ฆฌ๋์ ํธ์ถ -> ๋ด๋ถ ์ํ ์ ๋ฐ์ดํธ
- ์ก์ ์ด ์ด๋ฃจ์ด์ง๋ ๋์ ๋ถ๋ชจ ๋ทฐ์ ์์ ๋ทฐ์์ View ๋ ๋๋ง ์์ฒญ์ด ์ค๋ฉด ์ก์ ์ผ๋ก ์ธํ ๋ ๋๋ง ๋ฟ๋ง ์๋๋ผ ์์ ๋ทฐ์ ๋ ๋๋ง ์์ฒญ์ ์ํด ์ฌ๋ฌ๋ฒ ๋ ๋๋ง ํ๋ ์ํฉ์ด ๋ฐ์ ๋จ
- but ViewStore๋ ๋ณํ์ ์ค๋ณต์ ๋ฐฉ์งํ๋ ๊ธฐ๋ฅ์ ํ์ฌ -> View์ ๋ชจ๋ ์ํ๋ฅผ ๊ด์ฐฐ x, ์ผ๋ถ๋ถ์ ๋ณํ๋ก View๋ ๊ทธ ๋ณํ๋ฅผ ๊ฐ์งํ ์ ์์
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.stateSubject.value))
self._isInvalidated = store._isInvalidated
#if DEBUG
self.storeTypeName = ComposableArchitecture.storeTypeName(of: store)
Logger.shared.log("View\(self.storeTypeName).init")
#endif
self.viewCancellable = store.stateSubject
.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
}
}
- ์ ์ฝ๋์์ @escaping (_ state: State) -> ViewState ์์ View์ ํ์ํ ๋ณํ๋ฅผ ๊ด์ฐฐํ๊ธฐ ์ํด View์ ํ์ํ State struct๋ฅผ ๊ตฌ์ฑ
- ์๋ณธ State๋ฅผ ๊ด์ฐฐํ ์ํ๋ก ๊ตฌ์ฑํ ViewState๋ก ๋ณํ
- ViewState์ ๋ณํ๊ฐ ๊ฐ์ง๋๋ฉด SwiftUI View์๊ฒ ์ํ ๋ณํ๋ฅผ ์๋ฆผ
- isDuplicate๋ก ๋ณํ์ ์ค๋ณต์ ์์ค ๋ค ๋ด๋ถ์ State๊ฐ์ ๊ฐฑ์
WithViewStore
- Reducer ๊ตฌ์ฑ ํ WithViewStore๋ก Store์ View ์ฐ๊ฒฐ
let store: StoreOf<ProductDetailFeature>
var body: some View {
WithViewStore(self.store, observe: { $0 }) { viewStore in
// ๊ธฐ์กด ๋ฐฉ์
}
}
- ๊ธฐ์กด ๋ฐฉ์์ ํ์ ์ ๋ช ์ํด์ ์ปดํ์ผ๋ฌ์ ์ฐ์ฐ์ ์ค์ผ ์ ์์
let store: StoreOf<ProductDetailFeature>
@ObservedObject var viewStore: ViewStoreOf<ProductDetailFeature>
init(store: StoreOf<ProductDetailFeature>) {
self.store = store
self.viewStore = ViewStore(self.store, observe: { $0 })
}
- ์ด๋์ ๋ผ์ด์ ๋ก Store์ ์ฃผ์ ๋ฐ์
- ์ด๋์ ๋ผ์ด์ ๋ด๋ถ์์ ViewStore์ ์์ฑ
- View ๊ณ์ธต ๊ตฌ์กฐ์ ์ ์ด ์ฃผ์๋ WithViewStore ~ ์ ์ค์ผ ์ ์์ด ๊ธฐ์กด ๋ณด๋ค View ๊ณ์ธต์ ์ค์ผ ์ ์์
@dynamicMemberLookup
public final class ViewStore<ViewState, ViewAction>: ObservableObject { /* ... */ }
N.B. `ViewStore` does not use a `@Published` property, so `objectWillChange` won't be synthesized automatically. To work around issues on iOS 13 we explicitly declare it. |
Scope
public func scope<ChildState, ChildAction>(
state toChildState: @escaping (_ state: State) -> ChildState,
action fromChildAction: @escaping (_ childAction: ChildAction) -> Action
) -> Store<ChildState, ChildAction> {
self.scope(state: toChildState, action: fromChildAction, removeDuplicates: nil)
}
- Store์ scope ๋ฉ์๋๋ฅผ ์ด์ฉํด ํ์ State ๋ฐ Action์ ๋ค๋ฃจ๋ ์คํ ์ด๋ก ๋ณํ
- Store์ ์์ ๋ฒ์์ Store๋ก ๋๋ ์ ์์
- ํด๋น ๋๋ฉ์ธ์ ์ํ์ ์ก์ ์ ์ถ์ถํ ์์ ๋ฒ์์ Store์ ๋ฐํ
- ๊ฐ View์ ํ์ํ ์ํ์ ์ก์ ์ ์ ์ํ๊ณ ์ ๊ทผํ ์ ์์
- ๋ชจ๋ํ์ ์ ์ฐ์ฑ
- ์ ๋ ํ ์คํธ ์ฉ์ด
- state: ์ํ๋ฅผ ์ถ์ถํ KeyPath๋ก์ ์์ ๋ทฐ์ state๋ฅผ ์ถ์ถ (WritableKeyPath)
- action: ์ก์ ์ ์ถ์ถํ๊ธฐ ์ํ KeyPath (CasePath)
AmountControlView(
store: self.store.scope(state: \.amountControl, action:
ProductDetailFeature.Action.amountControl)
)
- self.store๋ ์์ ๋ทฐ์ธ ProductDetailFeature์ Store
Scope ๋ ์์๋ณด๊ธฐ
2023.11.30 - [Architecture] - [SwiftUI/TCA] Scope
'iOS ๐ > Architecture' ์นดํ ๊ณ ๋ฆฌ์ ๋ค๋ฅธ ๊ธ
[SwiftUI/TCA] Binding (0) | 2023.12.07 |
---|---|
[SwiftUI/TCA] Scope (1) | 2023.11.30 |
[SwiftUI/TCA] Effect ๊ตฌํ๊ณผ ํ์ฉ (0) | 2023.11.29 |
[SwiftUI/TCA] Timer ๋ง๋ค๊ธฐ (1) | 2023.11.24 |
[SwiftUI/TCA] TCA ๊ธฐ๋ณธ ๊ฐ๋ ์ ๋ํด ์์๋ณด๊ธฐ (0) | 2023.11.21 |