Una notificación push es una alerta que entrega el sistema operativo y alcanza apps cerradas; una live query transmite cambios con la app abierta. Plantearlas como rivales es el error clásico — cubren estados disjuntos de la app, y la verdadera pregunta de diseño no es “cuál de las dos” sino “¿dónde está el usuario ahora mismo?”. La mayoría de las apps que se sienten realmente en tiempo real corren ambas y enrutan según esa respuesta.
Puntos clave
| Pregunta | Respuesta |
|---|---|
| Notificaciones push | Entrega por el gateway del SO (APNs, FCM) — alcanza apps cerradas, payload opaco de ~4 KB, mejor esfuerzo |
| Live queries | Suscripción por WebSocket — objetos completos con permisos verificados, tiempo real, la app debe estar abierta |
| La rivalidad | Falsa — atienden estados disjuntos de la app y se componen, no compiten |
| La regla de enrutamiento | App abierta → live query · app cerrada → push · toque en el push → abre, resuscribe, ponte al día |
| El modo de falla | Usar el push como canal de datos, o esperar que los sockets sobrevivan al proceso |
Ambos canales, en código
El patrón al que converge casi toda app de mensajería, pedidos y alertas:
// JavaScript — Back4app JS SDK: one channel per app state
// While the app is OPEN — the live query delivers the data itself
const messages = new Parse.Query('Message');
messages.equalTo('conversation', conversationId);
const sub = await messages.subscribe();
sub.on('create', (m) => appendBubble(m)); // full object, real time
// While it is CLOSED — push owns delivery: register this device
const installation = await Parse.Installation.currentInstallation();
installation.set('channels', [`user-${currentUser.id}`]);
await installation.save();
// A Cloud Code afterSave trigger sends the push to this channel —
// the OS shows the alert; the tap opens the app, which re-subscribes. // Flutter / Dart — Back4app Flutter SDK: one channel per app state
// While the app is OPEN — the live query delivers the data itself
final liveQuery = LiveQuery();
final messages = QueryBuilder<ParseObject>(ParseObject('Message'))
..whereEqualTo('conversation', conversationId);
final sub = await liveQuery.client.subscribe(messages);
sub.on(LiveQueryEvent.create, (m) => appendBubble(m)); // full object
// While it is CLOSED — push owns delivery: register this device
final installation = await ParseInstallation.currentInstallation();
installation.set('channels', ['user-$userId']);
await installation.save();
// A Cloud Code afterSave trigger sends the push to this channel —
// the OS shows the alert; the tap opens the app, which re-subscribes. // iOS / Swift — Back4app Swift SDK: one channel per app state
// While the app is OPEN — the live query delivers the data itself
let messages = Message.query("conversation" == conversationId)
let subscription = messages.subscribeCallback
subscription?.handleEvent { _, event in
if case .created(let m) = event { appendBubble(m) } // full object
}
// While it is CLOSED — push owns delivery: register this device
var installation = Installation.current
installation?.channels = ["user-\(userId)"]
installation?.save { _ in }
// A Cloud Code afterSave trigger sends the push to this channel —
// the OS shows the alert; the tap opens the app, which re-subscribes. // Android / Kotlin — Back4app Android SDK: one channel per app state
// While the app is OPEN — the live query delivers the data itself
val client = ParseLiveQueryClient.Factory.getClient()
val messages = ParseQuery.getQuery<ParseObject>("Message")
messages.whereEqualTo("conversation", conversationId)
val sub = client.subscribe(messages)
sub.handleEvent(SubscriptionHandling.Event.CREATE) { _, m ->
appendBubble(m) // full object
}
// While it is CLOSED — push owns delivery: register this device
val installation = ParseInstallation.getCurrentInstallation()
installation.put("channels", listOf("user-$userId"))
installation.saveInBackground()
// A Cloud Code afterSave trigger sends the push to this channel. Fíjate en lo que resuelve cada mitad: la suscripción entrega datos — objetos enteros, eventos tipados, en tiempo real. El registro de la instalación reserva atención — el derecho a interrumpir al usuario más tarde, por un pipeline que tu app no controla.
Dos entregas, dos dueños
Las rutas difícilmente podrían ser más distintas, y cada propiedad de la tabla comparativa se desprende de quién es dueño del último kilómetro.
Una notificación push sale de tu backend como una solicitud a un gateway operado por el sistema operativo — los gateways de push de iOS y Android (APNs, FCM), o un servicio de push del navegador hablando RFC 8030 en la web. El gateway es dueño de la entrega: guarda mensajes para dispositivos offline, agrupa y limita bajo presión de batería, y despierta tu app o renderiza el banner. Ese poder prestado es todo el punto — solo el SO puede alcanzar un proceso que no está corriendo — y también toda la restricción: payloads limitados a unos 4 KB, entrega de mejor esfuerzo, despertares silenciosos racionados por día y un payload fuera del modelo de permisos de tu backend.
Una live query nunca sale de tu perímetro de confianza: la app mantiene un WebSocket con el servidor de suscripciones del backend, que compara cada escritura en la base de datos con la consulta suscrita y hace push de eventos tipados — create, update, enter, leave, delete — con ACLs aplicadas por suscriptor. Objetos completos, latencia de tiempo real, sin techo de payload que valga la pena nombrar. La dependencia es brutal a cambio: la suscripción es estado dentro de tu proceso, y cuando el SO suspende la app — cosa que las plataformas móviles hacen agresivamente — el socket, y el canal, desaparecen.
Notificaciones push vs. live queries
| Notificaciones push | Suscripciones de live query | |
|---|---|---|
| Alcanza una app cerrada | Sí — el poder que la define | No — la suscripción muere con el proceso |
| Payload | ~4 KB, opaco a tus ACLs | Objetos completos, con permisos verificados por suscriptor |
| Garantía de entrega | Mejor esfuerzo; agrupable, limitable, descartable | Confiable mientras hay conexión; requiere ponerse al día tras los huecos |
| Latencia | Del orden de segundos, depende del gateway | Tiempo real (~RTT) |
| Dueño del transporte | El SO y su gateway | La flota de WebSockets de tu backend |
| Consentimiento del usuario | Prompt de permiso; el usuario puede revocarlo | Ninguno — son los datos de tu propia app |
| Costo del mal uso | Fatiga de notificaciones, desinstalaciones | Carga de batería y de sockets si te suscribes de más |
| Hecha para | Atención y reenganche | Datos y estado dentro de la app |
Las filas se componen limpiamente porque los dos canales responden preguntas distintas: el push responde “¿cómo alcanzo al usuario?”, las live queries responden “¿cómo se mantiene fiel la pantalla?” — y por eso la comparación a nivel de transporte entre SSE, WebSockets y polling vive por completo dentro de la segunda pregunta.
La costura: donde las apps realmente se rompen
Los bugs viven en la unión entre canales. Un usuario toca un push sobre un mensaje que también llegó por live query antes de que la app se suspendiera — deduplica por ID de objeto, no por canal. Llega un push sobre datos a los que el usuario ya no puede acceder — busca por la API normal al abrir y deja que las ACLs respondan, nunca confíes en el payload. La app estuvo cerrada tres días — la app reabierta no puede reproducir el hueco desde el push (las notificaciones no son un diario), así que la transición es siempre resuscribirse y luego reejecutar la consulta base para reconstruir la verdad, manteniendo el registro del token de push fresco en segundo plano. Diseña la costura una vez y ambos canales se vuelven aburridos — que es la meta.
Casos de uso comunes
- Chat y mensajería — la live query renderiza la conversación abierta; el push lleva el “mensaje nuevo” por el estado cerrado. La app canónica de dos canales.
- Seguimiento de pedidos y entregas — la pantalla de seguimiento abierta se suscribe; los cambios a “entregado” con la app cerrada llegan como push.
- Alertas operativas — las consolas de guardia se suscriben para el tablero; la ruta del pager es push, porque nadie deja la app abierta a las 3 de la mañana.
- Subastas y lanzamientos — movimiento de precio en vivo dentro de la app; que te superen la oferta mientras no estás es un push, con deep link de vuelta a la pantalla en vivo.
- Engagement social — likes y respuestas llegan como push de reenganche; el feed abierto se vuelve vivo vía suscripción.
¿Qué canal deberías usar? Matriz de decisión
| Tu situación | Usa |
|---|---|
| La pantalla está abierta y debe mantenerse al día | Live query |
| El evento ocurre con la app cerrada y el usuario debe enterarse | Notificación push |
| El payload es sensible o está restringido por permisos | Live query — o push solo con identificadores, buscando al abrir |
| Necesitas entrega garantizada y ordenada de datos | Ninguno solo — actualización por consulta al abrir, canales como aceleradores |
| Actualizar un badge o contador dentro de la app en tiempo real | Live query |
| Reenganchar a usuarios que llevan días sin abrir la app | Push — es el único canal capaz |
| Kiosco o tablero de pared que nunca duerme | Solo live query; el push no aporta nada |
Limitaciones y trade-offs
- El push no es un canal de datos. El techo de payload, el enrutamiento opaco fuera de tus ACLs y el agrupamiento lo hacen estructuralmente incorrecto para cargar estado — envía punteros, busca la verdad al abrir.
- La entrega del push es una probabilidad, no una promesa. Los optimizadores de batería, los permisos revocados y el throttling del gateway descartan mensajes en silencio; todo lo que no puede perderse necesita una ruta de reconciliación basada en consultas.
- Las live queries se detienen en la frontera del proceso. Ningún socket sobrevive a la suspensión; tratar una suscripción como canal siempre encendido es la premisa falsa detrás de la mayoría de los bugs de “perdimos eventos”.
- Ambos canales le cobran al cliente. Las suscripciones demasiado amplias queman batería y matching en el servidor; el push demasiado ansioso quema buena voluntad — la desinstalación es el rate limiting del usuario.
- La costura es responsabilidad tuya. La deduplicación, las consultas de actualización y la renovación de tokens son lógica de aplicación; ninguno de los dos canales las regala.
Push y live queries en Back4app
Back4app es una plataforma open-source de Backend as a Service (BaaS) que combina base de datos gestionada, APIs REST y GraphQL generadas automáticamente, autenticación, almacenamiento de archivos y funciones serverless con Cloud Code. Ambos canales vienen integrados y comparten una sola ruta de escritura: un trigger afterSave de Cloud Code sobre la misma escritura en la base de datos puede enviar el push — por las integraciones de la plataforma con los gateways, con segmentación por dispositivo y por canal — mientras Live Query propaga el objeto completo a cada cliente suscrito y con permisos verificados, automáticamente. El diagrama de enrutamiento de arriba colapsa en un trigger y una llamada de subscribe, y la lógica de la costura — consultas de actualización, registro del token vía la clase Installation — corre por los mismos SDKs que muestran las pestañas de código.
Preguntas frecuentes
¿Cuál es la diferencia entre notificaciones push y live queries?
La ruta de entrega y el estado de la app que cada una atiende. Una notificación push viaja por el gateway de push del sistema operativo y llega al dispositivo incluso con tu app cerrada — pero lleva un payload pequeño y opaco, con garantías de mejor esfuerzo. Una live query es una suscripción por WebSocket que tu app en ejecución mantiene abierta: objetos completos, en tiempo real, con permisos verificados — y muerta en el instante en que la app lo está.
¿Las live queries funcionan con la app cerrada?
No, y ninguna astucia del lado del cliente lo cambia. Una live query es estado dentro de tu proceso en ejecución — un WebSocket que la app mantiene abierto. Cuando el sistema operativo suspende o mata la app, el socket muere con ella, y las plataformas móviles suspenden agresivamente las apps en segundo plano para ahorrar batería. Alcanzar una app cerrada es exactamente el trabajo que el SO reserva para su propio gateway de push.
¿Una app de chat debería usar notificaciones push o live queries?
Ambas, divididas por el estado de la app. La conversación abierta se suscribe a una live query — los mensajes se renderizan al instante con datos completos, indicadores de escritura y confirmaciones de lectura incluidos. La app cerrada depende del push para alertar al destinatario, cargando apenas lo suficiente para renderizar el banner. El toque abre la app, que se resuscribe y reconsulta para ponerse al día. Todo mensajero popular funciona así.
¿La llegada de las notificaciones push está garantizada?
No — la entrega es de mejor esfuerzo por diseño. Los gateways del SO agrupan, limitan y descartan mensajes bajo presión de batería, los usuarios desactivan los permisos por completo y los dispositivos se quedan sin señal. Los push silenciosos en segundo plano se limitan aún más que los visibles. Trata el push como un toque en el hombro para despertar a alguien, nunca como un contrato de transporte de datos; la app debe reconciliar el estado consultando después de abrir.
¿Qué tan grande puede ser el payload de una notificación push?
Kilobytes, no datos. Los gateways del SO limitan el payload a unos 4 KB, y ese payload es opaco al modelo de permisos de tu backend — lo que pongas ahí queda en el pipeline de notificaciones, fuera de tus ACLs. El patrón robusto envía solo identificadores y strings para mostrar, y deja que la app abierta busque los objetos reales por la API normal, con permisos verificados.
¿Qué es una notificación push silenciosa?
Un push sin alerta visible que le pide al SO despertar tu app brevemente en segundo plano — típicamente para precargar datos y que la próxima apertura se sienta instantánea. Es el recurso más racionado del sistema de push: el SO presupuesta los despertares por app por día e ignora el exceso, así que el push silencioso funciona como capa de optimización, nunca como canal confiable de sync.
¿Necesito notificaciones push y live queries a la vez?
Si a tus usuarios les importan los eventos que ocurren con la app cerrada — mensajes, pedidos, alertas —, sí, casi inevitablemente. Los dos canales cubren estados disjuntos de la app: las live queries son dueñas de la experiencia con la app abierta, el push es dueño del reenganche desde el estado cerrado. Los backends que traen ambos integrados dejan que una sola escritura en la base de datos se propague a cada canal, así que necesitar ambos deja de implicar construir dos veces.