NotificationCenter es un mecanismo del sistema en iOS para enviar y recibir notificaciones entre componentes de la aplicación sin una conexión directa entre emisor y receptor. Basado en el patrón Observer, NotificationCenter permite que los objetos se suscriban a eventos y respondan a ellos de forma asíncrona. Según Apple Documentation (2025), NSNotificationCenter admite tanto el envío sincrónico de notificaciones mediante post(name:object:) como el envío diferido mediante NotificationQueue. El centro de notificaciones opera dentro de un solo proceso y no cruza los límites de las aplicaciones.
Puntos clave
NotificationCenter (NSNotificationCenter) es un mecanismo integrado de iOS para implementar comunicación débilmente acoplada entre objetos. El patrón Observer permite que un objeto (emisor) notifique a múltiples otros objetos (observadores) sobre un evento sin una referencia directa a ellos. NotificationCenter opera con tres entidades: Notification.Name (identificador de notificación), Notification (contenedor con datos) y NotificationCenter (despachador). Cada aplicación tiene un default center compartido.
Notification.Name es una estructura que identifica el tipo de notificación. Se crea mediante extension Name: Notification.Name(“MyNotification”). Notification es un objeto que contiene name, object (emisor) y userInfo (diccionario con datos). Las notificaciones del sistema se declaran como constantes: UIApplication.didBecomeActiveNotification, UIResponder.keyboardWillShowNotification. Las notificaciones personalizadas deben agruparse mediante extension para evitar colisiones de nombres. Los nombres deben tener formato de dominio inverso.
// Definición de notificaciones personalizadas
extension Notification.Name {
static let dataDidUpdate =
Notification.Name("com.app.dataDidUpdate")
static let userLoggedOut =
Notification.Name("com.app.userLoggedOut")
}
// Envío de una notificación con datos
let userInfo: [String: Any] = [
"userId": 123,
"timestamp": Date()
]
NotificationCenter.default.post(
name: .dataDidUpdate,
object: nil,
userInfo: userInfo
)
Un observador se suscribe a una notificación mediante el método addObserver(_:selector:name:object:). Selector es el método que se llamará al recibir la notificación. El parámetro object permite filtrar notificaciones de un remitente específico. Si object es nil, el observador recibe todas las notificaciones con el nombre especificado de cualquier remitente. Desde iOS 9, addObserver no requiere eliminación manual para la API block-based, pero selector-based sigue requiriendo removeObserver.
// Suscripción a una notificación (basada en selector)
NotificationCenter.default.addObserver(
self,
selector: #selector(handleDataUpdate),
name: .dataDidUpdate,
object: nil
)
@objc func handleDataUpdate(_ notification: Notification) {
guard let userId = notification.userInfo?["userId"] as? Int else { return }
updateUI(for: userId)
}
// Suscripción a una notificación (basada en bloque, iOS 9+)
var observer: NSObjectProtocol?
observer = NotificationCenter.default.addObserver(
forName: .dataDidUpdate,
object: nil,
queue: .main
) { [weak self] notification in
guard let self else { return }
self.handleNotification(notification)
}
NotificationCenter almacena una tabla de mapeo (nombre → conjunto de observadores). Cuando el emisor llama a post(name:object:), el centro de notificaciones recorre sincrónicamente todos los observadores suscritos a ese nombre y ejecuta sus selectores o bloques. Característica clave: post bloquea el hilo actual hasta que todos los manejadores completen. Si los manejadores realizan operaciones pesadas, esto retrasa al emisor. NotificationQueue resuelve este problema difiriendo la entrega de notificaciones.
El método post(name:object:userInfo:) envía una notificación inmediatamente a todos los observadores. La llamada es sincrónica — el código después de post se ejecuta solo después de que todos los manejadores hayan completado. El orden de invocación de los observadores no está garantizado y puede cambiar entre ejecuciones. Para procesamiento secuencial, use NotificationQueue con coalescing. No llame a post dentro de un manejador de la misma notificación — esto provoca recursión infinita.
NotificationQueue añade notificaciones a una cola para entrega asíncrona. Admite coalescing (fusión de notificaciones idénticas) y selección de cola de entrega (asap, idle, modal). El coalescing es útil para eventos frecuentes (progreso de descarga) cuando solo necesita notificar con el último valor. NotificationQueue usa el run loop para activarse, por lo que solo funciona en hilos con un run loop activo.
// Envío diferido mediante NotificationQueue
let notification = Notification(
name: .dataDidUpdate,
object: self,
userInfo: ["progress": 0.5]
)
// Coalescing: múltiples notificaciones se fusionan en una
NotificationQueue.default.enqueue(
notification,
postingStyle: .whenIdle,
coalesceMask: .onName,
forModes: [.common]
)
// Entrega asíncrona mediante DispatchQueue
DispatchQueue.main.async {
NotificationCenter.default.post(name: .dataDidUpdate, object: nil)
}
iOS proporciona tres mecanismos principales para la comunicación entre objetos: NotificationCenter, Delegate y KVO (Key-Value Observing). Cada uno resuelve el problema de notificación pero con diferentes compensaciones en acoplamiento, rendimiento y seguridad de tipos. La elección del mecanismo depende de la relación uno-a-uno o uno-a-muchos y la necesidad de transferencia de datos.
| Característica | NotificationCenter | Delegate | KVO |
|---|---|---|---|
| Acoplamiento | Débil (nombre de notificación) | Fuerte (protocolo) | Medio (clave) |
| Relación | Uno-a-muchos | Uno-a-uno | Uno-a-muchos |
| Seguridad de tipos | Baja (userInfo como Dictionary) | Alta (métodos de protocolo) | Media (Any?) |
| Rendimiento | Medio (recorrido de tabla) | Alto (llamada directa) | Bajo (NSObject) |
| Asincronía | Sincrónico (post bloquea) | Sincrónico en el hilo del emisor | Sincrónico al cambiar |
NotificationCenter es ideal para eventos a los que deben responder múltiples componentes independientes. Ejemplos: cambios en la configuración de la aplicación, cierre de sesión del usuario, recepción de una notificación push en segundo plano. NotificationCenter también es adecuado para módulos débilmente acoplados (la característica A no debe conocer la característica B). La desventaja es la falta de seguridad de tipos: las claves de userInfo son cadenas, no enumeraciones.
Delegate es la opción para relaciones uno-a-uno con un contrato claro (tableView.delegate). Delegate es más rápido y seguro por tipos. KVO es la opción para observar cambios en una propiedad específica del modelo (isLoading, progress). KVO requiere herencia de NSObject y puede causar dificultades de depuración (cadenas mágicas como claves). En Swift moderno, Combine y las secuencias async reemplazan los tres enfoques.
El método addObserver admite dos variantes de suscripción: selector-based (tradicional) y block-based (con closure). Selector-based requiere compatibilidad @objc y eliminación manual del observador. Block-based (iOS 9+) permite usar una capture list y es gestionado automáticamente por el OS cuando se usan bloques sin referencias fuertes. Block-based también admite queue — el observador recibe la notificación en la cola especificada.
La forma tradicional de suscripción mediante selector. El método manejador debe estar marcado con @objc y aceptar un Notification opcional. Ventaja: puede ser usado por cualquier clase, incluyendo Objective-C heredado. Desventajas: falta de seguridad de tipos del selector, riesgo de errores tipográficos en el nombre del selector, removeObserver obligatorio en deinit. Si el observador se elimina antes que el objeto, el manejador no se llamará.
La API block-based acepta un closure que se ejecuta al recibir la notificación. El parámetro queue determina en qué cola se ejecuta el bloque — la cola principal para actualizaciones de UI o una cola en segundo plano para procesamiento de datos. El valor de retorno NSObjectProtocol se usa para eliminar el observador: NotificationCenter.default.removeObserver(observer). Block-based es preferible en Swift moderno.
protocol NotificationToken {
func dispose()
}
extension NotificationCenter {
func observe(
name: NSNotification.Name,
object: Any? = nil,
queue: OperationQueue? = .main,
using block: @escaping (Notification) -> Void
) -> NotificationToken {
let observer = addObserver(forName: name, object: object,
queue: queue, using: block)
return NotificationTokenWrapper(observer: observer, center: self)
}
}
// Uso con eliminación automática
class ViewModel {
private var tokens: [NotificationToken] = []
func startObserving() {
let token = NotificationCenter.default.observe(
name: .dataDidUpdate,
queue: .main
) { [weak self] notification in
self?.handleUpdate(notification)
}
tokens.append(token)
}
deinit {
tokens.forEach { $0.dispose() }
}
}
Las fugas de memoria son uno de los principales problemas al trabajar con NotificationCenter. Si un observador no se elimina antes de la desasignación, cuando se envíe una notificación el centro intentará llamar a un método en un objeto ya desasignado, resultando en EXC_BAD_ACCESS. Desde iOS 9, block-based addObserver usa referencias débiles, pero selector-based sigue requiriendo removeObserver manual. Mejor práctica: eliminar el observador en deinit.
Selector-based: llame siempre a NotificationCenter.default.removeObserver(self) en deinit. Si el observador está suscrito a múltiples notificaciones, puede eliminar todas a la vez (sin parámetros) o una específica por nombre. Block-based: elimine mediante removeObserver con el token devuelto por addObserver. Para block-based en iOS 9+, no ocurre una fuga, pero la eliminación sigue siendo recomendada por rendimiento: los observadores desasignados no se recorrerán durante post.
class SafeObserver {
private var observers: [NSObjectProtocol] = []
func addSubscriptions() {
let token1 = NotificationCenter.default.addObserver(
forName: .dataDidUpdate, object: nil,
queue: .main) { [weak self] _ in
self?.refreshData()
}
let token2 = NotificationCenter.default.addObserver(
forName: .userLoggedOut, object: nil,
queue: .main) { [weak self] _ in
self?.logout()
}
observers.append(contentsOf: [token1, token2])
}
deinit {
observers.forEach { NotificationCenter.default.removeObserver($0) }
}
private func refreshData() { }
private func logout() { }
}
El patrón Token automatiza la gestión de observadores. Al suscribirse, se devuelve un objeto token (NSObjectProtocol) que elimina automáticamente al observador al desasignarse. NotificationTokenWrapper almacena una referencia débil a NotificationCenter y al token del observador, llamando a removeObserver en deinit. Esto acerca NotificationCenter al enfoque de Combine, donde AnyCancellable gestiona el ciclo de vida de la suscripción.
Seguridad de hilos: NotificationCenter garantiza que post puede llamarse desde cualquier hilo, y todos los observadores recibirán la notificación en el mismo hilo donde se llamó a post. Esto es crítico para aplicaciones multiproceso: si una notificación se envía desde un hilo en segundo plano, los manejadores también se ejecutarán en el hilo en segundo plano. Para actualizaciones de UI, despache el manejo a la cola principal mediante DispatchQueue.main.async.
NotificationCenter es seguro para hilos para llamadas a post y addObserver desde diferentes hilos. La sincronización interna usa bloqueos, por lo que los posts frecuentes desde múltiples hilos pueden crear contención. Para escenarios de alta carga (progreso de descarga de 1000 archivos), use una cola de notificaciones separada o un publisher de Combine. NotificationQueue con postingStyle .now es equivalente a post directo.
NotificationCenter admite el publisher de Combine mediante NotificationCenter.default.publisher(for:object:). Publisher convierte cada notificación en un evento de Combine que puede transformarse mediante map, filter, debounce y throttle. Esto resuelve el problema de entrega sincrónica: Combine procesa notificaciones de forma asíncrona en el Scheduler especificado. NotificationCenter.publisher es un puente entre el mecanismo heredado y la programación reactiva moderna.
import Combine
class ReactiveViewModel {
private var cancellables = Set<AnyCancellable>()
func setupCombineSubscription() {
NotificationCenter.default
.publisher(for: .dataDidUpdate)
.receive(on: DispatchQueue.main)
.compactMap { $0.userInfo?["progress"] as? Float }
.debounce(for: .seconds(0.3), scheduler: RunLoop.main)
.sink { [weak self] progress in
self?.progressLabel.text = "\(Int(progress * 100))%"
}
.store(in: &cancellables)
}
}
Preguntas frecuentes
Sí, NotificationCenter es seguro para hilos para llamadas a post y addObserver desde cualquier hilo. Sin embargo, los manejadores se ejecutan en el mismo hilo donde se llamó a post. Para actualizaciones de UI, use queue: .main en block-based addObserver o DispatchQueue.main.async dentro del manejador. El publisher de Combine con receive(on:) también resuelve el problema del hilo.
Selector-based: crash EXC_BAD_ACCESS al enviar una notificación después de la desasignación del observador. Block-based (iOS 9+): no hay fuga gracias a la referencia débil, pero el centro de notificaciones sigue manteniendo el bloque en memoria hasta removeObserver explícito. Se recomienda eliminar siempre al observador en deinit o usar el patrón Token para gestión automática.
NotificationCenter es un mecanismo de transmisión para eventos arbitrarios entre componentes no relacionados. KVO observa cambios en una propiedad específica de un objeto específico. KVO requiere herencia de NSObject y notifica automáticamente sobre cambios de propiedad mediante setter. NotificationCenter solo notifica cuando se llama explícitamente a post. Para observación del modelo, es preferible KVO o Combine.
Un default center por proceso de aplicación. Se pueden crear centros adicionales mediante NotificationCenter(), pero en la práctica se usa el default compartido. Cada centro opera de forma independiente — post en uno no se entrega a los observadores de otro. Para el aislamiento de módulos, use espacios de nombres Name separados mediante nombres de notificación de dominio inverso.
Parcialmente. Combine proporciona NotificationCenter.Publisher, que envuelve NotificationCenter en un flujo reactivo. Combine resuelve el problema de sincronía (mediante receive(on:)), añade operadores de transformación y gestión automática de suscripciones (AnyCancellable). Sin embargo, NotificationCenter sigue siendo necesario para notificaciones del sistema iOS (UIApplication, UIKeyboard) y código heredado. Combine es una mejora, no un reemplazo.
Resumen
Desarrollaremos una aplicación móvil llave en mano
IT Sectr crea aplicaciones para iOS y Android para startups y empresas desde 2017. Le asesoraremos y le propondremos la mejor solución.
Lea también