[DISCUSSION]combine으로 비동기처리를 할때 loadingview 관리를 하는 방법에 대한 의견 #60
-
현재 온보딩에서 닉네임을 서버에 등록하는 비동기처리 과정에서 loadingview의 active상태를 publish해야하는 상황입니다 제가 생각한 방식은 두가지 입니다 첫번째 방식첫번째방식은 버튼을 눌렀을때 비동기처리의 결과는 void로 반환하지만 내부적으로 loadingstate를 받고 보내주는 passthroughsubject에 start와 end를 보내고 그 subject를 vc에서 받아서 state마다 loadingview의 animation을 켜고 끄고 해주는 방식입니다 final class OnboardingViewModelImpl: OnboardingViewModel, NicknameCheck {
enum OnboardingFlowType {
case backButtonTapped, signInButtonTapped
}
...생략...
var navigationSubject = PassthroughSubject<OnboardingFlowType, Never>()
let signInStatePublisher = PassthroughSubject<LoadingState, Never>()
var cancelBag = Set<AnyCancellable>()
...생략...
func transform(input: OnboardingInput) -> OnboardingOutput {
...생략...
// 이 부분 입니다
input.singInButtonTapped
.flatMap { nickname -> AnyPublisher<Void, Never> in
return Future<Void, Error> { promise in
Task {
do {
self.signInStatePublisher.send(.start)
try await Task.sleep(nanoseconds: 100_000_000_0)
try await self.nickNameManager.registerUser(nickName: nickname)
self.signInStatePublisher.send(.end)
self.navigationSubject.send(.signInButtonTapped)
promise(.success(()))
} catch {
promise(.failure(error))
}
}
}
.catch { _ in
Just(())
}
.eraseToAnyPublisher()
}
.eraseToAnyPublisher()
.sink {}
.store(in: &cancelBag)
return OnboardingOutput(nickNameResultPublisher: nickNameResultPublisher, signInStatePublisher: signInStatePublisher)
}
}
두번째 방식두번째 방식은 애초에 비동기처리의 결과로 loadingview state를 publish해주는 방식입니다
func bindInput() {
...생략...
self.signInButton.tapPublisher
.sink { [weak self] in
guard let text = self?.nickNameTextField.text else { return }
// 버튼을 누르면 loadingview 시작
self?.loadingView.startAnimating()
self?.singInButtonTapped.send(text)
self?.signInButton.isUserInteractionEnabled = false
}
.store(in: &cancelBag)
}
그리고 future쪽 로직이 아래와 같이 변경됩니다 let signInStatePublisher = input.singInButtonTapped
.flatMap { nickname -> AnyPublisher<LoadingState, Never> in
return Future<LoadingState, Error> { promise in
Task {
do {
try await Task.sleep(nanoseconds: 100_000_000_0)
try await self.nickNameManager.registerUser(nickName: nickname)
self.navigationSubject.send(.signInButtonTapped)
promise(.success(.end))
} catch {
promise(.failure(error))
}
}
}
.catch { _ in
Just(.error)
}
.eraseToAnyPublisher()
}
.eraseToAnyPublisher()
두 방식이 제가 생각한 loadingview에대한 state 처리 하는 방식인데 둘다 똑같이 동작을합니다 그래서 더좋은방식이나 혹은 두 방식중 어떤 방식이 더 나아보이는지 궁금해서 discussion을 만들어봤습니다 |
Beta Was this translation helpful? Give feedback.
Replies: 1 comment 2 replies
-
저는 후자의 방식이 조금 더 맞다고 생각됩니다.
저는 이게 loadingView의 로직이 나뉘어졌다고 생각이 들진 않고 오히려 객체가 각자의 책임에 충실하게 해야할 일을 하는 것 같이 느껴집니다. 전자의 경우 loadingView를 start, hide하는 UI업데이트까지의 역할을 수행하고 있어 오히려 ViewController의 책임을 침범하는 느낌이 드는 것 같습니다. |
Beta Was this translation helpful? Give feedback.
저는 후자의 방식이 조금 더 맞다고 생각됩니다.
loadingView
도 결국UIView
이고,loadingView
를 start시키고 hide시키는건 UI 업데이트하는 쪽에 속한다고 생각됩니다. 따라서 UI를 단순히 업데이트 하는 로직(start, hide) 자체는 UI와 UI로직의 책임을 가지고 있는ViewController
에 있는 것이 더 자연스럽고 있어도 이상하지 않다고 생각합니다. 그리고ViewController
에게 start시킬지 hide시킬지를 명령하는 주체는 결국ViewModel
이기에 해당loadingView
의 state를 따로 저장하진 않지만, loadingView의 state또한 ViewModel이 결정한다고 봐도 무방할 것 같습니다.저는 이게 loadingView의 로직이 나뉘어졌다고 생각이 들진 않고 오히려 객체가 각자의 책임에 충실하게 해야할 일을 하는 것 같이 느껴집니다.
전자의 경우 loadingView를 start, hide하는 UI업데이트까지의 역할을 수행하고 있어 오히려 ViewController의 책임을 침범하는 느낌이 드는 것 같습니다.