-
Notifications
You must be signed in to change notification settings - Fork 2
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
[refactor] 준비 정보 입력 뷰 Rx 리팩토링 #405
base: suyeon
Are you sure you want to change the base?
Conversation
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
rx마스터 수야미!!!!! 고생햇습니다!!!
} | ||
|
||
override func viewWillAppear(_ animated: Bool) { | ||
super.viewWillAppear(animated) | ||
|
||
navigationController?.isNavigationBarHidden = false | ||
viewWillAppearRelay.accept(()) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
👍
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
고생하셨습니다.
UI는 비교적 덜 복잡하지만, 로직 자체는 복잡함이 있기에 많은 고민을 하신 것 같습니다.
뷰모델 클래스가 가지는 변수가 많아, 이를 줄여보는 노력을 해보면 어떨까 하는 바램도 있습니다.
이 고민은 '이 변수가 클래스 내 전역변수로 존재해야 하는가? 메서드 내 지역 변수로 존재해도 괜찮지 않을까?' 하는 생각에서 말씀드려봤습니다.
클래스 내 전역변수 (클래스 내에서 정의된 프로퍼티)를 경계하는 이유는 여러 메서드에 의해 변경될 수 있기 때문에 추적이 어렵고, 유지보수가 힘들다는 단점 때문인데요. 이를 위해서 잘 고민해 보시고 코드를 수정해 나가보시는 것도 좋은 경험이 될 것 같습니다.
private func setupTextField(textField: UITextField) { | ||
let textFieldEvent = Observable.merge( | ||
textField.rx.controlEvent(.editingDidBegin).map { UIColor.maincolor.cgColor }, | ||
textField.rx.controlEvent(.editingDidEnd).map { UIColor.gray3.cgColor }, | ||
textField.rx.controlEvent(.editingDidEndOnExit).map { UIColor.gray3.cgColor } | ||
) | ||
|
||
textFieldEvent | ||
.bind { borderColor in | ||
textField.layer.borderColor = borderColor | ||
} | ||
.disposed(by: disposeBag) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
3가지의 이벤트에 대해 위와 같이 구현하여, 반복되는 코드를 줄인 것이 대단한 것 같습니다. 👍
private func showToast(_ message: String, bottomInset: CGFloat = 128) { | ||
guard let view else { return } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
guard let view else { return }
을 한 이유가 있을까요?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
사실 저 부분은 세미나 코드를 뜯어온 것이었는데 ㅎㅎ show() 함수에서 view가 호출될 때 view가 nil인 경우 런타임 에러가 발생할 수 있기 때문에 조금 더 안전한 호출을 위해 저렇게 작성했을 것으로 생각됩니다 :)
textField.keyboardType = .numberPad | ||
textField.accessibilityIdentifier = identifier | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
아래의 두 내용은 View 클래스에서 선언되어도 되지 않나 하는 생각이 있네요.
UI를 설정하는 관점에서 바라본다면 말이죠.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
해당 코드는 키보드를 숫자 패드로 설정하고, 숫자만 입력 가능하도록 제한(delegate에서 설정)하는 기능을 뷰컨에서 하나의 함수로 묶어둔 것입니다. 현재 뷰의 텍스트필드는 앱 전체에서 공통 컴포넌트로 활용하고 있는 커스텀 텍스트필드를 사용하기 때문에 추가적으로 이런 설정을 해준 것인데요. 진웅 님의 코멘트를 듣고 생각을 해보니 숫자 패드를 사용하는 새로운 커스텀 텍스트필드를 만드는 게 더 목적에 부합하다는 생각이 드네요!
output.isSucceed | ||
.drive(with: self) { owner, isSucceed in | ||
if isSucceed { | ||
owner.navigateToSetReadyCompleted() | ||
} | ||
} | ||
.disposed(by: disposeBag) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
아래와 같이 구현할 수도 있을 것 같네요.
output.isSucceed
.filter { $0 } // 성공 조건 필터링
.drive(with: self) { owner, _ in
owner.navigateToSetReadyCompleted()
}
.disposed(by: disposeBag)
.drive(with: self) { owner, err in | ||
owner.showToast(err) | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
가능하면 축약형이 아닌 error
로 표현해주세요.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
넵!
output.readyHourText | ||
.drive(with: self) { owner, text in | ||
owner.rootView.readyHourTextField.text = text | ||
} | ||
.disposed(by: disposeBag) | ||
|
||
output.readyMinuteText | ||
.drive(with: self) { owner, text in | ||
owner.rootView.readyMinuteTextField.text = text | ||
} | ||
.disposed(by: disposeBag) | ||
|
||
output.moveHourText | ||
.drive(with: self) { owner, text in | ||
owner.rootView.moveHourTextField.text = text | ||
} | ||
.disposed(by: disposeBag) | ||
|
||
output.moveMinuteText | ||
.drive(with: self) { owner, text in | ||
owner.rootView.moveMinuteTextField.text = text | ||
} | ||
.disposed(by: disposeBag) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
bind
를 이용하여 구현할 수도 있을 것 같아요.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
근데 bind
와 drive
모두 메인 스레드에서 동작하는 명령어인데 어떨 때 어떤 명령어를 사용해야 하는지 감이 잘 안잡힙니다 ㅠㅠ
정확히 어떤 상황에서 사용하는지 예시 하나만 들어주실 수 있을까요 🥹
let tapGesture = UITapGestureRecognizer() | ||
view.addGestureRecognizer(tapGesture) | ||
tapGesture.rx.event | ||
.subscribe(with: self) { owner, _ in | ||
owner.view.endEditing(true) | ||
} | ||
.disposed(by: disposeBag) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
👍
guard let readyHour = Int(readyHourRelay.value) else { return } | ||
guard let readyMinute = Int(readyMinuteRelay.value) else { return } | ||
guard let moveHour = Int(moveHourRelay.value) else { return } | ||
guard let moveMinute = Int(moveMinuteRelay.value) else { return } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
조건문은 하나로 합칠수도 있을 것 같아요.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
좋은 의견입니다!
let promiseID: Int | ||
let promiseName: String | ||
let promiseTime: String | ||
|
||
let isValid = ObservablePattern<Bool>(false) | ||
let errMessage = ObservablePattern<String>("") | ||
let isSucceedToSave = ObservablePattern<Bool>(false) | ||
|
||
|
||
var readyHour = ObservablePattern<String>("") | ||
var readyMinute = ObservablePattern<String>("") | ||
var moveHour = ObservablePattern<String>("") | ||
var moveMinute = ObservablePattern<String>("") | ||
let errMessageRelay = PublishRelay<String>() | ||
let isSucceedRelay = BehaviorRelay<Bool>(value: false) | ||
|
||
var preparationTime = ObservablePattern<Int>(0) | ||
var travelTime = ObservablePattern<Int>(0) | ||
let readyHourRelay = BehaviorRelay<String>(value: "") | ||
let readyMinuteRelay = BehaviorRelay<String>(value: "") | ||
let moveHourRelay = BehaviorRelay<String>(value: "") | ||
let moveMinuteRelay = BehaviorRelay<String>(value: "") | ||
|
||
var storedReadyHour: Int = 0 | ||
var storedReadyMinute: Int = 0 | ||
var storedMoveHour: Int = 0 | ||
var storedMoveMinute: Int = 0 | ||
|
||
let bufferTime: TimeInterval = 10 * 60 | ||
var storedReadyHour: String = "" | ||
var storedReadyMinute: String = "" | ||
var storedMoveHour: String = "" | ||
var storedMoveMinute: String = "" | ||
|
||
var readyTime: Int = 0 | ||
var moveTime: Int = 0 | ||
|
||
let bufferTime: TimeInterval = 10 * 60 |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
뷰컨트롤러가 직접적으로 접근하지 않는 변수라면 private
으로 선언해도 좋아 보여요.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
요 부분을 신경을 못 썼네요 반영하겠습니다!
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
그녀는 어디까지 발전하는가? 저도 빨리 작업해서 PR 올리겠습니다. 수야미짱복복천재만재개발자 고생했어요 ~~ 🤍
output.readyHourText | ||
.drive(with: self) { owner, text in | ||
owner.rootView.readyHourTextField.text = text | ||
} | ||
.disposed(by: disposeBag) | ||
|
||
output.readyMinuteText | ||
.drive(with: self) { owner, text in | ||
owner.rootView.readyMinuteTextField.text = text | ||
} | ||
.disposed(by: disposeBag) | ||
|
||
output.moveHourText | ||
.drive(with: self) { owner, text in | ||
owner.rootView.moveHourTextField.text = text | ||
} | ||
.disposed(by: disposeBag) | ||
|
||
output.moveMinuteText | ||
.drive(with: self) { owner, text in | ||
owner.rootView.moveMinuteTextField.text = text | ||
} | ||
.disposed(by: disposeBag) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
근데 bind
와 drive
모두 메인 스레드에서 동작하는 명령어인데 어떨 때 어떤 명령어를 사용해야 하는지 감이 잘 안잡힙니다 ㅠㅠ
정확히 어떤 상황에서 사용하는지 예시 하나만 들어주실 수 있을까요 🥹
let textFieldEvent = Observable.merge( | ||
textField.rx.controlEvent(.editingDidBegin).map { UIColor.maincolor.cgColor }, | ||
textField.rx.controlEvent(.editingDidEnd).map { UIColor.gray3.cgColor }, | ||
textField.rx.controlEvent(.editingDidEndOnExit).map { UIColor.gray3.cgColor } | ||
) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
와 진짜 깔끔하네요 ... 수야미 짱
enum Time { | ||
case hour | ||
case minute | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
enum 사용 굿!!!!!!!!!!
moveHourRelay.map { !$0.isEmpty }, | ||
moveMinuteRelay.map { !$0.isEmpty } | ||
) | ||
.map { $0 && $1 && $2 && $3 } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
이렇게도 구현할 수 있군요 ~!
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
👍
고생하셨습니다~
🔗 연결된 이슈
📄 작업 내용
👀 기타 더 이야기해볼 점
뷰는 간단한데 로직이 많아서 생각보다 깐깐하네요 ㅠㅠ Rx 이제야 좀 알겠는 듯 모르겠는 듯 ㅋㅋ