Skip to content

Commit c54854b

Browse files
authored
Merge pull request #405 from OMZigak/refactor/#404-setReadyInfo
[refactor] 준비 정보 입력 뷰 Rx 리팩토링
2 parents 37319d6 + 9f1b8d3 commit c54854b

File tree

3 files changed

+267
-231
lines changed

3 files changed

+267
-231
lines changed

KkuMulKum/Source/Promise/ReadyStatus/ViewController/ReadyStatusViewController.swift

Lines changed: 4 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -253,16 +253,14 @@ extension ReadyStatusViewController {
253253
promiseID: viewModel.promiseID,
254254
promiseTime: promiseTime,
255255
promiseName: promiseName,
256+
storedReadyHour: (preparationTime / 60).description,
257+
storedReadyMinute: (preparationTime % 60).description,
258+
storedMoveHour: (travelTime / 60).description,
259+
storedMoveMinute: (travelTime % 60).description,
256260
service: PromiseService()
257261
)
258262

259-
viewModel.storedReadyHour = preparationTime / 60
260-
viewModel.storedReadyMinute = preparationTime % 60
261-
viewModel.storedMoveHour = travelTime / 60
262-
viewModel.storedMoveMinute = travelTime % 60
263-
264263
let viewController = SetReadyInfoViewController(viewModel: viewModel)
265-
266264
navigationController?.pushViewController(viewController, animated: true)
267265
}
268266

KkuMulKum/Source/SetReadyInfo/ViewController/SetReadyInfoViewController.swift

Lines changed: 116 additions & 154 deletions
Original file line numberDiff line numberDiff line change
@@ -7,13 +7,19 @@
77

88
import UIKit
99

10+
import RxCocoa
11+
import RxSwift
12+
1013
final class SetReadyInfoViewController: BaseViewController {
1114

1215

1316
// MARK: - Property
1417

1518
private let rootView = SetReadyInfoView()
19+
1620
private let viewModel: SetReadyInfoViewModel
21+
private let viewWillAppearRelay = PublishRelay<Void>()
22+
private let disposeBag = DisposeBag()
1723

1824

1925
// MARK: - Initializer
@@ -31,108 +37,152 @@ final class SetReadyInfoViewController: BaseViewController {
3137
// MARK: - LifeCycle
3238

3339
override func loadView() {
34-
self.view = rootView
40+
view = rootView
3541
}
3642

3743
override func viewDidLoad() {
3844
super.viewDidLoad()
3945
view.backgroundColor = .white
4046

41-
setupNavigationBarBackButton()
42-
setupNavigationBarTitle(with: "준비 정보 입력하기")
43-
44-
setupBinding()
45-
setupTapGesture()
46-
setupTextField()
47+
bindViewModel()
4748
}
4849

4950
override func viewWillAppear(_ animated: Bool) {
5051
super.viewWillAppear(animated)
5152

5253
navigationController?.isNavigationBarHidden = false
54+
viewWillAppearRelay.accept(())
55+
}
56+
57+
override func setupView() {
58+
setupNavigationBarBackButton()
59+
setupNavigationBarTitle(with: "준비 정보 입력하기")
5360
}
5461

5562
override func setupDelegate() {
5663
setTextFieldDelegate()
5764
}
5865

5966
override func setupAction() {
60-
rootView.readyHourTextField.addTarget(
61-
self,
62-
action: #selector(textFieldDidChange),
63-
for: .editingChanged
64-
)
65-
rootView.readyMinuteTextField.addTarget(
66-
self,
67-
action: #selector(textFieldDidChange),
68-
for: .editingChanged
69-
)
70-
rootView.moveHourTextField.addTarget(
71-
self,
72-
action: #selector(textFieldDidChange),
73-
for: .editingChanged
74-
)
75-
rootView.moveMinuteTextField.addTarget(
76-
self,
77-
action: #selector(textFieldDidChange),
78-
for: .editingChanged
79-
)
80-
rootView.doneButton.addTarget(
81-
self,
82-
action: #selector(doneButtonDidTap),
83-
for: .touchUpInside
84-
)
67+
setupTextField(textField: rootView.readyHourTextField)
68+
setupTextField(textField: rootView.readyMinuteTextField)
69+
setupTextField(textField: rootView.moveHourTextField)
70+
setupTextField(textField: rootView.moveMinuteTextField)
71+
72+
let tapGesture = UITapGestureRecognizer()
73+
view.addGestureRecognizer(tapGesture)
74+
tapGesture.rx.event
75+
.subscribe(with: self) { owner, _ in
76+
owner.view.endEditing(true)
77+
}
78+
.disposed(by: disposeBag)
8579
}
8680

87-
@objc
88-
private func textFieldDidChange(_ textField: UITextField) {
89-
let text = textField.text ?? ""
90-
viewModel.updateTime(textField: textField.accessibilityIdentifier ?? "", time: text)
91-
viewModel.checkValid(
92-
readyHourText: rootView.readyHourTextField.text ?? "",
93-
readyMinuteText: rootView.readyMinuteTextField.text ?? "",
94-
moveHourText: rootView.moveHourTextField.text ?? "",
95-
moveMinuteText: rootView.moveMinuteTextField.text ?? ""
81+
private func bindViewModel() {
82+
let input = SetReadyInfoViewModel.Input(
83+
viewWillAppear: viewWillAppearRelay,
84+
readyHourText: rootView.readyHourTextField.rx.text.orEmpty.asObservable(),
85+
readyMinuteText: rootView.readyMinuteTextField.rx.text.orEmpty.asObservable(),
86+
moveHourText: rootView.moveHourTextField.rx.text.orEmpty.asObservable(),
87+
moveMinuteText: rootView.moveMinuteTextField.rx.text.orEmpty.asObservable(),
88+
doneButtonDidTap: rootView.doneButton.rx.tap.asObservable()
9689
)
90+
91+
let output = viewModel.transform(input: input, disposeBag: disposeBag)
92+
93+
output.readyHourText
94+
.drive(with: self) { owner, text in
95+
owner.rootView.readyHourTextField.text = text
96+
}
97+
.disposed(by: disposeBag)
98+
99+
output.readyMinuteText
100+
.drive(with: self) { owner, text in
101+
owner.rootView.readyMinuteTextField.text = text
102+
}
103+
.disposed(by: disposeBag)
104+
105+
output.moveHourText
106+
.drive(with: self) { owner, text in
107+
owner.rootView.moveHourTextField.text = text
108+
}
109+
.disposed(by: disposeBag)
110+
111+
output.moveMinuteText
112+
.drive(with: self) { owner, text in
113+
owner.rootView.moveMinuteTextField.text = text
114+
}
115+
.disposed(by: disposeBag)
116+
117+
output.errorMessage
118+
.drive(with: self) { owner, error in
119+
owner.showToast(error)
120+
}
121+
.disposed(by: disposeBag)
122+
123+
output.doneButtonIsEnabled
124+
.drive(with: self) { owner, isEnabled in
125+
owner.rootView.doneButton.backgroundColor = isEnabled ? .maincolor : .gray2
126+
owner.rootView.doneButton.isEnabled = isEnabled
127+
}
128+
.disposed(by: disposeBag)
129+
130+
output.isSucceed
131+
.drive(with: self) { owner, isSucceed in
132+
if isSucceed {
133+
owner.navigateToSetReadyCompleted()
134+
}
135+
}
136+
.disposed(by: disposeBag)
97137
}
98138

99-
@objc
100-
private func doneButtonDidTap(_ sender: UIButton) {
101-
viewModel.updateReadyInfo()
139+
private func setupTextField(textField: UITextField) {
140+
let textFieldEvent = Observable.merge(
141+
textField.rx.controlEvent(.editingDidBegin).map { UIColor.maincolor.cgColor },
142+
textField.rx.controlEvent(.editingDidEnd).map { UIColor.gray3.cgColor },
143+
textField.rx.controlEvent(.editingDidEndOnExit).map { UIColor.gray3.cgColor }
144+
)
145+
146+
textFieldEvent
147+
.bind { borderColor in
148+
textField.layer.borderColor = borderColor
149+
}
150+
.disposed(by: disposeBag)
102151
}
103152

153+
private func navigateToSetReadyCompleted() {
154+
let setReadyCompletedViewController = SetReadyCompletedViewController()
155+
self.navigationController?.pushViewController(
156+
setReadyCompletedViewController,
157+
animated: true
158+
)
159+
}
104160

105-
// MARK: - Keyboard Dismissal
106-
107-
private func setupTapGesture() {
108-
let tapGesture = UITapGestureRecognizer(target: self, action: #selector(dismissKeyboard))
109-
view.addGestureRecognizer(tapGesture)
161+
private func showToast(_ message: String, bottomInset: CGFloat = 128) {
162+
guard let view else { return }
163+
Toast().show(message: message, view: view, position: .bottom, inset: bottomInset)
110164
}
111165

112-
@objc private func dismissKeyboard() {
113-
view.endEditing(true)
166+
private func setTextFieldDelegate() {
167+
let textFields: [(UITextField, String)] = [
168+
(rootView.readyHourTextField, "readyHour"),
169+
(rootView.readyMinuteTextField, "readyMinute"),
170+
(rootView.moveHourTextField, "moveHour"),
171+
(rootView.moveMinuteTextField, "moveMinute")
172+
]
173+
174+
textFields.forEach { (textField, identifier) in
175+
textField.delegate = self
176+
textField.keyboardType = .numberPad
177+
textField.accessibilityIdentifier = identifier
178+
}
114179
}
115180
}
116181

117182

118183
// MARK: - UITextFieldDelegate
119184

120185
extension SetReadyInfoViewController: UITextFieldDelegate {
121-
func textFieldDidBeginEditing(_ textField: UITextField) {
122-
textField.layer.borderColor = UIColor.maincolor.cgColor
123-
}
124-
125-
func textFieldDidEndEditing(_ textField: UITextField) {
126-
textField.layer.borderColor = UIColor.gray3.cgColor
127-
128-
if let text = textField.text, !text.isEmpty {
129-
viewModel.updateTime(
130-
textField: textField.accessibilityIdentifier ?? "",
131-
time: textField.text ?? ""
132-
)
133-
}
134-
}
135-
136186
func textField(
137187
_ textField: UITextField,
138188
shouldChangeCharactersIn range: NSRange,
@@ -143,91 +193,3 @@ extension SetReadyInfoViewController: UITextFieldDelegate {
143193
return allowedCharacters.isSuperset(of: characterSet)
144194
}
145195
}
146-
147-
148-
// MARK: - Function
149-
150-
private extension SetReadyInfoViewController {
151-
func setupTextField() {
152-
/// 저장된 준비 시간이 0이 아니면 텍스트 필드에 설정
153-
if viewModel.storedReadyHour != 0 || viewModel.storedReadyMinute != 0 {
154-
rootView.readyHourTextField.text = String(viewModel.storedReadyHour)
155-
rootView.readyMinuteTextField.text = String(viewModel.storedReadyMinute)
156-
}
157-
158-
/// 저장된 이동 시간이 0이 아니면 텍스트 필드에 설정
159-
if viewModel.storedMoveHour != 0 || viewModel.storedMoveMinute != 0 {
160-
rootView.moveHourTextField.text = String(viewModel.storedMoveHour)
161-
rootView.moveMinuteTextField.text = String(viewModel.storedMoveMinute)
162-
}
163-
164-
viewModel.checkValid(
165-
readyHourText: rootView.readyHourTextField.text ?? "",
166-
readyMinuteText: rootView.readyMinuteTextField.text ?? "",
167-
moveHourText: rootView.moveHourTextField.text ?? "",
168-
moveMinuteText: rootView.moveMinuteTextField.text ?? ""
169-
)
170-
}
171-
172-
func setTextFieldDelegate() {
173-
let textFields: [(UITextField, String)] = [
174-
(rootView.readyHourTextField, "readyHour"),
175-
(rootView.readyMinuteTextField, "readyMinute"),
176-
(rootView.moveHourTextField, "moveHour"),
177-
(rootView.moveMinuteTextField, "moveMinute")
178-
]
179-
180-
textFields.forEach { (textField, identifier) in
181-
textField.delegate = self
182-
textField.keyboardType = .numberPad
183-
textField.accessibilityIdentifier = identifier
184-
}
185-
}
186-
187-
func showToast(_ message: String, bottomInset: CGFloat = 128) {
188-
guard let view else { return }
189-
Toast().show(message: message, view: view, position: .bottom, inset: bottomInset)
190-
}
191-
192-
// MARK: - Data Bind
193-
194-
func setupBinding() {
195-
viewModel.readyHour.bind { [weak self] readyHour in
196-
self?.rootView.readyHourTextField.text = readyHour
197-
}
198-
199-
viewModel.readyMinute.bind { [weak self] readyMinute in
200-
self?.rootView.readyMinuteTextField.text = readyMinute
201-
}
202-
203-
viewModel.moveHour.bind { [weak self] moveHour in
204-
self?.rootView.moveHourTextField.text = moveHour
205-
}
206-
207-
viewModel.moveMinute.bind { [weak self] moveMinute in
208-
self?.rootView.moveMinuteTextField.text = moveMinute
209-
}
210-
211-
viewModel.isValid.bind { [weak self] isValid in
212-
self?.rootView.doneButton.isEnabled = isValid
213-
}
214-
215-
viewModel.errMessage.bind { [weak self] err in
216-
if !err.isEmpty {
217-
self?.showToast(err)
218-
}
219-
}
220-
221-
viewModel.isSucceedToSave.bind { [weak self] _ in
222-
if self?.viewModel.isSucceedToSave.value == true {
223-
DispatchQueue.main.async {
224-
let viewController = SetReadyCompletedViewController()
225-
self?.navigationController?.pushViewController(
226-
viewController,
227-
animated: true
228-
)
229-
}
230-
}
231-
}
232-
}
233-
}

0 commit comments

Comments
 (0)