ย Publishers
Publishers์ ์ญํ ๊ณผ ์ค์์ฑ
Publishers๋ Combine ํ๋ ์์ํฌ์ ํต์ฌ ๊ฐ๋
์ค ํ๋๋ก, ๋ฐ์ดํฐ ์คํธ๋ฆผ์ ์์ฑํ๊ณ ์ด๋ฒคํธ๋ฅผ ๋ฐฉ์ถํ๋ ์ญํ ์ ํ๋ค. ๊ตฌ๋
์(Subscriber)๋ ์ด๋ฌํ Publishers์ ๊ตฌ๋
ํ์ฌ ๋ฐ์ดํฐ ์คํธ๋ฆผ์ ๋ฐ์ ์ฒ๋ฆฌํ ์ ์๋ค.
ํน์ง
โข
ํ์
์์
โฆ
Publishers๋ ๋ฐฉ์ถํ๋ ๊ฐ์ ํ์
๊ณผ ์ค๋ฅ ํ์
์ ๋ช
์ํ๋ค. ์ด๋ฅผ ํตํด ์ปดํ์ผ ํ์์ ํ์
์ฒดํฌ๋ฅผ ํ ์ ์์ด, ๋ฐํ์ ์ค๋ฅ์ ๊ฐ๋ฅ์ฑ์ ์ค์ธ๋ค.
โข
์กฐํฉ ๊ฐ๋ฅ
โฆ
์ฌ๋ฌ Publishers๋ฅผ ์กฐํฉํ๊ณ ๋ณํํ๋ Operator๋ฅผ ํตํด ๋ณต์กํ ๋ฐ์ดํฐ ์ฒ๋ฆฌ ๋ก์ง์ ๊ตฌํํ ์ ์๋ค.
ย Publishers
Publishers
โข
protocol Publisher
โฆ
Publisher ํ๋กํ ์ฝ์ ๊ฐ์ด๋ ์ด๋ฒคํธ๋ฅผ ์์ฑํ๊ณ , ๊ตฌ๋
์์๊ฒ ๋ฐฉ์ถํ๋ ๊ฐ์ฒด๊ฐ ์ค์ํด์ผ ํ๋ ๊ท์ฝ์ด๋ค
protocol Publisher<Output, Failure>
// Output : Publisher๊ฐ ๋ฐฉ์ถํ ๊ฐ์ ํ์
// Failure : Publisher๊ฐ ๋ฐฉ์ถํ ์ ์๋ ์ค๋ฅ์ ํ์
. ์ค๋ฅ๊ฐ ์์ ๊ฒฝ์ฐ 'Never' ํ์
์ ์ฌ์ฉํ๋ค.
Swift
๋ณต์ฌ
โฆ
๋ชฉ์ : ๋ชจ๋ Publisher ํ์
์ ๊ธฐ๋ณธ์ด ๋๋ ํ๋กํ ์ฝ, ์ด ํ๋กํ ์ฝ์ ์ค์ํ๋ ํ์
์ ๊ฐ์ ๋ฐํํ๊ณ , Combine์ ๋ค๋ฅธ ํ์
๋ค์ด ๊ตฌ๋
ํ ์ ์๋ค.
โฆ
์ฃผ์ ๋ฉ์๋ : subscribe(_:) : ๊ตฌ๋
์๋ฅผ ๋ฐ์๋ค์ธ๋ค.
โข
enum Publishers
enum Publishers
Swift
๋ณต์ฌ
โฆ
๋ชฉ์ : ๋ค์ํ ์ข
๋ฅ์ Publisher๋ฅผ ์์ฑํ๋ factory ๋ฉ์๋๋ฅผ ํฌํจํ๊ณ ์๋ ๋ค์์คํ์ด์ค ์ญํ ์ ํ๋ค. ์๋ฅผ๋ค์ด Publishers.Sequence ๋ ์ํ์ค์ ์์๋ฅผ ๋ฐํํ๋ Publisher๋ฅผ ์์ฑํ๋ค.
โข
struct AnyPublisher
@frozen
struct AnyPublisher<Output, Failure> where Failure: Error
Swift
๋ณต์ฌ
โฆ
๋ชฉ์ : ํ์
์๊ฑฐ๋ฅผ ์ฌ์ฉํ์ฌ Publisher์ ํ์
์ ๋ณด๋ฅผ ์จ๊ธฐ๋ฉด์. ์ด๋ค Publisher์๋ ํธํ๋ ์ ์๋ ์ผ๋ฐ์ ์ธ Publisher๋ฅผ ๋ง๋ ๋ค. ์ด๋ฅผ ํตํด ๊ตฌ์ฒด์ ์ธ Publisher ํ์
์ ๋
ธ์ถํ์ง ์๊ณ ๋ ์ธํฐํ์ด์ค๋ฅผ ์ ๊ณตํ ์ ์๋ค.
โข
struct Published
@propertyWrapper
struct Published<Value>
Swift
๋ณต์ฌ
โฆ
๋ชฉ์ : ํ๋กํผํฐ๋ฅผ Observable ๊ฐ์ฒด๋ก ๋ณํํ์ฌ, ํ๋กํผํฐ์ ๊ฐ์ด ๋ณ๊ฒฝ๋ ๋๋ง๋ค ์๋ฆผ์ ๋ฐ์ ์ ์๊ฒ ํ๋ค.
โฆ
์ฌ์ฉ๋ฒ : @Published ํ๋กํผํฐ ๋ํผ๋ฅผ ์ฌ์ฉํ์ฌ, ๊ฐ์ ๋ณ๊ฒฝ์ ๊ตฌ๋
ํ ์ ์๋ค.
์์ )
class Weather {
@Published var temperature: Double
init(temperature: Double) {
self.temperature = temperature
}
}
let weather = Weather(temperature: 20)
cancellable = weather.$temperature
.sink() {
print("Temperature now: \($0)")
}
weather.temperature = 25
// Prints:
// Temperature now: 20.0
// Temperature now: 25.0
Swift
๋ณต์ฌ
ํ๋กํผํฐ๊ฐ ๋ณ๊ฒฝ๋๋ฉด ํ๋กํผํฐ์ willSet ๋ธ๋ก์์ Publisheing์ด ๋ฐ์ํ๋ฏ๋ก Subsciber๋ ํ๋กํผํฐ์ ์ค์ ๋ก ์ค์ ๋๊ธฐ ์ ์ ์ ๊ฐ์ ๋ฐ๊ฒ ๋๋ค.
โข
protocol Cancellable
protocoll Cancellable
Swift
๋ณต์ฌ
โฆ
๋ชฉ์ : ์์
์ ์ทจ์ํ ์ ์๋ ๊ฐ์ฒด๋ฅผ ์ํ ํ๋กํ ์ฝ์ด๋ค. ๊ตฌ๋
์ ์ทจ์ํ ๋ ์ฌ์ฉ!
โฆ
์ฃผ์ ๋ฉ์๋ : cancel() : ํ์ฌ ์ํ ์ค์ธ ์์
์ ์ทจ์ํ๋ค.
โข
class AnyCancellable
final class AnyCancellable
Swift
๋ณต์ฌ
โฆ
๋ชฉ์ : Cancellable ํ๋กํ ์ฝ์ ์ค์ํ๋ ํ์
์๊ฑฐ ํด๋์ค๋ก, ๊ตฌ๋
์ ์ทจ์ํ ๋ ์ฌ์ฉ๋๋ค.
โฆ
AnyCancellable ์ธ์คํด์ค๊ฐ ํด์ ๋ ๋ ์๋์ผ๋ก cancel() ๋ฉ์๋๋ฅผ ํธ์ถํ๋ค.
์์ )
import Combine
// Observable ํด๋์ค ์ ์
class MyModel {
@Published var score = 0
}
// Observable ๊ฐ์ฒด ์์ฑ
let model = MyModel()
// ๊ตฌ๋
์์ฑ ๋ฐ ๊ฐ ๋ณํ์ ๋ฐ๋ฅธ ์ฒ๋ฆฌ
var cancellables = Set<AnyCancellable>()
model.$score
.sink { newScore in
print("Score updated: \(newScore)")
}
.store(in: &cancellables)
// ๊ฐ ๋ณ๊ฒฝ
model.score = 10
Swift
๋ณต์ฌ
โMyModelโ ํด๋์ค์ @Published ๋ก ์ ์ธ๋ score ํ๋กํผํฐ๊ฐ ์๋ค.
model.$score.sink ๋ฅผ ํตํด score ํ๋กํผํฐ์ ๋ณํ๋ฅผ ๊ตฌ๋
ํ๊ณ , ๊ฐ์ด ๋ณ๊ฒฝ๋ ๋๋ง๋ค ์ฝ์์ ์ถ๋ ฅํ๋ค.
AnyCancellable ์ธ์คํด์ค๋ cancellables ์ธํธ์ ์ ์ฅ๋์ด, ๊ตฌ๋
์ด ํ์ ์์ด์ง๋ฉด ์๋์ผ๋ก ์ทจ์๋๋ค.
ย Convenience Publishers
โข
class Future
โฆ
๋จ์ผ ๊ฐ์ ์์ฑํ ๋ค์ finishes ๋๋ fails๋ฅผ ๋ณด๋ด์ฃผ๋ Publisher
โฆ
๋น๋๊ธฐ ์์
์ ๊ฒฐ๊ณผ๋ฅผ ๋ฐฉ์ถํ๋ Single-shot Publisher
final class Future<Output, Failure> where Failure: Error
Swift
๋ณต์ฌ
โฆ
๋ชฉ์ : ํ๋์ ์์
๊ฒฐ๊ณผ๋ฅผ ๋ํ๋ด๋ Publisher์ด๋ค. ๋น๋๊ธฐ ์์
์ ์ํํ๊ณ , ๊ทธ ๊ฒฐ๊ณผ๋ฅผ ๋์ค์ ๋ฐํํ๋ค.
โฆ
ํน์ง : ์์
์ด ์๋ฃ๋ ๋ ๋จ ํ๋ฒ๋ง ๊ฒฐ๊ณผ๋ฅผ ๋ฐํํ๋ค.
์์ ) Future ๋ฅผ ์ฌ์ฉํ์ฌ ์ด๋ค ์์
์ ์ํํ ํ ๋จ์ผ ์์๋ฅผ ๋น๋๊ธฐ์ ์ผ๋ก ๊ฒ์ํ๋ ๋ฐฉ๋ฒ
โฆ
Future ๋ ์ฑ๊ณต ๋๋ ์คํจ๋ฅผ ๋ํ๋ด๋ Result ๋ก ํ๋ผ๋ฏธ์ค๋ฅผ ํธ์ถํ๋ ํด๋ก์ ์ ํจ๊ป ์ด๊ธฐํ๋๋ค. ์ฑ๊ณตํ ๊ฒฝ์ฐ, Future ์ ํ๋ฅ ๊ตฌ๋
์๋ ๊ฒ์ ์คํธ๋ฆผ์ด ์ ์์ ์ผ๋ก ์๋ฃ๋๊ธฐ ์ ์ ์์๋ฅผ ๋ฐ๋๋ค. ๊ฒฐ๊ณผ๊ฐ ์ค๋ฅ์ธ ๊ฒฝ์ฐ, ๊ฒ์๋ ํด๋น ์ค๋ฅ๋ก ์ข
๋ฃ๋๋ค.
// ์งง์ ์ง์ฐ ํ์ ๋๋ค ์ซ์๋ฅผ ๋น๋๊ธฐ์ ์ผ๋ก ๊ฒ์ํ๋ 'future'๋ฅผ ์ฌ์ฉํ๋ ๋ฐฉ๋ฒ
func generateAsyncRandomNumberFromFuture() -> Future<Int, Never> {
return Future() { promise in
DispatchQueue.main.asyncAfter(deadline: .now() + 2) {
let number = Int.random(in: 1...10)
promise(.success(number))
}
}
}
// async-await ๋ฒ์
func generateAsyncRandomNumberFromContinuation() async -> Int {
return await withCheckedContinuation { continuation in
DispatchQueue.main.asyncAfter(deadline: .now() + 2) {
let number = Int.random(in 1...10)
continuation.resume(returning: number)
}
}
}
Swift
๋ณต์ฌ
๊ฒ์๋ ๊ฐ์ ๋ฐ๊ธฐ ์ํด, Subscriber.Sink Combine ๊ตฌ๋
์๋ฅผ ์ฌ์ฉ!
cancellable = generateAsyncRandomNumberFromFuture()
.sink { number in print("Got random number \(number).") }
Swift
๋ณต์ฌ
async-await ๊ตฌ๋ฌธ๊ณผ ํตํฉํ๊ธฐ ์ํด, Future ๋ ๋๊ธฐํ๋ ํธ์ถ์์๊ฒ ๊ฐ์ ์ ๊ณตํ ์ ์๋ค. ์ด๋ Future ๊ฐ ๋ค๋ฅธ Publisher ์ ๋ฌ๋ฆฌ ๋จ์ผ ์์๋ง์ ๊ฒ์ํ๋ค๋ ์ ์์ ํนํ ์ ์ฉํ๋ค. value ์์ฑ์ ์ฌ์ฉํ๋ฉด ์์ ํธ์ถ ํฌ์ธํธ๋ ๋ค์๊ณผ ๊ฐ์ด ๋ณด์ธ๋ค.
// future ์ฌ์ฉ ๋ฒ์
let number = await generateAsyncRandomNumberFromFuture().value
print("Got random number \(number).")
// async-await ๋ฒ์
let asyncRandom = await generateAsyncRandomNumberFromContinuation()
Swift
๋ณต์ฌ
์์ 2)
import Combine
var cancellables = Set<AnyCancellable>()
let futurePublisher = Future<String, Error> { promise in
DispatchQueue.global().asyncAfter(deadline: .now() + 2) {
promise(.success("Future ๊ฒฐ๊ณผ"))
}
}
futurePublisher
.sink(receiveCompletion: { completion in
switch completion {
case .finished:
print("์๋ฃ")
case .failure(let error):
print("์๋ฌ ๋ฐ์: \(error)")
}
}, receiveValue: { value in
print("๋ฐ์ ๊ฐ: \(value)")
})
.store(in: &cancellables)
Swift
๋ณต์ฌ
โข
struct Just
โฆ
๊ตฌ๋
์๋ง๋ค ๋จ ํ ๋ฒ์ฉ ์ถ๋ ฅ์ ๋ฐฉ์ถํ ํ ์ข
๋ฃ๋๋ ๋ฐํ์
struct Just<Output>
Swift
๋ณต์ฌ
โฆ
๋ชฉ์ : ๋จ์ผ ๊ฐ์ ๋ฐํํ๊ณ ์๋ฃ๋๋ Publisher. ์ด๊ธฐํํ ๋ ์ ๊ณต๋ ๊ฐ์ ๊ตฌ๋
์์๊ฒ ์ฆ์ ๋ฐํํ๋ค.
โฆ
ํน์ง : ํ
์คํธ๋ ๋จ์ํ ๊ฐ ๋ณํ์ ์ ์ฉํ๋ค.
โข
struct Defferred
struct Defferred<DeferredPublisher> where DeferredPublisher: Publisher
Swift
๋ณต์ฌ
โฆ
๋ชฉ์ : ๊ตฌ๋
์ด ๋ฐ์ํ ๋๊น์ง Publisher ์์ฑ์ ์ง์ฐ์ํค๋ Publisher ์ด๋ค. ์ด๋ฅผ ํตํด ๊ตฌ๋
์๋ง๋ค ์๋ก์ด Publisher ์ธ์คํด์ค๋ฅผ ์์ฑํ ์ ์๋ค.
โฆ
ํน์ง : ์ด๊ธฐํ ๋น์ฉ์ด ํฐ Publisher๋ฅผ ์ฌ์ฉํ ๋ ์ ์ฉํ๋ค.
โข
struct Empty
struct Empty<Output, Failure> where Failure: Error
Swift
๋ณต์ฌ
โฆ
๋ชฉ์ : ์๋ฌด ๊ฐ๋ ๋ฐํํ์ง ์๊ณ ์ฆ์ ์๋ฃ๋๊ฑฐ๋, ์๋ฃ๋์ง ์๋ Publisher
โฆ
ํน์ง : ํ
์คํธ๋ ๊ธฐ๋ณธ๊ฐ ์์ด ๋น ์ํ๋ฅผ ๋ํ๋ผ ๋ ์ฌ์ฉ๋๋ค.
โข
struct Fail
struct Fail<Output, Failure> where Failure: Error
Swift
๋ณต์ฌ
โฆ
๋ชฉ์ : ๊ตฌ๋
์งํ์ ์คํจ๋ฅผ ๋ฐํํ๋ Publisher์ด๋ค. ์๋ฌ ์ฒ๋ฆฌ๋ฅผ ํ
์คํธํ๊ฑฐ๋ ๊ตฌํํ ๋ ์ฌ์ฉ๋๋ค.
โฆ
ํน์ง : ํน์ ํ ์๋ฌ ํ์
์ ๋ฐํํ๋ค.
โข
struct Record
struct Record<Output, Failure> where Failure: Error
Swift
๋ณต์ฌ
โฆ
๋ชฉ์ : ๋ฏธ๋ฆฌ ์ ์๋ ๊ฐ์ ์์๋๋ก ๋ฐํํ๊ณ , ์๋ฃ ์ด๋ฒคํธ๋ฅผ ๋ณด๋ด๋ Publisher์ด๋ค. ํ
์คํธ ๋ชฉ์ ์ผ๋ก ์ฌ์ฉ๋๋ค.
โฆ
ํน์ง : ๊ณ ์ ๋ ๋ฐ์ดํฐ ์คํธ๋ฆผ์ ์ฌ์์ฐํ๋ค.
โข
protocol ConnectablePublisher
protocol ConnectablePublisher<Output, Failure>: Publisher
Swift
๋ณต์ฌ
โฆ
๋ชฉ์ : ๊ตฌ๋
์๊ฐ ๊ตฌ๋
ํ๊ธฐ ์ ๊น์ง ๋ฐ์ดํฐ ๋ฐํ์ ์์ํ์ง ์๋ Publisher๋ฅผ ์ํ ํ๋กํ ์ฝ์ด๋ค. connect() ๋ฉ์๋๋ฅผ ํธ์ถํด์ผ ๋ฐ์ดํฐ ๋ฐํ์ ์์ํ๋ค.
โฆ
ํน์ง : ์ฌ๋ฌ ๊ตฌ๋
์๊ฐ ๋์์ ๋์ผํ ๋ฐ์ดํฐ ์ํ์ค๋ฅผ ์์ ํ ์ ์๊ฒ ํด์ค๋ค.
โ ์ฃผ๋ก ๋ฉํฐ์บ์คํ
(multicasting) ์ํฉ์์ ์ ์ฉํ๊ฒ ์ฌ์ฉ๋๋ค.
์์ ) Just, Empty, Fail
import Combine
// Just ์์
let justPublisher = Just("Just ๋ฐํ ๊ฐ")
justPublisher
.sink(receiveValue: { print($0) })
// Just๋ ๋จ์ผ ๊ฐ ๋ฐํ์ ์ฌ์ฉ!
// Empty ์์
let emptyPublisher = Empty<String, Never>()
emptyPublisher
.sink(receiveCompletion: { print($0) }, receiveValue: { print($0) })
let failPublisher = Fail<String, Error>(error: NSError(domain: "TestError", code: -1, userInfo: nil))
failPublisher
.sink(receiveCompletion: { completion in
switch completion {
case .finished:
print("์๋ฃ")
case .failure(let error):
print("์๋ฌ ๋ฐ์: \(error.localizedDescription)")
}
}, receiveValue: { value in
print(value)
})
// Empty์ Fail์ ํน์ ์ํฉ(๊ฐ์ด ์๊ฑฐ๋ ์๋ฌ ๋ฐ์)์ ์๋ฎฌ๋ ์ด์
ํ ๋ ์ฌ์ฉ๋๋ค.
Swift
๋ณต์ฌ
โข
Publishers.Sequence
โฆ
์ด ํผ๋ธ๋ฆฌ์
๋ ์ํ์ค(๋ฐฐ์ด, ๋ฒ์ ๋ฑ)์ ์
๋ ฅ์ผ๋ก ๋ฐ์, ์ํ์ค์ ๊ฐ ์์๋ฅผ ์์ฐจ์ ์ผ๋ก ๋ฐฉ์ถํ๋ค. ์ด๋ฅผ ์ฌ์ฉํ๋ฉด ์ฃผ์ด์ง ์ํ์ค์ ๋ํด ๋ฐ์ํ ํ๋ก๊ทธ๋๋ฐ์ ์ ์ฉํ ์ ์๋ค.
import Combine
// ๋ฐฐ์ด ์ฌ์ฉ ์
let numbers = [1, 2, 3, 4, 5]
let numbersPublisher = Publisher.Sequence(sequence: numbers)
// ๊ตฌ๋
์์ฑ ๋ฐ ๊ฐ ์ฒ๋ฆฌ
let subscription = numbersPublisher
.sink(
receiveCompletion: { completion in
switch completion {
case .finished:
print("Completed")
case .failure(let error):
print("Error: \(error)")
}
},
receiveValue: { value in
print("Received value: \(value)")
}
)
// ๋ฒ์ ์ฌ์ฉ ์
let rangePublisher = Publishers.Sequence(sequence: 1...5)
// ๊ตฌ๋
์์ฑ ๋ฐ ๊ฐ ์ฒ๋ฆฌ
let rangeSubscription = rangePublisher
.sink(
receiveCompletion: { completion in
print("Range Completed")
},
receiveValue: { value in
print("Range Received Value: \(value)")
}
)
Swift
๋ณต์ฌ
ย ๋ฐ์ดํฐ ์คํธ๋ฆผ ์์
๋ง์ฐฌ๊ฐ์ง๋ก Combine์ ์ฌ์ฉํ๋ฉด ์ด๋ฒคํธ๋ ๋ฐ์ดํฐ ์คํธ๋ฆผ์ ์ ์ธ์ ์ผ๋ก ์กฐ์ํ๊ณ ๋ณํํ๋ ๋ค์ํ ์ฐ์ฐ์(Operator)๋ฅผ ์ฌ์ฉํ ์ ์๋ค.
๋ณํ ์ฐ์ฐ์
โข
map
โฆ
map์ ์คํธ๋ฆผ์ ํฌํจ๋ ๊ฐ ๊ฐ์ ๋ํด ํจ์๋ฅผ ์ ์ฉํ๊ณ ๊ทธ ๊ฒฐ๊ณผ๋ฅผ ์๋ก์ด ๊ฐ์ผ๋ก ๋ฐฉ์ถํ๋ค. ์ด๋ฅผ ํตํด ์
๋ ฅ ๋ฐ์ดํฐ๋ฅผ ์ํ๋ ํํ๋ก ๋ณํํ ์ ์๋ค.
let publisher = [1, 2, 3, 4 ,5].publisher
publisher
.map { $0 * 2 }
.sink { print("\($0)", terminator: " ") }
// 2 4 6 8 10
Swift
๋ณต์ฌ
โข
flatMap
โฆ
flatMap์ ๊ฐ ์
๋ ฅ ๊ฐ์ ๋ํด ์๋ก์ด ํผ๋ธ๋ฆฌ์
๋ฅผ ์์ฑํ๊ณ , ์์ฑ๋ ๋ชจ๋ ํผ๋ธ๋ฆฌ์
์ ์ถ๋ ฅ์ ๋จ์ผ ํผ๋ธ๋ฆฌ์
๋ก ๋ณํฉํ๋ค. ์ด๊ฒ์ ๋น๋๊ธฐ ์์
์ ์ฒด์ธ์ผ๋ก ์ฐ๊ฒฐํ ๋ ํนํ ์ ์ฉํ๋ค.
ํํฐ๋ง ์ฐ์ฐ์
โข
filter
โฆ
filter๋ ์ฃผ์ด์ง ์กฐ๊ฑด์ ๋ง๋ ๊ฐ๋ง์ ๋ฐฉ์ถํ๋ค. ์กฐ๊ฑด์ ๋ง์ง ์๋ ๊ฐ์ ๋ฌด์
publisher
.filter { $0 % 2 == 0 }
.sink { print("\($0)", terminator: " ") }
// 2 4
Swift
๋ณต์ฌ