The Composable Architecture
Elm이나 Redux 라이브러리의 아이디어에 기반하여 Point-Free에서 만든 상태 관리 기반 단방향 아키텍처
Combine 프레임워크를 dependency로 가지고 있어 최소 iOS 13, macOS 10.15, Mac Catalyst 12, tvOS 12 그리고 watchOS 6 이상이어야 함
(만약 더 이전 OS를 지원해야 하는 경우 ReactiveSwift나 RxSwift 버전을 사용)
값 타입으로 앱 상태를 관리하고 상태 공유를 통해 화면에서 일어나는 변화를 다른 화면에서 즉시 관측하는 방법 제공
기능을 여러 독립된 모듈로 추출하거나 다시 결합하여 컴포넌트의 집합으로 구성하는 방법 제공
테스트 가능하고 이해하기 쉬운 방식으로 외부와 통신하도록 하는 방법 제공
아키텍처 내부의 기능 테스트 뿐 아니라 여러 파트로 구성된 기능의 통합 테스트 작성
사이트 이펙트가 앱에 끼치는 영향을 테스트
이러한 테스트를 통해 비즈니스 로직이 잘 작동하는지 보장
위 내용들을 가능한 적은 개념의 간단한 API로 이루는 방법 제공
Swift Packages › Add Package Dependency
를 선택https://github.com/pointfreeco/swift-composable-architecture
를 입력하여 추가State
비즈니스 로직 수행 및 UI 렌더링을 위한 데이터 타입Action
사용자 액션, 알림 및 이벤트 등 앱에서 발생할 수 있는 모든 작업을 나타내는 타입Reducer
액션(Action)에 대해 상태(State)를 다음 상태로 변화시키는 함수. EffectTask를 반환하 여 실행할 수 있는 이펙트(Effect, API 요청 등)를 반환하는 역할Store
실제 기능을 구동하는 공간. 사용자 행동(Action)을 Store로 보내면 Reducer와 Effect 를 실행할 수 있고, Store에서 일어나는 상태(State) 변화를 관측하여 UI를 업데이트
struct Feature: ReducerProtocol {
}
struct Feature: ReducerProtocol {
struct State: Equatable {
var count = 0
var numberFactAlert: String?
}
}
struct Feature: ReducerProtocol {
enum Action: Equatable {
case factAlertDismissed
case decrementButtonTapped
case incrementButtonTapped
case numberFactButtonTapped
case numberFactResponse(String)
}
}
struct Feature: ReducerProtocol {
func reduce(into state: inout State, action: Action) -> EffectTask<Action> {
switch action {
case .factAlertDismissed:
state.numberFactAlert = nil
return .none
case .decrementButtonTapped:
state.count -= 1
return .none
case .incrementButtonTapped:
state.count += 1
return .none
case .numberFactButtonTapped:
return .run { [count = state.count] send in
let (data, _) = try await URLSession.shared.data(
from: URL(string: "http://numbersapi.com/\(count)/trivia")!
)
await send(
.numberFactResponse(String(decoding: data, as: UTF8.self)
)
}
case let .numberFactResponse(fact):
state.numberFactAlert = fact
return .none
}
}
}
}
.none
을 반환struct FeatureView: View {
let store: StoreOf<Feature>
var body: some View {
WithViewStore(self.store, observe: { $0 }) { viewStore in
VStack {
HStack {
Button("−") { viewStore.send(.decrementButtonTapped) }
Text("\(viewStore.count)")
Button("+") { viewStore.send(.incrementButtonTapped) }
}
Button("Number fact") { viewStore.send(.numberFactButtonTapped) }
}
.alert(
item: viewStore.binding(
get: { $0.numberFactAlert.map(FactAlert.init(title:)) },
send: .factAlertDismissed
),
content: { Alert(title: Text($0.title)) }
)
}
}
}
StoreOf<Feature>
을 유지@main
struct MyApp: App {
var body: some Scene {
FeatureView(
store: Store(initialState: Feature.State()) {
Feature()
}
)
}
}
초기 State와 앱을 가동시킬 Reducer를 지정
참고 자료
개발 문서
항상 좋은 글 감사합니다.