DEV Community

GoyesDev
GoyesDev

Posted on

[SC] Migrando a Notificaciones seguras (en términos de concurrencia)

Comprensión durante la lectura

¿Por qué las notificaciones estándar de NotificationCenter no son thread-safe en Swift Concurrency?

Porque el tipo Notification es nonisolated por defecto, incluso cuando se publica desde un actor conocido como el MainActor. Esto impide que el compilador razone sobre la seguridad de hilos al llamar métodos con aislamiento específico como @MainActor, incluso aunque recibamos la notificación en el hilo principal.

func startObservingOldWay() {
  NotificationCenter.default.addObserver(forName: UIApplication.didBecomeActiveNotification, object: nil, queue: .main, using: { [weak self] notification in
    self?.handleDidBecomeActive()
  })
}

@MainActor
func handleDidBecomeActive() {
  print("Did become active!")
}
Enter fullscreen mode Exit fullscreen mode

¿Qué problema concreto resuelve MainActorMessage frente al API anterior?

Garantiza que el closure de observación se ejecute en el main actor, eliminando la advertencia de concurrencia y permitiendo llamar directamente a métodos @MainActor sin conversiones manuales.

func startObservingNewWay() {
  token = NotificationCenter.default.addObserver(of: UIApplication.self, for: .didBecomeActive) { [weak self] message in
    self?.handleDidBecomeActive()
  }
}

@MainActor
func handleDidBecomeActive() {
  print("Did become active!")
}
Enter fullscreen mode Exit fullscreen mode

¿Cuál es la diferencia clave entre MainActorMessage y AsyncMessage?

MainActorMessage entrega la notificación sincrónicamente en el main actor. AsyncMessage la entrega de forma asíncrona en un aislamiento arbitrario y puede publicarse desde cualquier dominio de aislamiento.

struct RecentBuildsChangedMessage: NotificationCenter.AsyncMessage {
  typealias Subject = [RecentBuild]

  let recentBuilds: Subject
}
Enter fullscreen mode Exit fullscreen mode
extension NotificationCenter.MessageIdentifier where Self == NotificationCenter.BaseMessageIdentifier<RecentBuildsChangedMessage> {
  static var recentBuildsChanged: NotificationCenter.BaseMessageIdentifier<RecentBuildsChangedMessage> {
    .init()
  }
}
Enter fullscreen mode Exit fullscreen mode
let recentBuilds = [RecentBuild(appName: "Stock Analyzer")]
let message = RecentBuildsChangedMessage(recentBuilds: recentBuilds)
NotificationCenter.default.post(message)
Enter fullscreen mode Exit fullscreen mode
recentBuildsToken = NotificationCenter.default.addObserver(of: [RecentBuild].self, for: .recentBuildsChanged) { [weak self] message in
  self?.handleNewRecentBuilds(message.recentBuilds)
}
Enter fullscreen mode Exit fullscreen mode

¿Qué ventajas concretas aporta migrar a notificaciones con tipo fuerte (strongly typed)?

Se elimina la necesidad de hacer casting manual desde notification.object as? [RecentBuild], se reduce el boilerplate y se obtiene verificación en tiempo de compilación, además de la seguridad de hilos.

¿Qué cambio de comportamiento en threading puede ocurrir al migrar al nuevo API?

En el API anterior las notificaciones se entregaban en la misma cola desde la que se publicaban. Con el nuevo API, el @MainActor se aplica más estrictamente, lo que puede cambiar el comportamiento de threading de la app tras la migración.


Bibliografía

Top comments (0)