Observer — ключові поняття патерну підписки на зміни

Автор: IT Sectr Опубліковано: 2026-02-17 Час читання: 7 хв

Observer — поведінковий патерн, в якому один об'єкт (видавець) сповіщає багатьох підписників про зміни свого стану. У мобільній розробці Observer лежить в основі реактивних механізмів: UI підписується на зміни даних та автоматично оновлюється. Патерн реалізовано в NotificationCenter на iOS та LiveData/Flow на Android. Детальніше — на Refactoring Guru: Observer.

Головне

  • Observer — патерн підписки: один видавець, багато підписників
  • NotificationCenter — вбудована реалізація Observer в iOS/macOS
  • Flow та LiveData — реактивні реалізації Observer в Android
  • Push vs Pull — видавець може надсилати дані або сповіщати про події
  • Витоки пам'яті — підписники повинні відписуватися для запобігання витокам

Що таке Observer: суть патерну спостерігач

Observer — поведінковий патерн GoF, що визначає залежність «один до багатьох» між об'єктами. Коли один об'єкт (Subject або Observable) змінює стан, всі залежні об'єкти (Observers) автоматично сповіщаються та оновлюються. Патерн реалізує слабку зв'язаність: видавець не знає конкретних класів підписників — лише те, що вони реалізують інтерфейс Observer.

Структура Observer включає інтерфейс Subject з методами attach(), detach(), notify() та інтерфейс Observer з методом update(). ConcreteSubject зберігає стан та список підписників. ConcreteObserver реалізує update() та реагує на зміни. У мобільній розробці класична реалізація GoF зустрічається рідко — її замінюють вбудовані механізми: NotificationCenter, Combine, Flow, LiveData, які реалізують ту ж ідею з сучасним API.

Модель Push vs Pull — у Push-моделі Subject надсилає дані всім підписникам (NotificationCenter.post). У Pull-моделі Subject лише сповіщає, а підписник сам запитує дані. Android LiveData використовує Push (дані передаються в observe()), RxJava/Flow підтримують обидві моделі. Вибір залежить від завдання: Push простіший для UI-оновлень, Pull ефективніший для великих обсягів даних, які підписник може не захотіти отримувати.

Observer в iOS: NotificationCenter, Combine та KVO

NotificationCenter — вбудований механізм iOS/macOS для реалізації Observer. Видавець надсилає Notification через NotificationCenter.default.post(name:, object:, userInfo:). Підписник реєструється через addObserver(forName:, queue:, using:). NotificationCenter підтримує іменовані сповіщення (Notification.Name) та може передавати будь-які дані в userInfo. UIKeyboardWillShowNotification, UIApplicationDidEnterBackgroundNotification — системні приклади.

swift
extension Notification.Name {
    static let userDidLogin = Notification.Name("userDidLogin")
}

// Видавець
NotificationCenter.default.post(
    name: .userDidLogin,
    object: nil,
    userInfo: ["userId": "123"]
)

// Підписник
class ProfileViewModel {
    private var observers: [NSObjectProtocol] = []

    func startObserving() {
        let observer = NotificationCenter.default.addObserver(
            forName: .userDidLogin,
            object: nil,
            queue: .main
        ) { [weak self] notification in
            guard let userId = notification.userInfo?["userId"] as? String else { return }
            // Підписник реагує на подію
            self?.loadProfile(userId: userId)
        }
        observers.append(observer)
    }

    func stopObserving() {
        observers.forEach { NotificationCenter.default.removeObserver($0) }
        observers.removeAll()
    }
}

Combine framework — сучасна реактивна альтернатива NotificationCenter, що з'явилася в iOS 13. Publisher (NotificationCenter, URLSession, Timer) — видавець, Subscriber (sink, assign) — підписник. Combine додає оператори (map, filter, combineLatest) для трансформації потоку даних. @Published — property wrapper, що автоматично сповіщає підписників про зміни. В MVVM з SwiftUI Combine замінює NotificationCenter для зв'язування ViewModel та View.

KVO (Key-Value Observing) — старий механізм ObjC/Swift для спостереження за окремими властивостями об'єктів. @objc dynamic var name: String — властивість, що спостерігається. observe(.name) — підписка. KVO працює лише з @objc-сумісними класами та спадщиною ObjC. Apple рекомендує Combine та @Published замість KVO в нових проєктах. KVO залишається актуальним для UIKit-сумісності в гібридних проєктах.

Observer в Android: LiveData, StateFlow та SharedFlow

LiveData — компонент Android Architecture Components для реалізації Observer. Observable-клас, який сповіщає підписників про зміни даних. LiveData враховує життєвий цикл: підписники (LifecycleOwner) автоматично відписуються при знищенні. LiveData — Push-модель: дані передаються в observe(). LiveData — базовий будівельний блок MVVM в Android до впровадження Jetpack Compose.

kotlin
// ViewModel — видавець
class UserViewModel : ViewModel() {
    private val _user = MutableLiveData<User?>(null)
    val user: LiveData<User?> = _user

    fun loadUser(id: String) {
        viewModelScope.launch {
            val result = userRepository.getUser(id)
            _user.value = result
        }
    }
}

// Fragment — підписник
class UserFragment : Fragment() {
    private val viewModel: UserViewModel by viewModels()

    override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
        viewModel.user.observe(viewLifecycleOwner) { user ->
            // Підписник реагує на зміни
            userName.text = user?.name
            userEmail.text = user?.email
        }
    }
}

StateFlow та SharedFlow — реактивні типи з Kotlin Coroutines, що замінили LiveData в Jetpack Compose. StateFlow — observable state holder з фіксованим поточним значенням. SharedFlow — конфігурований hot flow без стану, придатний для одноразових подій (навігація, тости). Обидва типи тісно інтегровані з Compose: collectAsState(), collectAsEffect(). StateFlow обов'язковий в сучасних Android-проєктах з Compose.

LiveData vs StateFlow — LiveData прив'язаний до Android Lifecycle, StateFlow — платформонезалежний. StateFlow підтримує coroutines, оператори (map, filter) та тестується без Android-залежностей. LiveData простіший для Java-сумісності. Google рекомендує StateFlow для нових проєктів на Kotlin + Compose, LiveData — для підтримки старих проєктів або Java-коду.

Управління підписками та витоки пам'яті

Витоки пам'яті — головна проблема Observer без правильного управління підписками. Якщо підписник (Activity, Fragment, UIViewController) знищений, але не відписаний, видавець продовжує тримати посилання на нього, і збирач сміття не може звільнити пам'ять. В Android LifecycleOwner (Activity/Fragment) повинен викликати removeObserver() або використовувати observe(viewLifecycleOwner). В iOS — removeObserver в deinit або disposeBag в Combine.

ПлатформаМеханізм ObserverАвтоматична відпискаРучна відписка
iOSNotificationCenterНіremoveObserver() в deinit
iOSCombine (sink)Ніstore(in: &bag) — DisposeBag
iOSKVOНіremoveObserver() в deinit
AndroidLiveDataТак (LifecycleOwner)removeObserver() опціонально
AndroidStateFlowЧерез viewModelScopecancel() Job при відписці
AndroidRxJavaНіdispose() в CompositeDisposable

Weak reference в підписниках — при замиканні в блоці підписки використовуйте [weak self] в Swift та посилайтеся на lifecycle scope в Kotlin. LiveData автоматично управляє підпискою через LifecycleOwner — підписка активна тільки коли Lifecycle в стані STARTED або RESUMED. StateFlow в Compose використовує collectAsState() з урахуванням lifecycle. NotificationCenter в iOS вимагає явного [weak self], оскільки замикання сильно посилається на self.

Observer vs Publisher-Subscriber: в чому різниця

Observer (GoF) та Publisher-Subscriber (PubSub) — схожі, але різні патерни. В Observer видавець безпосередньо сповіщає підписників через виклик їх методів. Видавець знає про підписників (зберігає список). В PubSub видавець та підписник не знають один про одного — між ними знаходиться посередник (Event Bus, Message Queue, NotificationCenter). Видавець надсилає повідомлення на канал, підписник слухає канал. PubSub — більш слабка зв'язаність.

Приклади PubSub в мобільній розробці — NotificationCenter в iOS можна вважати PubSub: видавець не знає підписників — він просто публікує сповіщення. EventBus або Otto в Android (застарілі). SharedFlow з BroadcastChannel — PubSub в світі Kotlin. В розподілених системах PubSub реалізується через RabbitMQ, Kafka, Google PubSub. Для мобільної розробки PubSub корисний при модульній архітектурі, де модулі не повинні залежати один від одного.

Що вибрати — для UI-оновлень (ViewModel → View) використовуйте Observer (LiveData, StateFlow, @Published). Для міжмодульних подій (авторизація, вихід з акаунту, зміна теми) — PubSub (SharedFlow, NotificationCenter, EventBus). Observer простіший та ефективніший в межах одного екрана, PubSub гнучкіший для глобальних подій, але складніший у налагодженні через неявні залежності.

Часті запитання

Чим StateFlow відрізняється від LiveData?

StateFlow — платформонезалежний тип з Kotlin Coroutines, LiveData прив'язаний до Android Lifecycle. StateFlow підтримує coroutines та оператори, тестується без Android. LiveData автоматично управляє підпискою через LifecycleOwner. Google рекомендує StateFlow для нових проєктів на Kotlin + Compose, LiveData — для Java-сумісності.

Як уникнути витоків пам'яті з NotificationCenter?

Використовуйте [weak self] в замиканні-обробнику та викликайте removeObserver() в deinit. Зберігайте посилання на observer (NSObjectProtocol) та видаляйте його при знищенні об'єкта. В Combine використовуйте AnyCancellable та store(in:) для автоматичної відписки при звільненні DisposeBag.

Чи можна використовувати Observer в SwiftUI без Combine?

Так, SwiftUI підтримує ObservableObject з @Published та @StateObject/@ObservedObject — це вбудована реалізація Observer. @Published автоматично сповіщає View про зміни. Combine не обов'язковий: ObservableObject використовує objectWillChange Publisher, вбудований в SwiftUI. Combine додає оператори для трансформації потоків.

Коли використовувати SharedFlow замість StateFlow?

SharedFlow — для одноразових подій (навігація, тости, Snackbar), де не потрібне поточне значення. StateFlow — для стану UI (список даних, прогрес завантаження), де потрібен поточний знімок. SharedFlow не має value-властивості та не повертає останнє значення новим підписникам.

В чому різниця між KVO та Combine в iOS?

KVO — застарілий механізм ObjC, вимагає @objc dynamic та працює лише з класами, успадкованими від NSObject. Combine — сучасний Swift-фреймворк, типобезпечний, з операторами та інтеграцією зі SwiftUI. Combine замінює KVO та NotificationCenter. Apple рекомендує Combine для нових проєктів, KVO — тільки для підтримки легасі.

Підсумки

  • Observer — поведінковий патерн для сповіщення підписників про зміни
  • iOS NotificationCenter — PubSub-реалізація з іменованими сповіщеннями
  • iOS Combine — реактивний фреймворк з Publisher та Subscriber
  • Android LiveData — lifecycle-aware Observer з Android Architecture Components
  • Android StateFlow — реактивний State holder для Compose та корутин
  • Управління пам'яттю — обов'язкова відписка для запобігання витокам
  • Observer vs PubSub — пряма підписка vs посередник для слабкої зв'язаності

Ми розробимо мобільний застосунок під ключ

IT Sectr створює застосунки для iOS та Android для стартапів і бізнесу з 2017 року. Ми проконсультуємо вас і запропонуємо найкраще рішення.

Обговорити проект

Читайте також