본문 바로가기

TIL

[TIL] Combine VS RxSwift

기본 차이

Combine과 RxSwift에서 사용되는 개념들을 각각 대응시켜 보겠습니다.

우선 RxSwift에서는 Observable - Observer - Operator 가 주요 구성 요소들인 반면

Combine에서는 Publisher - Subscriber - Operator 가 주요 구성 요소들입니다.

 

그리고 RxSwift에서 구독하기 위해 사용되는 subscribe는 Combine에서 sink에 해당합니다.

더욱이 RxSwift에서 메모리를 해제하기 위해 사용하는 dispose는 Combine에서 store에 해당합니다.

 

이제 Subject로 넘어가보겠습니다.

RxSwift에서 주로 사용하는 Subject에는 PublishSubject와 BehaviorSubject가 있는데요.

이 두 Subject는 Combine에서 각각 PassthroughSubject와 CurrentValueSubject에 대응됩니다.

 

아래 표는 위 내용들을 표로 정리한 것입니다.

RxSwift Combine
Observable - Observer - Operator Publisher - Subscriber - Operator
subscribe sink
dispose store
PublishSubject PassthroughSubject
BehaviorSubject CurrentValueSubject

 

실제 사용법

이제 실제 코드로 구현해보겠습니다.

 

아래 코드는 ViewModelType 프로토콜을 정의하고 이를 채택하여 Input/Output 패턴으로 구성한 ViewModel입니다.

(전체적인 코드 흐름은 RxSwift와 비슷하므로 자세한 설명은 생략하도록 하겠습니다.)

import Foundation
import Combine

// CoinCombineViewModel.swift
final class CoinCombineViewModel: ViewModelType {

    // var bag = DisposeBag()
    var cancellables = Set<AnyCancellable>()
    
    var input = Input()
    
    @Published var output = Output()

    init() {
        transform()
    }
}

extension CoinCombineViewModel {
    struct Input {
    	// Error를 따로 처리해주어야 하는 RxSwift와 달리 Combine은 성공/실패 케이스에 대해 함께 정의할 수 있습니다.
        var viewOnAppear = PassthroughSubject<Void, Never>()
    }
    
    struct Output {
        var markets: Markets = []
    }
    
    func transform() {
        input.viewOnAppear
            .sink { [weak self] _ in
                guard let self else { return }
                
                Task {
                    try? await self.fetchMarket()
                }
            }
            .store(in: &cancellables)
    }
    
    func fetchMarket() async throws {
        do {
            output.markets = try await Network.shared.requestUpbitAPI()
        } catch {
            output.markets = []
        }
    }
}

// ViewModelType.swift
protocol ViewModelType: AnyObject, ObservableObject {
    associatedtype Input
    associatedtype Output
    
    var cancellables: Set<AnyCancellable> { get set }
    
    var input: Input { get set }
    var output: Output { get set }
    
    func transform()
}

// Network.swift
final class Network {
    static let shared = Network()
    
    private init() {}
    
    func requestUpbitAPI() async throws -> [Market] {
        let url = URL(string: "https://api.upbit.com/v1/market/all")!
        
        let (data, response) = try await URLSession.shared.data(from: url)
        
        guard let response = response as? HTTPURLResponse else {
            throw NetworkError.invalidResponse
        }
        
        guard (200..<300) ~= response.statusCode else {
            throw NetworkError.statusCodeError
        }
        
        let result = try JSONDecoder().decode(Markets.self, from: data)
        
        return result
    }
}

 

마치며

역시나 너무나도 간단한 설명이었지만 읽어주셔서 감사합니다.