NSNotificationCenter addObserver в Swift: Полный гайд с примерами

Онлайн-тренажер Python для начинающих

Изучайте Python легко и без перегрузки теорией. Решайте практические задачи с автоматической проверкой, получайте подсказки на русском языке и пишите код прямо в браузере — без необходимости что-либо устанавливать.

Начать курс

NSNotificationCenter addObserver в Swift: Полное руководство с живыми примерами

Представьте, что вы пишете приложение для заказа пиццы. Пользователь нажимает «Оплатить» на экране корзины, а через несколько секунд другой экран — «Статус заказа» — должен автоматически показать, что пицца уже в пути. Как связать эти два экрана, не превращая код в спагетти из замыканий и делегатов? Ответ — NSNotificationCenter (а в современном Swift — просто NotificationCenter).

Этот паттерн — один из самых популярных в iOS-разработке. На Stack Overflow вопрос «NSNotificationCenter addObserver in Swift» набирает тысячи просмотров, потому что даже опытные разработчики иногда путаются в синтаксисе, управлении памятью и удалении наблюдателей. Давайте раз и навсегда разберемся, как правильно использовать уведомления в Swift.

Что такое NotificationCenter и зачем он нужен?

NotificationCenter — это встроенный механизм iOS, реализующий паттерн «Наблюдатель» (Observer). Он позволяет одним объектам (постерам) отправлять сообщения, а другим (наблюдателям) — подписываться на эти сообщения и реагировать на них. Главное преимущество — слабая связанность: отправитель и получатель ничего не знают друг о друге.

Когда это полезно:

  • Уведомление одного экрана о действии на другом (например, корзина → статус заказа)
  • Реакция на системные события (клавиатура появилась, приложение ушло в фон)
  • Обновление UI после завершения фоновой загрузки данных

В старых проектах (Objective-C) использовался NSNotificationCenter, в Swift начиная с iOS 9 он называется просто NotificationCenter. В этой статье мы будем использовать современный Swift-синтаксис.

Как подписаться на уведомление: addObserver

Метод addObserver — это сердце системы. Он регистрирует объект как наблюдателя определенного уведомления. Есть два основных способа: через селектор (классика) и через блок (замыкание).

1. Метод addObserver с селектором (target-action)

Этот способ пришел еще из Objective-C. Вы указываете объект-наблюдатель, селектор (метод, который будет вызван), имя уведомления и, опционально, объект-отправителя.

import UIKit

class OrderStatusViewController: UIViewController {
    
    override func viewDidLoad() {
        super.viewDidLoad()
        
        // Подписываемся на уведомление об оплате
        NotificationCenter.default.addObserver(
            self,
            selector: #selector(didReceivePaymentNotification(_:)),
            name: Notification.Name("PaymentDidSucceed"),
            object: nil
        )
    }
    
    @objc func didReceivePaymentNotification(_ notification: Notification) {
        print("Оплата прошла успешно! Обновляем статус заказа.")
        // Здесь обновляем UI
    }
    
    // ВАЖНО: не забываем отписываться!
    deinit {
        NotificationCenter.default.removeObserver(self)
    }
}

Что здесь важно:

  • Метод-обработчик обязан быть помечен @objc — это требование runtime Objective-C.
  • Параметр notification типа Notification содержит userInfo — словарь с дополнительными данными.
  • Если передать object: nil, наблюдатель будет получать уведомления от любого отправителя.

2. Метод addObserver с замыканием (iOS 9+)

Современный и более безопасный способ — использовать блок. Он не требует @objc и позволяет захватывать внешние переменные.

class PaymentManager {
    
    private var observer: NSObjectProtocol?
    
    func startObservingPayment() {
        observer = NotificationCenter.default.addObserver(
            forName: Notification.Name("PaymentDidSucceed"),
            object: nil,
            queue: .main
        ) { [weak self] notification in
            guard let self = self else { return }
            print("Получены данные: \(notification.userInfo ?? [:])")
            self.showSuccessAlert()
        }
    }
    
    private func showSuccessAlert() {
        // Показываем alert
    }
    
    deinit {
        if let observer = observer {
            NotificationCenter.default.removeObserver(observer)
        }
    }
}

Ключевые моменты:

  • Метод возвращает объект типа NSObjectProtocol — его нужно сохранить, чтобы потом отписаться.
  • Замыкание выполняется в указанной очереди (.main — для обновления UI).
  • Обязательно используйте [weak self], чтобы избежать цикла сильных ссылок.

Отправка уведомлений: post

Чтобы уведомление дошло до наблюдателей, его нужно отправить. Делается это через post(name:object:userInfo:).

// Где-то в PaymentViewController
let paymentData: [String: Any] = [
    "orderID": 12345,
    "amount": 29.99,
    "currency": "USD"
]

NotificationCenter.default.post(
    name: Notification.Name("PaymentDidSucceed"),
    object: self,
    userInfo: paymentData
)

Обратите внимание: object может быть любым объектом. Если наблюдатель указал конкретный объект при подписке, он получит уведомление только от этого отправителя.

Современный подход: Notification.Name как расширение

Хардкодить строки — плохая практика. В Swift принято создавать расширения для Notification.Name:

extension Notification.Name {
    static let paymentDidSucceed = Notification.Name("PaymentDidSucceed")
    static let paymentDidFail = Notification.Name("PaymentDidFail")
    static let orderStatusChanged = Notification.Name("OrderStatusChanged")
}

// Использование:
NotificationCenter.default.post(name: .paymentDidSucceed, object: nil)
NotificationCenter.default.addObserver(self, selector: #selector(handlePayment), name: .paymentDidSucceed, object: nil)

Так вы избегаете опечаток и получаете автодополнение в Xcode.

Удаление наблюдателей: жизненно важный шаг

Самый частый баг новичков — забыть отписаться. Если объект-наблюдатель удален из памяти, но продолжает быть зарегистрированным, при отправке уведомления произойдет креш (EXC_BAD_ACCESS).

Правило №1: Всегда вызывайте removeObserver в deinit.

Правило №2: Для селекторного метода достаточно вызвать removeObserver(self) — это удалит все подписки объекта.

Правило №3: Для блочного метода удаляйте конкретный объект:

deinit {
    if let observer = observer {
        NotificationCenter.default.removeObserver(observer)
    }
}

Начиная с iOS 16 и Swift 5.7, появился новый способ — addObserver(with:), который возвращает токен, автоматически удаляемый при деините. Но пока большинство проектов поддерживают iOS 14+, классический подход остается актуальным.

Типичные ошибки и как их избежать

  • Двойная подписка: Если выз

Блоги

Рекомендаций по книгам