Skip to content

Commit cb7b5fd

Browse files
committedDec 9, 2024
refactor/#404 텍스트필드 조건 검사 및 입력 제한 로직 Rx 리팩토링
1 parent e73e46c commit cb7b5fd

File tree

2 files changed

+128
-82
lines changed

2 files changed

+128
-82
lines changed
 

‎KkuMulKum/Source/SetReadyInfo/ViewController/SetReadyInfoViewController.swift

+41-53
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,7 @@ final class SetReadyInfoViewController: BaseViewController {
4646
setupBinding()
4747
setupTapGesture()
4848
setupTextField()
49+
bindViewModel()
4950
}
5051

5152
override func viewWillAppear(_ animated: Bool) {
@@ -69,33 +70,48 @@ final class SetReadyInfoViewController: BaseViewController {
6970
setupTextField(textField: rootView.moveHourTextField)
7071
setupTextField(textField: rootView.moveMinuteTextField)
7172

72-
rootView.readyHourTextField.addTarget(
73-
self,
74-
action: #selector(textFieldDidChange),
75-
for: .editingChanged
76-
)
77-
rootView.readyMinuteTextField.addTarget(
78-
self,
79-
action: #selector(textFieldDidChange),
80-
for: .editingChanged
81-
)
82-
rootView.moveHourTextField.addTarget(
83-
self,
84-
action: #selector(textFieldDidChange),
85-
for: .editingChanged
86-
)
87-
rootView.moveMinuteTextField.addTarget(
88-
self,
89-
action: #selector(textFieldDidChange),
90-
for: .editingChanged
91-
)
9273
rootView.doneButton.addTarget(
9374
self,
9475
action: #selector(doneButtonDidTap),
9576
for: .touchUpInside
9677
)
9778
}
9879

80+
private func bindViewModel() {
81+
let input = SetReadyInfoViewModel.Input(
82+
readyHourText: rootView.readyHourTextField.rx.text.orEmpty.asObservable(),
83+
readyMinuteText: rootView.readyMinuteTextField.rx.text.orEmpty.asObservable(),
84+
moveHourText: rootView.moveHourTextField.rx.text.orEmpty.asObservable(),
85+
moveMinuteText: rootView.moveMinuteTextField.rx.text.orEmpty.asObservable()
86+
)
87+
88+
let output = viewModel.transform(input: input, disposeBag: disposeBag)
89+
90+
output.readyHourText
91+
.drive(with: self) { owner, text in
92+
owner.rootView.readyHourTextField.text = text
93+
}
94+
.disposed(by: disposeBag)
95+
96+
output.readyMinuteText
97+
.drive(with: self) { owner, text in
98+
owner.rootView.readyMinuteTextField.text = text
99+
}
100+
.disposed(by: disposeBag)
101+
102+
output.moveHourText
103+
.drive(with: self) { owner, text in
104+
owner.rootView.moveHourTextField.text = text
105+
}
106+
.disposed(by: disposeBag)
107+
108+
output.moveMinuteText
109+
.drive(with: self) { owner, text in
110+
owner.rootView.moveMinuteTextField.text = text
111+
}
112+
.disposed(by: disposeBag)
113+
}
114+
99115
private func setupTextField(textField: UITextField) {
100116
let textFieldEvent = Observable.merge(
101117
textField.rx.controlEvent(.editingDidBegin).map { UIColor.maincolor.cgColor },
@@ -110,18 +126,6 @@ final class SetReadyInfoViewController: BaseViewController {
110126
.disposed(by: disposeBag)
111127
}
112128

113-
@objc
114-
private func textFieldDidChange(_ textField: UITextField) {
115-
let text = textField.text ?? ""
116-
viewModel.updateTime(textField: textField.accessibilityIdentifier ?? "", time: text)
117-
viewModel.checkValid(
118-
readyHourText: rootView.readyHourTextField.text ?? "",
119-
readyMinuteText: rootView.readyMinuteTextField.text ?? "",
120-
moveHourText: rootView.moveHourTextField.text ?? "",
121-
moveMinuteText: rootView.moveMinuteTextField.text ?? ""
122-
)
123-
}
124-
125129
@objc
126130
private func doneButtonDidTap(_ sender: UIButton) {
127131
viewModel.updateReadyInfo()
@@ -204,31 +208,15 @@ private extension SetReadyInfoViewController {
204208
// MARK: - Data Bind
205209

206210
func setupBinding() {
207-
viewModel.readyHour.bind { [weak self] readyHour in
208-
self?.rootView.readyHourTextField.text = readyHour
209-
}
210-
211-
viewModel.readyMinute.bind { [weak self] readyMinute in
212-
self?.rootView.readyMinuteTextField.text = readyMinute
213-
}
214-
215-
viewModel.moveHour.bind { [weak self] moveHour in
216-
self?.rootView.moveHourTextField.text = moveHour
217-
}
218-
219-
viewModel.moveMinute.bind { [weak self] moveMinute in
220-
self?.rootView.moveMinuteTextField.text = moveMinute
221-
}
222-
223211
viewModel.isValid.bind { [weak self] isValid in
224212
self?.rootView.doneButton.isEnabled = isValid
225213
}
226214

227-
viewModel.errMessage.bind { [weak self] err in
228-
if !err.isEmpty {
229-
self?.showToast(err)
230-
}
231-
}
215+
// viewModel.errMessage.bind { [weak self] err in
216+
// if !err.isEmpty {
217+
// self?.showToast(err)
218+
// }
219+
// }
232220

233221
viewModel.isSucceedToSave.bind { [weak self] _ in
234222
if self?.viewModel.isSucceedToSave.value == true {

‎KkuMulKum/Source/SetReadyInfo/ViewModel/SetReadyInfoViewModel.swift

+87-29
Original file line numberDiff line numberDiff line change
@@ -8,15 +8,28 @@
88
import Foundation
99
import UserNotifications
1010

11+
import RxCocoa
12+
import RxSwift
13+
14+
enum Time {
15+
case hour
16+
case minute
17+
}
18+
1119
final class SetReadyInfoViewModel {
1220
let promiseID: Int
1321
let promiseName: String
1422
let promiseTime: String
1523

1624
let isValid = ObservablePattern<Bool>(false)
17-
let errMessage = ObservablePattern<String>("")
1825
let isSucceedToSave = ObservablePattern<Bool>(false)
1926

27+
var errMessage: String = ""
28+
29+
let readyHourRelay = BehaviorRelay<Int>(value: 0)
30+
let readyMinuteRelay = BehaviorRelay<Int>(value: 0)
31+
let moveHourRelay = BehaviorRelay<Int>(value: 0)
32+
let moveMinuteRelay = BehaviorRelay<Int>(value: 0)
2033

2134
var readyHour = ObservablePattern<String>("")
2235
var readyMinute = ObservablePattern<String>("")
@@ -53,15 +66,6 @@ final class SetReadyInfoViewModel {
5366
self.notificationManager = notificationManager
5467
}
5568

56-
private func validTime(time: Int, range: ClosedRange<Int>, defaultValue: String) -> String {
57-
if range.contains(time) {
58-
return String(time)
59-
} else {
60-
errMessage.value = "시간은 23시간 59분까지만 입력할 수 있어요!"
61-
return defaultValue
62-
}
63-
}
64-
6569
private func calculateTimes() {
6670
let readyHours = Int(readyHour.value) ?? storedReadyHour
6771
let readyMinutes = Int(readyMinute.value) ?? storedReadyMinute
@@ -72,25 +76,6 @@ final class SetReadyInfoViewModel {
7276
moveTime = moveHours * 60 + moveMinutes
7377
}
7478

75-
func updateTime(textField: String, time: String) {
76-
guard let time = Int(time) else { return }
77-
78-
switch textField {
79-
case "readyHour":
80-
readyHour.value = validTime(time: time, range: 0...23, defaultValue: "23")
81-
case "readyMinute":
82-
readyMinute.value = validTime(time: time, range: 0...59, defaultValue: "59")
83-
case "moveHour":
84-
moveHour.value = validTime(time: time, range: 0...23, defaultValue: "23")
85-
case "moveMinute":
86-
moveMinute.value = validTime(time: time, range: 0...59, defaultValue: "59")
87-
default:
88-
break
89-
}
90-
91-
calculateTimes()
92-
}
93-
9479
func checkValid(readyHourText: String,
9580
readyMinuteText: String,
9681
moveHourText: String,
@@ -209,3 +194,76 @@ final class SetReadyInfoViewModel {
209194
}
210195
}
211196
}
197+
198+
extension SetReadyInfoViewModel: ViewModelType {
199+
struct Input {
200+
let readyHourText: Observable<String>
201+
let readyMinuteText: Observable<String>
202+
let moveHourText: Observable<String>
203+
let moveMinuteText: Observable<String>
204+
}
205+
206+
struct Output {
207+
let readyHourText: Driver<String>
208+
let readyMinuteText: Driver<String>
209+
let moveHourText: Driver<String>
210+
let moveMinuteText: Driver<String>
211+
}
212+
213+
func transform(input: Input, disposeBag: RxSwift.DisposeBag) -> Output {
214+
input.readyHourText
215+
.distinctUntilChanged()
216+
.compactMap { Int($0) }
217+
.bind(to: readyHourRelay)
218+
.disposed(by: disposeBag)
219+
220+
input.readyMinuteText
221+
.distinctUntilChanged()
222+
.compactMap { Int($0) }
223+
.bind(to: readyMinuteRelay)
224+
.disposed(by: disposeBag)
225+
226+
input.moveHourText
227+
.distinctUntilChanged()
228+
.compactMap { Int($0) }
229+
.bind(to: moveHourRelay)
230+
.disposed(by: disposeBag)
231+
232+
input.moveMinuteText
233+
.distinctUntilChanged()
234+
.compactMap { Int($0) }
235+
.bind(to: moveMinuteRelay)
236+
.disposed(by: disposeBag)
237+
238+
let readyHourText = checkValidTime(time: .hour, relay: readyHourRelay)
239+
let readyMinuteText = checkValidTime(time: .minute, relay: readyMinuteRelay)
240+
let moveHourText = checkValidTime(time: .hour, relay: moveHourRelay)
241+
let moveMinuteText = checkValidTime(time: .minute, relay: moveMinuteRelay)
242+
243+
let output = Output(
244+
readyHourText: readyHourText,
245+
readyMinuteText: readyMinuteText,
246+
moveHourText: moveHourText,
247+
moveMinuteText: moveMinuteText
248+
)
249+
250+
return output
251+
}
252+
}
253+
254+
private extension SetReadyInfoViewModel {
255+
func checkValidTime(time: Time, relay: BehaviorRelay<Int>) -> Driver<String> {
256+
let range: ClosedRange<Int> = time == .hour ? 0...23 : 0...59
257+
return relay
258+
.map { value in
259+
if range.contains(value) {
260+
return value.description
261+
} else {
262+
self.errMessage = "시간은 23시간 59분까지만 입력할 수 있어요!"
263+
print(self.errMessage)
264+
return String(range.upperBound)
265+
}
266+
}
267+
.asDriver(onErrorJustReturn: "0")
268+
}
269+
}

0 commit comments

Comments
 (0)
Please sign in to comment.