-
[SwiftUI] ์ํ๊ด๋ฆฌ(redux)iOS/TCA 2023. 7. 6. 17:30
PointFree ๊ฐ์ Composable Architecture
Composable State Management: Reducers ์ ๋ฆฌ๐ ์ด๋ฒ ๊ฐ์ ๋ชฉํ
์ ๋ฒ ๊ฒ์๊ธ์์ ์์ฑํ ์ฝ๋์์
1. Combine์ผ๋ก๋ถํฐ ๋ถ๋ฆฌ
2. ๊ฐ ํ์ ์ ์ด์ ํ์ฉ์ ์ํด ์ํ ๋ณํ๋ฅผ ๊ฐ์งํ๋ ๊ฐ์ฒด๋ฅผ ๋ง๋ค๊ณ ์ ํ๋ค!
๐ ์ฝ๋
struct AppState { var count = 0 var favoritePrimes: [Int] = [] var loggedInUser: User? var activityFeed: [Activity] = [] var didChange = PassthroughSubject<Void, Never>() struct Activity { let timestamp: Date let type: ActivityType enum ActivityType { case addedFavoritePrime(Int) case removedFavoritePrime(Int) } } struct User { let id: Int let name: String let bio: String } }
๊ธฐ์กด class ํ์ ์ AppState ์ฝ๋๋ฅผ ๊ตฌ์กฐ์ฒด๋ก ๋ณ๊ฒฝํ๋ค. ์ด๋ฌ๋ฉด ๋์ด์ @Published๋ก ๋ณ์๋ฅผ ์ ์ธํ์ง ์์๋ ๋๋ค.
์ฌ๊ธฐ์ ObservableObject๋ฅผ ์ค์ํ๊ธฐ ์ํด์ ๊ตฌ์กฐ์ฒด๋ฅผ ๊ฐ์ธ๋ ํด๋์ค๋ฅผ ์๋ก ์ ์ธํด์ผํจ(Store)
final class Store<Value, Action>: ObservableObject { let reducer: (inout Value, Action) -> Void @Published var value: Value init(initialValue: Value, reducer: @escaping (inout Value, Action) -> Void) { self.value = initialValue self.reducer = reducer } func send(_ action: Action) { self.reducer(&self.value, action) } }
AppState์ ๋ณํ๋ฅผ ๊ฐ์งํ๋ Store<AppState> ๊ฐ์ฒด๋ฅผ ์์ฑํ๊ธฐ ์ํ ํด๋์ค๋ฅผ ์ ์ํ๋ค.
Store ํด๋์ค์ ์ญํ : ๊ฐ ์ ํ์ ๋ํํด์ ๊ด์ฐฐ์์๊ฒ ํ ์ ๊ณต
AppState์ ๋ํด์๋ ์ ํ์๊ฐ ์๊ธฐ ๋๋ฌธ์ ์ ๋ค๋ฆญ ํ์ ์ผ๋ก ์ ์ธ// ์ฌ์ฉ์ ์ก์ ํ์ ์ง์ enum CounterAction { case decrementTapped case incrementTapped } enum PrimeModalAction { case saveFavoritePrimeTapped case removeFavoritePrimeTapped } enum FavoritePrimesAction { case deleteFavoritePrimes(IndexSet) } enum AppAction { case counter(CounterAction) case primeModal(PrimeModalAction) case favoritePrimes(FavoritePrimesAction) } func appReducer(state: inout AppState, action: AppAction) -> Void { switch action { case .counter(.decrementTapped): state.count -= 1 case .counter(.incrementTapped): state.count += 1 case .primeModal(.saveFavoritePrimeTapped): state.favoritePrimes.append(state.count) state.activityFeed.append(.init(timestamp: Date(), type: .addedFavoritePrime(state.count))) case .primeModal(.removeFavoritePrimeTapped): state.favoritePrimes.removeAll(where: { $0 == state.count }) state.activityFeed.append(.init(timestamp: Date(), type: .removedFavoritePrime(state.count))) case let .favoritePrimes(.deleteFavoritePrimes(indexSet)): for index in indexSet { let prime = state.favoritePrimes[index] state.favoritePrimes.remove(at: index) state.activityFeed.append(.init(timestamp: Date(), type: .removedFavoritePrime(prime))) } } }
์ฌ์ฉ์ ์ก์ ํ์ ๊ณผ ๊ทธ์ ๋ฐ๋ฅธ ์ก์ ์ ์ ์ํ๋ค
@ObservedObject var store: Store<AppState, AppAction>
ObservedObject ๋ณ์๋ฅผ ๋ค์๊ณผ ๊ฐ์ด ๋ณ๊ฒฝํด์ค๋ค
์ํ ๋ณํ์ ๋ฐ๋ฅธ ํ๋์ reducer์ ๊ฒฉ๋ฆฌํด์ store์์ send() ๋ฉ์๋ ํธ์ถ์ ํตํด ํ ์ ์๋๋ก ํ๋ค.
'iOS > TCA' ์นดํ ๊ณ ๋ฆฌ์ ๋ค๋ฅธ ๊ธ
[TCA] TCA ์๋ ๋ฐฉ์ (0) 2023.08.31 [SwiftUI] ์ํ๊ด๋ฆฌ(Higher-Order Reducers) (0) 2023.08.02 [SwiftUI] ์ํ๊ด๋ฆฌ(Action Pullbacks) (0) 2023.07.18 [SwiftUI] ์ํ๊ด๋ฆฌ(State Pullbacks) (0) 2023.07.07 [SwiftUI] ์ํ ๊ด๋ฆฌ (0) 2023.07.05