GRAIN ์ Combine ํด์ผ๋ฉ๋๋ค! ๋ผ๊ณ ๋น๋นํ๊ฒ ์ธ์ณค์ผ๋ ๊ฐ๋ณด์๊ณ ~~ ! !
์ค์ ์ ๊ฝ ! ๐ธ Combine ์ ๋ํด ๊ณต๋ถํ๋ ๋ถ๋ถ์ ๊ณต์ ํ๋ ค๊ณ ํด์!
์ง๊ธ๋ถํฐ Combine ์ธ๋ฏธ๋ 1ํ ์์ํฉ๋๋น ๐๐ป
ํ .. ๊ทธ๋ผ ๋จผ์ ์ปด๋ฐ์ธ์ด ๋๋์ฒด ๋ฉ๊น!? ์ฐ๋จน๋ถํฐ ํด๋ณผ๊น์ ?
๊ทธ๋๊ทธ๋ ๊ทธ๋์ ์ปด๋ฐ์ธ์ด ๋ฌด์์ธ๊ณ ..! ํด๋ณด๋
https://developer.apple.com/documentation/combine
Combine | Apple Developer Documentation
Customize handling of asynchronous events by combining event-processing operators.
developer.apple.com
The Combine framework provides a declarative Swift API for processing values over time. These values can represent many kinds of asynchronous events.
ํด์ํด๋ณด๋ฉด, ์ปด๋ฐ์ธ์ ์ผ๋จ framework ์ด๊ณ ,
์๊ฐ์ ๋ฐ๋ฅธ ๊ฐ์ ์ฒ๋ฆฌํ๊ธฐ ์ํ Swift API๋ฅผ ์ ๊ณตํ๋ค๊ณ ํฉ๋๋ค.
์ด๋ฌํ ๊ฐ๋ค์ ๋ง์ ์ข ๋ฅ์ ๋น๋๊ธฐ ์ด๋ฒคํธ๋ฅผ ๋ํ๋ผ์ ์๋ค๊ณ ํ๋ค์!
why combine?
ํ ์คํค ! ๊ทผ๋ฐ.. ๊ทธ๋์ .. ์! Combine์ด ํ์ํ๊ฐ? ๋ผ๊ณ ๋๋ผ์ค ์ ์๋๋ฐ์
๊ธฐ์กด์ Swift ์ธ์ด์ ๋จ์ ์ค ํ๋๋ ๋น๋๊ธฐ ์ฒ๋ฆฌ๊ฐ ๊ต์ฅํ,,,, ๋ญ ๊ฐ์์ต๋๋ค ! ใ ใ
๊ธฐ์กด์ ๋น๋๊ธฐ์ฒ๋ฆฌ๋ฐฉ์์๋ DispatchQueue์ ์ ๊ทผํ๊ฑฐ๋, completionHandler๋ฅผ ์ ๋ฌ๋ฐ์ ํด๋น ์์ ์ด ์๋ฃ๋์์๋ ํธ์ถํ๋ ๋ฐฉ์์ ์ฌ์ฉํ์๋๋ฐ, ๊ฒฐ๋ก ์ ์ผ๋ก ๋น๋๊ธฐ์ฒ๋ฆฌ ์ฝ๋๊ฐ ๋๋ ๋ณต์กํ๋ค ์ด๊ฑฐ์ฃ !
๊ทธ๋์ ์ปด๋ฐ์ธ์ ์ฌ์ฉํด ์ด๋ฒคํธ ์ฒ๋ฆฌ ์ฝ๋์ ์ค์ฒฉ ํด๋ก์ ธ๋ ์ฝ๋ฐฑํจ์ ์ ๊ฐ์ ๊น๋ค๋ก์ด(์ง์ ๋ถ์ฐํ) ๊ธฐ์ ์ ์ ๊ฑฐํจ์ผ๋ก์จ ์ฝ๋๋ฅผ ์ ์ง๋ณด์ํ๊ธฐ ํธํ๊ฒ ๋ง๋ค์ด ์ฃผ๋ ค๊ณ ์ ํ์ด ์ ๊ธฐ์ ์ ๋ด๋์๊ฑฐ์ฃ .
์ด๊ฒ๋ง ๋ด์๋ ๊ฐ์ด ์์ค์คํ ๋ฐ์! Combine์ ๋ฐฑ๋ฌธ์ด ๋ถ์ฌ์ผํ ์ ๋๋ค.
์ณ๋ณด๊ธฐ ์ ์๋ ์ด๊ฒ ๋ญ์ง ? ์ฝ๊ฒ ๊ฐ์ด ์์กํ๋๋ผ๊ตฌ์.
Combine Overview
๋จผ์ Combine์์ ๊ฐ์ฅ ์ค์ํ Publisher์ Subscriber ๊ฐ๋ ๋ค์ ์์๋ณผ๊ฑฐ์์!
- Publisher: ์์ฐ์ ๋ผ๊ณ ์๊ฐํ๋ฉด ๋ ๊ฒ๊ฐ์์!
- Subscriber: ๊ตฌ๋ ์ ๋ผ๊ณ ์๊ฐํ๋ฉด ๋ ๊ฒ๊ฐ์์!
๋จผ์ Publisher์ Subscriber ์ ๊ด๊ณ๋ถํฐ ์ค๋ช ๋๋ฆฌ์๋ฉด,
Publisher ์ Subscriber์ ๊ด๊ณ๋ ์ ํ๋ธ ํฌ๋ฆฌ์์ดํฐ์ ๊ตฌ๋ ์๋ก ๋น์ ํ ์ ์์ต๋๋ค.
๊ตฌ๋ ์๋ ํฌ๋ฆฌ์์ดํฐ๋ฅผ ๊ตฌ๋ ์ ํด์ผ์ง๋ง ํด๋น ํฌ๋ฆฌ์์ดํฐ๊ฐ ์์์ ๋ฐ์์ ๋ณผ์ ์์ฃ
์ด ๊ฐ๋ ์ด๋ ๋๊ฐ์ต๋๋ค!
Publisher(ํฌ๋ฆฌ์์ดํฐ) ๊ฐ ๋ฐ์ดํฐ(์์)๋ฅผ ๊ฐ๊ณต(Operator)ํด์ ๋ฐฐ์ถํ๋ฉด Subscriber(๊ตฌ๋ ์) ๊ฐ ๊ทธ ๋ฐ์ดํฐ๋ฅผ ๋ฐ๋๊ฑฐ์ฃ
Publisher ?
๊ทธ๋ผ Publisher ๊ฐ๋ ์ ๋ํด ์์๋ณผ๊ฒ์
Publisher | Apple Developer Documentation
Declares that a type can transmit a sequence of values over time.
developer.apple.com

public protocol Publisher<Output, Failure> {
associatedtype Output
associatedtype Failure : Error
func receive<S>(subscriber: S) where S : Subscriber, Self.Failure == S.Failure, Self.Output == S.Input
}
๋จผ์ ์ฝ๊ฒ ๋งํ๋ฉด Publisher๋ ๋ฐ์ดํฐ๋ฅผ ๋ฐฐ์ถ ๐ฉ ํ๋ ์น๊ตฌ์ ๋๋ค.
Publisher protocol์ ์ดํด๋ณด๋ฉด
- Output ๊ณผ Failure (์๋ฌ ํ์ ) ์ ์ ์ํฉ๋๋ค.
- Subscriber ๊ฐ ์์ฒญํ ๊ฒ ๋งํผ ๋ฐ์ดํฐ๋ฅผ ์ ๊ณตํฉ๋๋ค.
Subscriber ?
Subscriber ๊ฐ๋ ์ ๋ํด ์์๋ณผ๊ฒ์
Subscriber | Apple Developer Documentation
Subscriber | Apple Developer Documentation
A protocol that declares a type that can receive input from a publisher.
developer.apple.com

public protocol Subscriber<Input, Failure> : CustomCombineIdentifierConvertible {
associatedtype Input
associatedtype Failure : Error
func receive(subscription: Subscription)
func receive(_ input: Self.Input) -> Subscribers.Demand
func receive(completion: Subscribers.Completion<Self.Failure>)
}
Subscriber ๋ Publisher์๊ฒ ๋ฐ์ดํฐ๋ฅผ ์์ฒญํ๋ ์นญ๊ตฌ์ ๋๋ค.
Publisher protocol์ ์ดํด๋ณด๋ฉด
- Input๊ณผ Failture(์๋ฌํ์ ) ํ์ ์ ์ ์ํด์ผํฉ๋๋ค.
- Publisher ๊ตฌ๋ ํ , ๊ฐฏ์๋ฅผ ์์ฒญํฉ๋๋ค.
- ํ์ดํ๋ผ์ธ์ ์ทจ์ํ ์ ์์ต๋๋ค.
Subscriber & Publisher Pattern

๊ทธ๋ผ ๋นํธ์ธ Publisher์ Subscriber ์ข ๋ฅ์ ์ฌ์ฉ๋ฐฉ๋ฒ์ ๋ํด ์์๋ณผ๊ฒ์!
๋จผ์ Publisher์๋ Just ์ Future ๋ผ๋ ๋นํธ์ธ Publisher๊ฐ ์กด์ฌํฉ๋๋ค.
- Just: ๋จ์ผ ๊ฐ์ ๋ค๋ฃจ๋ Publisher ์ ๋๋ค.
- Future: ์์ง ๋ฐ์ํ์ง ์์ ๋จ์ผ ๊ฐ์ ๋น๋๊ธฐ์ ์ผ๋ก ๋ฐฉ์ถํ๋, Function์ ๋ค๋ฃจ๋ Publisher์ ๋๋ค.
Subscriber ์๋ assign ๊ณผ sink ๊ฐ ์กด์ฌํฉ๋๋ค .
- assign: Subscriber์ ๊ฐ์ ํน์ ๊ฐ์ฒด์ ํ๋กํผํฐ์ ํ ๋นํ๋๋ฐ ์ฌ์ฉํฉ๋๋ค.
- sink: Publisher๊ฐ ์ ๊ณตํ ๋ฐ์ดํฐ๋ฅผ ๋ฐ์ ์ ์๋ ํด๋ก์ ธ๋ฅผ ์ ๊ณตํฉ๋๋ค.
๊ทธ๋ผ ํ๋ฒ ์ฌ์ฉํด๋ณผ๊ฒ์!
- Publisher
// Just (Publisher)
let justPublisher = Just("hello wonder")
let subscription = justPublisher.sink { value in
print(value) // "hello wonder"
}
// Future (Publisher)
func performAsyncTask() -> Future<String, Error> {
return Future<String, Error> { promise in
// ๋น๋๊ธฐ ์์
์ํ
// ์์
์ด ์๋ฃ๋๋ฉด ๊ฒฐ๊ณผ๋ฅผ promise์ ๋ฐฉ์ถ
if success {
promise(.success("์์
์ํ ์๋ฃ"))
} else {
promise(.failure(SomeError))
}
}
}
let futurePublisher = performAsyncTask()
let subscription = futurePublisher.sink { completion in
switch completion {
case .finished:
print("์์
์ํ ์๋ฃ")
case .failure(let error):
print("์ค๋ฅ ๋ฐ์: \\(error)")
}
} receiveValue: { value in
print(value)
}
- Subscriber
import Combine
// assign (Subscriber)
class ViewModel {
@Published var count: Int = 0 // UI ์์์ ๋ฐ์ธ๋ฉํ ํ๋กํผํฐ
}
let viewModel = ViewModel()
let publisher = Just(100)
// assign ๋ฉ์๋๋ ํน์ KeyPath ๋ฅผ ์ฌ์ฉํ์ฌ ๊ฐ์ ํ ๋นํฉ๋๋ค.
let subscription = publisher.assign(to: \.count, on: viewModel)
print(viewModel.count) // 100
// sink (Subscriber)
let publisher = Just("hello wonder")
let subscription = publisher.sink { value in
print(value) // "hello wonder"
}
Subscription?
Subscription | Apple Developer Documentation
Subscription | Apple Developer Documentation
A protocol representing the connection of a subscriber to a publisher.
developer.apple.com

์ด Subscription์ด ๋ฌด์์ด๋! Publisher(์ ํ๋ฒ) ๊ฐ Subscriber(๊ตฌ๋ ์) ์๊ฒ
๊ตฌ๋ ์ ํ์ผ๋ ๋ด ์์์ ์ด์ ๋ณด์ฌ์ค๊ฒ! ๋ผ๊ณ ์ธ์ฆํด์ฃผ๋ ์ธ์ฆ์ ์ด๋ผ๊ณ ๋ณด์๋ฉด ๋ฉ๋๋ค.
์ฆ Subscriber ์ Publisher๊ฐ ์ฐ๊ฒฐ์ด ๋์์์ ๋ํ๋ด๋ ์น๊ตฌ์ด์ฃ .
์ด Subscription ์ ์ ์๋ฅผ ๋ณด๋ฉด ,
Cancellable ํ๋กํ ์ฝ์ ๋ฐ๋ฅด๊ณ ์๋๊ฑฐ ๋ณด์ด์๋์?
์ด Cancellable ํ๋กํ ์ฝ์ ๊ตฌ๋ ์ ์ทจ์ํ ์ ์๋ ํ์ ์ ๋ํ๋ด๋ ํ๋กํ ์ฝ์ ๋๋ค.
- Cancellable ํ๋กํ ์ฝ์ ์ฑํํ ํ์ ์ cancel() ๋ฉ์๋๋ฅผ ๊ตฌํํด์ ๊ตฌ๋ ์ ์ทจ์ํ ์ ์์ต๋๋ค.
๊ทธ๋ผ cancel() ๋ฉ์๋๋ฅผ ์ฌ์ฉํ๋ ์ด์ ๋ ๋ฌด์์ผ๊น์ ?
๋ฐ๋ก ๋ฉ๋ชจ๋ฆฌ ๋์๋ฅผ ๋ฐฉ์งํ๊ณ ๋ถํ์ํ ๋ฆฌ์์ค ๋ญ๋น๋ฅผ ๋ง๊ธฐ ์ํด์๊ฒ ์ฃ !!!
์ผ์์ ์ธ ๊ตฌ๋ ์ด๋ ํ์ฌ ํ์ํ์ง ์์ ๊ตฌ๋ ์ ๊ณ์ ์ ์งํ๊ฒ ๋๋ฉด ์ฑ์ ์ฑ๋ฅ์๋ ์ํฅ์ ์ค ์ ์์ด์!
๋ฐ๋ผ์ ๊ตฌ๋ ์ด ๋์ด์ ํ์ํ์ง ์์๋๋ cancel() ๋ฉ์๋๋ฅผ ํธ์ถํด์ ๊ตฌ๋ ์ ์ทจ์ํ๊ณ , ๋ฆฌ์์ค๋ฅผ ํด์ ํ๋ ๊ฒ์ด ์ข์ต๋๋ค.
(์ผ๋ฐ์ ์ผ๋ก deinit ๋ฉ์๋๋, ์ฑ์์ ๋์ด์ ๊ตฌ๋ ์ด ํ์ํ์ง ์๋ ํ์ด๋ฐ์ ๊ตฌ๋ ์ ์ทจ์ํ ์ ์์ต๋๋ค. )
var cancellable: AnyCancellable?
cancellable = publisher
.sink { value in
// ๊ฐ ์ฒ๋ฆฌ
}
// ๊ตฌ๋
์ทจ์
cancellable?.cancel()
SwiftUI ์ฝ๋ ๋ก๋ ํ๋ฒ ์ ์ฉ์์ผ ๋ณผ๊ฒ์!
import Combine
import SwiftUI
class ViewModel: ObservableObject {
@Published var count: Int = 0
private var cancellables = Set<AnyCancellable>()
func setupSubscriptions() {
$count
.sink { value in
print("New count: \\(value)")
}
.store(in: &cancellables) // cancel ๋ ์ ์๋ ๊ตฌ๋
๋ค์ ์ ์ฅํฉ๋๋ค.
}
deinit {
cancellables.forEach { $0.cancel() } // ์ธ์คํด์ค ๋ฉ๋ชจ๋ฆฌ์์ ํด์ฒด๋ ๋ ํธ์ถ
}
}
struct ContentView: View {
@StateObject private var viewModel = ViewModel()
var body: some View {
VStack {
Text("Count: \\(viewModel.count)")
Button("Increment") {
viewModel.count += 1
}
}
.onAppear {
viewModel.setupSubscriptions()
}
}
}
์ฝ๋๋ก ๋ณด๋๊น ๋ ์ดํด๊ฐ ๊ฐ๋๊ฒ ๊ฐ์ง ์๋์??? ใ ใ ใ ใ
์ฌ๊ธฐ๊น์ง Publisher์ Subscriber์ ๊ฐ๋ ๊ณผ ๊ธฐ๋ณธ์ ์ธ ์ฌ์ฉ๋ฒ? ์ ์์๋ดค์ต๋๋ค ๐
๋ Publisher ์ Subscriber์ ์ฐ๊ฒฐ์ ๋ํ๋ด๋ ๊ฐ์ฒด์ธ Subscription ๋ ํ๋ฒ ์์๋ดค์ต๋๋ค! ๐
๋ค์ ํ์๋ ๋ฐ๋ก Subject ์ ๋๋ค ๋๋ฅ ๊ทธ๋ผ ๋ค์ ์๊ฐ์ ๋ง๋์ฉ
์๋จ๋ฝ ~~~๐ ๐ค
์ฐธ๊ณ
- ํจ์คํธ ์บ ํผ์ค ์์ด๋ณด๋ฅผ ์ํ, ํ ๋ฒ์ ๋๋ด๋ iOS ์ฑ ๊ฐ๋ฐ ๋ฐ์ด๋ธ
'ํ๋ก์ ํธ ๐ ๏ธ > iOS' ์นดํ ๊ณ ๋ฆฌ์ ๋ค๋ฅธ ๊ธ
| URLSession + Combine ์กฐํฉ์ผ๋ก ๋คํธ์ํฌ ๋ ์ด์ด ๋ง๋ค๊ธฐ (0) | 2023.06.10 |
|---|---|
| Combine(3): Operator (0) | 2023.06.10 |
| Combine(2): Subject (0) | 2023.06.10 |
| @StateObject @ObserverdObject ๊ทธ๋์ ๋ญ์ ๋ค๋ฅธ๋ (0) | 2023.04.06 |
| Firebase Storage ์ asnyc/await ๋ฅผ ์ฌ์ฉํด์ ๋ค์ค์ด๋ฏธ์ง ์ฒ๋ฆฌํ๊ธฐ (0) | 2023.04.06 |