상세 컨텐츠

본문 제목

Combine은 withLatestFrom을 제공하지 않는다

iOS/Combine

by 쑤야. 2023. 12. 25. 00:25

본문

 

RxSwift의 withLatestFrom


RxSwift에서 withLatestFrom은 방출 기준이 되는 observable이 방출될 때마다 다른 observable의 최신 값을 가져와 결합한 후, observable을 방출한다.

 

 

Combine은?


Combine에서 다른 publisher들을 모아서 재방출할 수 있는 연산자로 zip, merge, combineLatest 3가지가 있는데 아쉽게도 withLatestFrom와 같은 기능을 수행하는 연산자는 없다. 

 

combineLatest가 최신 값을 가져와 방출하는 특징을 가지고 있다. 하지만 기준으로 설정한 publisher가 새로운 이벤트를 방출할 때에 대해서만 이벤트가 방출되는 것이 아니라, 기준에 상관없이 결합된 다른 publisher들이 새로운 값을 방출할 때도 새롭게 결합된 모든 이벤트를 방출하기 때문에 적합하지 않다. 

 

만약 CTA 버튼과 텍스트 필드가 있으며, CTA 버튼의 탭 이벤트와 텍스트 필드의 텍스트 변화 값 이벤트가 combineLatest로 결합되어 있다면, CTA 버튼이 비활성화 상태일지라도 텍스트 필드의 값 변화로 인해 API가 요청되는 문제가 발생할 것이다. 

 

Combine에서의 대안


진행하고 있는 프로젝트에서 withLatestFrom의 연산자가 필요했었다. 

 

상황은 아래와 같으며, 3번 단계에서 withLatestFrom 사용을 생각했었다. 

  1.  회원가입 화면이며, 사용자로부터 닉네임, 생년월일, 성별 정보를 입력받는다.
  2. 이때 모든 정보를 입력하고, 각 정보는 조건을 충족해야 CTA 버튼이 활성화된다.
  3. CTA 버튼이 활성화된 이후 버튼을 탭 하면, 최신 입력값들로 Request 모델을 구성해 회원가입 API를 요청한다. 

탭을 했을 때 방출되는 Publisher를 기준으로 하고, 입력값들을 withLatestFrom으로 이미 방출된 최신 값을 가져오는 로직이다. 하지만 withLatestFrom이 없으니, 다른 방법을 찾아야 했다.

 

publisher를 결합하여 최신 값을 끌어오는 것은 불가능하므로 publisher가 아닌, 최신 데이터 자체를 전역 변수로 관리하며 끌어와야 겠다라고 생각했다.

 

 

1. 각 입력값 마다 전역변수 프로퍼티를 선언하여 각각 관리

  • 클래스에 회원 정보 개수만큼 프로퍼티가 늘어난다. 
  • 각각의 값을 다른 위치에서 따로 관리해줘야 하기 때문에 잘못 관리할 위험성도 있다.

2. 회원가입 유스케이스 요청 모델 사용

  • 이미 선언되어 있는 타입을 활용해, 효율성이 높다. 
  • 하나의 구조체 안에서 데이터들을 전부 관리할 수 있다. 

3. View Model에서 입력값들을 관리하기 위한 구조체를 새롭게 생성

  • 2번과 마찬가지로 하나의 구조체 안에서 데이터들을 전부 관리할 수 있다.
  • 2번과 달리 새로운 구조체를 하나 더 만들어서 관리해야 하기 때문에, 입력 정보가 추가되거나 제거될 경우 2개의 구조체를 수정해야 하는 문제점이 있다.

 

 

2번 방식이 재사용 측면과 효율성 측면에서 가장 적합하다고 판단했다.


 

2번 방식으로 프로젝트에 적용한 코드이다. 

canMove.combineLatest(input.nicknameEditingEnd, input.birthdayEditingEnd, input.gender)
    .sink{ [weak self] canMove, nickname, birth, gender in
        if canMove {
            guard let memberId = UserManager.shared.memberId else { return }
            self?.signUpRequestValue = SignUpUseCaseRequestValue(
                memberId: memberId,
                nickname: nickname,
                birth: birth,
                gender: gender
            )
        }
        else {
            self?.signUpRequestValue = nil
        }
    }
    .store(in: &cancellable)

CTA 버튼의 활성화 여부(canMove)와 회원 정보들을 결합한 Publisher를 생성했다. 

회원 정보들이 변화할 때마다, signUpRequestValue는 업데이트된다. 

 

input.moveNext
    .compactMap{ [weak self] _ in
        self?.signUpRequestValue
    }
    .flatMap{ request in
        self.signUpUseCase.execute(request: request)
    }
    .
    .
    .

CTA 버튼의 탭 이벤트를 알려주는 moveNext가 방출될 때, 전역 변수로 저장하고 있었던  signUpRequestValue를 가져온다. 

'iOS > Combine' 카테고리의 다른 글

throttle  (0) 2024.02.29
RunLoop.main과 DispatchQueue.main  (0) 2023.12.13
Combine과 함께 NotificationCenter 사용해보기  (0) 2023.12.10

관련글 더보기