기본 차이
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
}
}
마치며
역시나 너무나도 간단한 설명이었지만 읽어주셔서 감사합니다.
'TIL' 카테고리의 다른 글
[TIL] MapKit 사용해서 위치 자동완성 검색 기능 구현 (0) | 2024.05.11 |
---|---|
[TIL] Alamofire 로깅(Logging) (0) | 2024.05.09 |
[TIL] Swift Concurrency 등장 배경 (0) | 2024.05.08 |
[TIL] Alamofire을 이용한 Token 갱신 적용 (0) | 2024.05.04 |
[TIL] TikTok 피드 구현 과정 (0) | 2024.04.12 |