Skip to content

Conversation

@kimscastle
Copy link
Contributor

@kimscastle kimscastle commented Nov 16, 2023

[#164] REFACTOR : Auth관련 VC들 MVVM(Combine)-C으로 변경 (#164)

🌱 작업한 내용

1. 페어코딩으로 Auth관련 뷰들 리팩터링 완료(2023.11.14~2023.11.16)

라이온하트 MVVM 원칙

1. ViewController의 Input과 Output의 Failure타입은 모두 Never여야한다.

  • ViewController는 String만 받아서 toast에 띄우기만한다 → 에러는 catch operator를 통해서 Publisher.Just에 에러메시지만 담아서ViewController에 던진다.
    (ViewController에서의 stream은 성공이든 에러든 어떤 형태의 completion도 떠서는 안된다. completion을 받게 되면 stream이 끊어지기 때문)

2. ViewController에서 ViewModel은 init에서는 some으로 받는다

  • 최대한 컴파일타임에서 타입추론이 가능하도록 opaque type을 사용한다

일반적으로 성능, 안전성 및 신뢰성을 위해 "any"(런타임)보다 "some"(컴파일 타임)을 사용하는 것을 선호해야 합니다.

3. Factory에서의 VM과 VC는 Protocol타입으로 반환한다

  • 객체타입이 아닌 추상화된 interface를 바라보게 되어 객체간의 결합도를 낮추고 필요한 메서드만 호출할수있도록 합니다.

4. delegate를 combine으로 대체합니다

  • delegate pattern대신 publisher를 활용해 데이터를 상위 객체에게 넘겨줍니다
하위 객체 GetPregnancyVC

1. Data를 상위객체로 보낼 publisher를 선언합니다
var pregnancyIsValid = PassthroughSubject<(pregnancy: Int, isValid: Bool), Never>()

2. 하위객체에서 특정 값을 publisher에 send해줍니다
func bindInput() {
    pregnancyTextfield.textPublisher
        .sink { [weak self] in self?.pregancyTextfieldDidChanged.send($0) }
        .store(in: &cancelBag)
}
상위 객체 OnboardingVC

let pregnancyViewController = GetPregnancyViewController(viewModel: GetPregnancyViewModelImpl())

1. 상위 객체에서 하위객체의 publisher를 subscribe해서 그값을 상위객체의 publisher에 send해줍니다
func setChildViewController() {
  pregnancyViewController.pregnancyIsValid
      .sink { [weak self] in self?.pregenacy.send($0) }
      .store(in: &cancelBag)
}

5. VM에서 Coordinator관련 stream을 따로만들어서 flow stream을 관리합니다

  • SplashVC의 경우에 Network결과에 따라서 navigation flow가 변경됩니다
  • 아래 예시코드와 같이 navigator를 호출해야하는 경우 navigationSubject에 필요한 타입을 넘겨줍니다
let splashErrorMessage = input.lottiePlayFinished
    .map { [weak self] _ -> (String?, String?) in
            ...생략
            self?.navigationSubject.send(.expired)
    }

func reissueToken(refreshToken: String, accessToken: String) async throws {
    ...생략...
    self.navigationSubject.send(.valid)
}
  • 최종적으로 해당 navigationSubject를 VM내부에서 subscribe하여 navgation flow를 실행해줍니다
navigationSubject
    .receive(on: RunLoop.main)
    .sink { state in
        switch state {
        case .expired:
            self.navigator.checkToken(state: .expired)
        case .valid:
            self.navigator.checkToken(state: .valid)
        }
    }
    .store(in: &cancelBag)

🔥이유🔥

이렇게 결정한 이유는 특정 네트워크 결과에따라 main thread에서 실행되어야할 UI관련 코드가 background thread에서 동작하게 되어 문제가 발생할수있기에 매번 네트워킹 코드 내부에서 navigation호출을 DispatchQueue.main.asnyc{}를 해줬어야했습니다. 하지만 navigationSubject자체를 main thread에서 receive하게되면 navgation관련 flow는 main에서 실행됨을 보장받을수있게됩니다.

6. errorMessage를 보내는 Publisher는 Merge Operator를 활용해서 VC가 하나의 스트림만 바라보도록한다.

  • VM에서 두개의 Publisher가 error string을 보내는 경우 VC가 두개의 publisher를 sink하더라도 한번에 하나씩의 error를 띄우기때문에 merge를 활용해서 VC가 하나의 publisher로 부터 error message를 받도록 합니다.
ViewModel✅
func transform(input: SplashViewModelInput) -> SplashViewModelOutput {
  let mergeSubject = splashErrorMessage
      .merge(with: logoutSubject)
      .eraseToAnyPublisher()
  return SplashViewModelOutput(splashNetworkErrorMessage: mergeSubject)
}
ViewController✅
func bind() {
    ...생략...
    /// error를 하나의 stream에서 받는 구조
    output.splashNetworkErrorMessage
        .sink { errorMessage in
            print(errorMessage)
        }
        .store(in: &cancelBag)
}

📮 관련 이슈

@ffalswo2 ffalswo2 added this to the 🦁2차 Refactor🦁 milestone Nov 16, 2023
@kimscastle kimscastle requested review from cchanmi and ffalswo2 and removed request for cchanmi and ffalswo2 November 16, 2023 06:35
@kimscastle kimscastle assigned ffalswo2 and cchanmi and unassigned ffalswo2 and cchanmi Nov 16, 2023
@kimscastle kimscastle added 🦁민재 민재's 🦁의성 의성's 🦁찬미 찬미's ♻️Refactoring 리펙터링 labels Nov 16, 2023
@kimscastle kimscastle changed the title Refactor/#164 [REFACTOR] Auth관련 VC들 MVVM-C(Combine)으로 변경 (#164) Nov 16, 2023
@kimscastle kimscastle merged commit e620896 into main Nov 16, 2023
@kimscastle kimscastle changed the title [REFACTOR] Auth관련 VC들 MVVM-C(Combine)으로 변경 (#164) [REFACTOR] Auth관련 VC들 MVVM(Combine)-C으로 변경 (#164) Nov 16, 2023
@kimscastle kimscastle deleted the refactor/#164 branch November 19, 2023 02:02
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

♻️Refactoring 리펙터링 🦁민재 민재's 🦁의성 의성's 🦁찬미 찬미's

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[REFACTOR] Auth관련 ViewController들 MVVM(Combine)-C 리팩터링

4 participants