조동의 SwiftUI 구조 연구 대소동
- 다운로드 후 Toolings 디렉토리의 install-JDA 실행
sh install-JDA.sh
- 뷰모델 클래스에 ViewModelType 프로토콜 채택
import JDA
final class ContentViewModel: ViewModelType { }
- 기본 프로퍼티 3형제 초기화
@Published var state = State()
var action = ActionSubject<Action>()
var cancelable = Set<AnyCancellable>()
- View에 Binding되는 Action (enum)
enum Action {
case buttonDidTap
case validationText(String)
}
- Action에 의한 Networking과 같은 effect가 일어남
- Publisher<Effect, Never>를 반환
func effect(action: Action) -> any Publisher<Effect, Never> {
switch action {
case .buttonDidTap:
guard let id = Int(state.inputText) else {
return Empty().eraseToAnyPublisher()
}
return APIService.shared.singleUser(id: id)
.map { _ in .reqres }
.catch { _ in return Just<Effect>(.reqresError) }
case .validationText(let text):
return Just(.validation(text))
}
}
- effect()의 결과로 실행되는 Effect (enum)
enum Effect {
case reqres
case reqresError
case validation(String)
}
- Effect의 실행으로 State 변경
func reduce(effect: Effect, state: State) -> State {
var newState = state
switch effect {
case .reqres:
newState.response = "reqres"
case .reqresError:
newState.response = "Error Occur"
case .validation(let text):
newState.inputText = text
newState.validText = text.uppercased()
}
return newState
}
- View의 상태
struct State {
var response: String = "response"
var inputText: String = ""
var validText: String = "validtext"
}
- View는 ViewType 프로토콜 채택
struct ContentView: ViewType { }
typealias ViewModel = ContentViewModel
@StateObject var viewModel: ContentViewModel
- Button 등의 Action
Button("Request") {
viewModel.action.send(.buttonDidTap)
}
- TextField 등의 Binding
TextField("Validation Text", text: viewModel.value({
$0.inputText
}, action: {
ContentViewModel.Action.validationText($0)
}))
- 필요한곳에 ViewModel State 사용
Text(viewModel.state.response)
Text(viewModel.state.validText)