Publisher и Subscriber — это фундаментальные компоненты фреймворка Combine, представляющие собой реализацию паттерна "Наблюдатель" (Observer) для реактивного программирования в Swift.
.finished
или .failure(Error)
)import Combine
// 1. Создаем Publisher
let publisher = Just("Hello, Combine!") // Издатель, который emits одно значение
// 2. Создаем Subscriber
let subscriber = Subscribers.Sink<String, Never>(
receiveCompletion: { completion in
switch completion {
case .finished:
print("Получено завершение")
case .failure(let error):
print("Ошибка: \(error)")
}
},
receiveValue: { value in
print("Получено значение: \(value)")
}
)
// 3. Связываем Publisher и Subscriber
publisher.subscribe(subscriber)
Подписка (Subscription):
subscribe(_:)
у Publisherreceive(subscription:)
Запрос данных:
request(_:)
на SubscriptionОтправка данных:
receive(_:)
receive(completion:)
Отмена (Cancellation):
cancel()
let just = Just(42) // Издает одно значение и завершается
let future = Future<String, Error> { promise in
// Асинхронная операция
promise(.success("Результат"))
}
let subject = PassthroughSubject<String, Never>()
subject.send("Привет") // Ручная отправка значений
let currentValue = CurrentValueSubject<Int, Never>(0)
print(currentValue.value) // 0 - хранит текущее значение
[1, 2, 3].publisher
.sink(
receiveCompletion: { print($0) },
receiveValue: { print($0) }
)
class MyClass {
var value: String = "" {
didSet { print(value) }
}
}
let obj = MyClass()
["A", "B", "C"].publisher
.assign(to: \.value, on: obj)
struct MySubscriber: Subscriber {
typealias Input = Int
typealias Failure = Never
func receive(subscription: Subscription) {
subscription.request(.max(2)) // Запрашиваем 2 значения
}
func receive(_ input: Int) -> Subscribers.Demand {
print("Получено: \(input)")
return .none // Не изменяем спрос
}
func receive(completion: Subscribers.Completion<Never>) {
print("Завершено")
}
}
Между Publisher и Subscriber можно вставлять операторы для трансформации:
[1, 2, 3, 4, 5].publisher
.filter { $0 % 2 == 0 } // Только четные
.map { $0 * 2 } // Умножаем на 2
.sink { print($0) } // Выводим: 4, 8
Важно хранить подписки, иначе они немедленно отменяются:
var cancellables = Set<AnyCancellable>()
func setupSubscription() {
NotificationCenter.default.publisher(for: UIApplication.didBecomeActiveNotification)
.sink { _ in print("App became active") }
.store(in: &cancellables) // Сохраняем подписку
}
textField.publisher(for: .editingChanged)
.map { $0.text ?? "" }
.debounce(for: .seconds(0.5), scheduler: RunLoop.main)
.sink { [weak self] in self?.search($0) }
.store(in: &cancellables)
Publishers.CombineLatest(
fetchUserProfile(),
fetchUserSettings()
)
.sink { profile, settings in
// Обновляем UI
}
.store(in: &cancellables)
@Published var isLoading: Bool = false
func loadData() {
isLoading = true
networkService.fetchData()
.receive(on: DispatchQueue.main)
.sink { [weak self] _ in
self?.isLoading = false
} receiveValue: { [weak self] data in
self?.updateUI(with: data)
}
.store(in: &cancellables)
}
Забывают хранить подписки:
Не учитывают поток выполнения:
URLSession.shared.dataTaskPublisher(for: url)
.sink { /* Обработка в фоновом потоке! */ }
Используют неправильные операторы:
map
с flatMap
merge
и combineLatest
Publisher/Subscriber в Combine — это мощная абстракция для:
Ключевые моменты:
Практические преимущества:
Для эффективного использования:
Publisher/Subscriber — это фундамент современного реактивного подхода в iOS разработке.