본문 바로가기

Combine

[SwiftUI/Combine] RxSwift 토이 프로젝트 Combine으로 작업 후 비교 분석

반응형

SwiftUI, Combine을 학습하면서 개념은 이해가 가지만 어떤 식으로 사용하면 좋을지 감이 잡히지 않았습니다.

 

Combine은 RxSwift와 유사한 부분이 많기에, 난이도가 낮은 RxSwift를 사용하여 작업했던 토이 프로젝트를

SwiftUI + Combine으로 변경해보면 어느 정도 사용 방법을 터득하고, 이해할 수 있을 거 같아 시작하게 되었습니다!


2020/07/15 - [RxSwift] - [RxSwift] 토이 프로젝트를 통해 알아보는 RxSwift x MVVM

 

[RxSwift] 토이 프로젝트를 통해 알아보는 RxSwift x MVVM

위와 같은 토이 프로젝트를 만들어 보면서, RxSwift 활용과 MVVM을 적용하는 방법에 대해서 다루고자 합니다. 이 토이 프로젝트는 실제 네트워크 통신을 하지 않았습니다, 대신 request시 global Queue에

ntomios.tistory.com

 

따라서 이 글은 RxSwift의 이해와 Combine의 대략적인 개념을 알아야 합니다. ^^;;

 

그리고 SwiftUI Combine + MVVM으로 변경한 토이 프로젝트 깃 주소는 글 하단에 첨부하겠습니다.

 

자세한 기능 설명은 위에 첨부한 이전 포스팅 자료를 참고하시면 됩니다.

여기서는 위 토이 프로젝트와 차이점 위주로 설명하겠습니다.

 

Request

이 토이 프로젝트는 실제 네트워크 통신을 하지 않았습니다,
대신 request시 global Queue에서 작업을 진행하고 완료되면 main Queue로 변환하여 결과 데이터를 넘겨주도록 진행함으로써 네트워크 통신과 유사한 환경을 만들고자 했습니다.

RxSwift

Combine

 

Future는 단일 값 혹은 error를 발행하고 종료(finished) 된다는 점에서, RxSwift Single과 유사합니다.

하지만, RxSwift에서 Observable, Single 등은 ColdObservable로 구독 시, 이벤트를 생성하여 발행하는 반면

Future의 경우, 생성과 동시에 이벤트를 발행합니다.

따라서 구독 시점까지 기다려주는 Deferred로 감싸서, RxSwift의 ColdObservable로 만들 수 있습니다.

 

끝으로 RxSwift의 asObservable과 유사한 기능을 담당하는 eraseToAnyPublisher()를 사용하여, 

Deferred<Future<_, _>> 타입을 AnyPublisher 타입으로 캐스팅할 수 있습니다.

 

 

ViewModel

 

RxSwift

Combine

 

 

Input 관련 프로퍼티들을 먼저 살펴보겠습니다.

 

CurrentValueSubject는 RxSwift의 BehaviorSubject,

PassthroughSubject는 RxSwift의 PublishSubject와 대응됩니다.

 

언제 바뀐지는 모르겠지만, 기존 CurrentValueSubject는 최근 데이터와 관계없이, 구독 시, 초기화 시 주입한 데이터를 발행했는데,
지금은 BehaviorSubject와 동일하게 구독 시, 가장 최근 값을 제공하는 것으로 변경되었다.

 

따라서 초기값이 존재하는 페이지와 필터는 CurrentValueSubject

탭 이벤트가 발생해야 존재하는(초기값이 없는) 구매, 좋아요 탭은 PassthroughSubject로 지정했습니다.

 

Output 관련 프로퍼티들을 살펴보기 앞서, ViewModel이 채택한 ObservableObject protocol과 관련된 내용을 먼저 살펴보겠습니다.

 

protocol ObservableObject

  • 클래스 프로토콜 ObservableObject를 클래스에 채택하면 해당 클래스의 인스턴스를 관찰하고 있다가 값이 변경되면 뷰를 업데이트합니다.

@Published (ObservableObject를 채택한 객체 내에서 사용)

  • ObservableObject에서 속성을 선언할 때 사용하는 PropertyWrapper입니다.
    @Published로 선언된 속성이 ObservableObject에 포함되어 있다면 해당 속성이 업데이트될 때마다 뷰를 업데이트합니다.


@ObservedObject (View에서 사용)

  • ObservableObject를 구독하고 값이 업데이트될 때마다 뷰를 갱신하는 PropertyWrapper입니다.

위 내용에 따라 data리스트와, 좋아요 여부, alert 노출 여부들은 View 갱신과 관련 있으므로, 

items, like, showingPurchageAlert, showingLikeAlert 프로퍼티들을 @Published property wrapper 타입으로 지정합니다.

 

다음으로 bind를 살펴보겠습니다.

 

Combine의 assign은 RxSwift의 bind,

Combine의 sink는 RxSwift의 subscribe와 대응됩니다.

 

RxSwift에서 구독할 경우, Disposable을 리턴하며 이를 disposeBag을 통해 관리했습니다.

Combine의 경우에도 비슷합니다.

Disposable 대신 유사한 개념인 Cancellable를 리턴하며, 이를 Set<AnyCancellable> 타입에 저장(store)하여 DisposeBag처럼 관리할 수 있습니다.

 

Cancellable과 Disposable의 차이점은

Disposable을 deinit이 되더라도, dispose() 처리를 하지 않았다면 구독이 종료되지 않습니다.

반면 Cancellable은 deinit 시, 구독을 종료시키는 차이점이 있습니다.

 

그리고 RxSwift의 DisposeBag는 deinit시, 저장되어 있는 disposable을 모두 dispose 시키기 때문에
Combine의 Set<AnyCancellable>과 동일하게 동작합니다.

 

대부분의 Operator는 RxSwift로 구현된 코드와 비교해서 봤을 때, 명칭의 차이가 있을 뿐 어느 정도 매칭이 됩니다.

 

removeDuplicates - distinctUntilChanged

dropFirst - skip(1)

sink - subscribe

map + switchToLatest - flatMapLatest

 

RxSwift, Combine의 Operator를 비교해놓은 링크
github.com/CombineCommunity/rxswift-to-combine-cheatsheet
 

CombineCommunity/rxswift-to-combine-cheatsheet

RxSwift to Apple’s Combine Cheat Sheet. Contribute to CombineCommunity/rxswift-to-combine-cheatsheet development by creating an account on GitHub.

github.com

 

View

RxSwift + UIKit (일부 코드)

 

Combine + SwiftUI (전체 코드)

여기서 눈여겨볼 것은 Combine과 SwiftUI를 사용할 경우 bindOutput 같은 코드가 없다는 것입니다.

이런 코드가 필요 없는 이유는 ContentView는 viewModel을 구독하고 있으며, 각 @Published 프로퍼티의 값이 업데이트된다면, 뷰를 갱신하기 때문입니다.

 

결론

SwiftUI+Combine을 사용하여 불필요한 사용구를 없앨 수 있고, first-party(built-in) framework로 만날 수 있게 되어서 정말 좋습니다.

UI 구성도 정말 간편해졌다고 생각하고, 빨리 실전에서 사용하고 싶은 마음입니다.

 

github.com/NohEunTae/SwiftUICombineMVVMSample

 

NohEunTae/SwiftUICombineMVVMSample

Contribute to NohEunTae/SwiftUICombineMVVMSample development by creating an account on GitHub.

github.com

반응형

'Combine' 카테고리의 다른 글

[RxSwift/Combine] RxSwift Disposable vs Combine AnyCancellable  (1) 2021.01.25