Integración del SDK de iOS
SDK nativo de iOS para rastrear eventos, gestionar sesiones de usuario, enviar notificaciones push y mostrar mensajes in-app.
Funciones
- Ligero: ocupa poco espacio.
- Rápido: optimizado para el rendimiento.
- Compatibilidad sin conexión: cola de eventos basada en SQLite.
- Reintento automático: espera exponencial ante fallos.
- Procesamiento por lotes: agrupación eficiente de eventos (50 eventos / 5 s).
- Mensajería in-app: mensajes nativos y HTML, 5 tipos, con límite de frecuencia.
- Notificaciones push: Apple Push Notification Service (APNS).
- Privacidad primero: cumple el RGPD y respeta el consentimiento de los usuarios.
- iOS 14+: compatible con versiones modernas de iOS.
Requisitos
- iOS 14.0+
- Xcode 15.0+
- Swift 5.9+
Instalación
Swift Package Manager
El SDK se distribuye como paquete Swift. Añade lo siguiente a Package.swift:
dependencies: [
.package(url: "https://github.com/joryio/joryio-ios.git", from: "1.0.0")
]
El paquete ofrece dos products. Enlaza el que necesite tu app:
.target(
name: "YourApp",
dependencies: [
.product(name: "Joryio", package: "joryio-ios"), // tracking, identidad, push
.product(name: "JoryioUI", package: "joryio-ios"), // + visualización de mensajes in-app
]
)
CocoaPods
pod 'Joryio/UI' # todo, incluida la visualización in-app
# pod 'Joryio' # solo tracking, identidad y push: sin WebKit enlazado
JoryioUI depende de Joryio, así que enlazar el producto de UI te da ambos.
Usa Joryio solo cuando tu app no necesite mensajes in-app, o cuando una
revisión de seguridad se oponga a enlazar un web view. Ojo: el valor por defecto
en CocoaPods es Joryio sin UI, así que pod 'Joryio' no mostrará mensajes
in-app hasta que lo cambies por pod 'Joryio/UI'.
O desde Xcode:
- Archivo → Añadir dependencias de paquetes.
- Escribe
https://github.com/joryio/joryio-ios.git. - Selecciona la versión y añádela al target.
Inicio rápido
1. Inicializa el SDK
En AppDelegate.swift:
import Joryio
func application(
_ application: UIApplication,
didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?
) -> Bool {
// Inicializa con tu clave de SDK
Joryio.shared.initialize(
sdkKey: "jry_sdk_ios_YOUR_SDK_KEY",
apiHost: "api-eu1.joryio.com"
)
return true
}
Encuentra tu clave de SDK en el dashboard de Joryio: Configuración → Apps → [Tu app] → Claves de SDK.
2. Rastrear eventos
// Evento básico
Joryio.shared.track("Button Tapped")
// Evento con propiedades
Joryio.shared.track("Product Viewed", properties: [
"product_id": "abc123",
"product_name": "Wireless Headphones",
"price": 99.99,
"category": "Electronics"
])
// Vista de pantalla
Joryio.shared.trackScreen("ProductDetail", properties: [
"product_id": "abc123"
])
3. Identificar usuarios
// Identifica un usuario
Joryio.shared.identify("user-123")
Joryio.shared.setAttributes([
"email": "user@example.com",
"name": "John Doe",
"plan": "premium"
])
// Establece atributos más adelante
Joryio.shared.setAttribute("last_purchase", value: Date())
Joryio.shared.incrementAttribute("lifetime_value", by: 99.99)
// Al cerrar sesión
Joryio.shared.reset()
Datos automáticos de sesión
El SDK de iOS enriquece los eventos Session Start con datos de dispositivo y entorno:
$device_id$platform(ios)$model$os_name/$os_version$app_version/$build_number$bundle_id$screen_width/$screen_height$locale$language/$languages$timezonecountry(ISO-3166-1 alfa-2, derivado de la IP al iniciar la sesión)
Notificaciones push
Activa las notificaciones push para enviar mensajes segmentados mediante Apple Push Notification Service (APNS).
1. Configuración en AppDelegate
import Joryio
class AppDelegate: UIResponder, UIApplicationDelegate {
func application(
_ application: UIApplication,
didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?
) -> Bool {
// Inicializa el SDK
Joryio.shared.initialize(
sdkKey: "jry_sdk_ios_YOUR_KEY",
apiHost: "api-eu1.joryio.com"
)
// Solicita permisos de push
Task {
let granted = await Joryio.shared.requestPushPermissions()
if granted {
print("Notificaciones push activadas")
}
}
return true
}
// Gestiona el registro del token de dispositivo
func application(
_ application: UIApplication,
didRegisterForRemoteNotificationsWithDeviceToken deviceToken: Data
) {
Joryio.shared.didRegisterForRemoteNotifications(deviceToken: deviceToken)
}
// Gestiona el fallo de registro
func application(
_ application: UIApplication,
didFailToRegisterForRemoteNotificationsWithError error: Error
) {
Joryio.shared.didFailToRegisterForRemoteNotifications(error: error)
}
// Gestiona la notificación push recibida
func application(
_ application: UIApplication,
didReceiveRemoteNotification userInfo: [AnyHashable: Any],
fetchCompletionHandler completionHandler: @escaping (UIBackgroundFetchResult) -> Void
) {
Joryio.shared.didReceiveRemoteNotification(userInfo, completionHandler: completionHandler)
}
}
2. Configurar APNS en el dashboard
Para enviar notificaciones push, configura tus credenciales de APNS:
- Ve a Configuración → Apps → [Tu app].
- Abre la pestaña Notificaciones push.
- Sube tu certificado APNS (.p12) o clave de autenticación (.p8).
- Introduce el ID de equipo y el ID de clave (para .p8).
- Selecciona el entorno (desarrollo/producción).
3. Funciones de push
// Comprueba si push está activado
let isEnabled = await Joryio.shared.isPushEnabled()
// Obtén el token de dispositivo
if let token = Joryio.shared.getDeviceToken() {
print("Device token: \(token)")
}
// Gestión de insignias
Joryio.shared.updateBadgeCount(5)
Joryio.shared.clearBadge()
// Anula el registro de push
Joryio.shared.unregisterFromPushNotifications()
Mensajería in-app
Muestra mensajes in-app segmentados a los usuarios según su comportamiento y atributos.
Tokens de entrega (delivery tokens)
Cuando el backend sirve una campaña elegible, emite también un token de entrega firmado y de corta duración. El SDK lo devuelve al informar una impresión, un clic o un cierre, y el servidor verifica la firma antes de registrar nada.
No tienes que hacer nada: el SDK se encarga por ti. Se documenta porque cambia lo que ocurre con un cliente que no envía token:
POST /v1/in-app/track (no deliveryToken)
{ "success": false, "error": "A delivery token is required" }
El token es lo que hace que una impresión sea fiable: sin él, cualquiera que tenga la SDK key - que viaja dentro de cada app y cada página - podría informar impresiones y clics de una campaña que nunca se mostró, y tus informes los contarían.
Si dejan de registrarse impresiones in-app, comprueba que la app esté compilada contra una versión actual del SDK: una compilación anterior a los tokens de entrega no envía token y el servidor rechazará sus impresiones.
Sincronización automática de campañas
El SDK sincroniza automáticamente las campañas desde el servidor cuando:
- La app pasa a primer plano.
- Se recibe una notificación push.
Las sincronizaciones tienen un límite de una por intervalo de sincronización. Llama a syncInAppCampaigns() para sincronizar en otros momentos, por ejemplo después de identificar un usuario o actualizar atributos.
Control manual de campañas
// Sincroniza manualmente las campañas desde el servidor
await Joryio.shared.syncInAppCampaigns()
// Activa manualmente la evaluación de campañas
await Joryio.shared.evaluateInAppCampaigns()
// Restablece las campañas mostradas (para pruebas)
Joryio.shared.resetDisplayedCampaigns()
Dos tipos de contenido
Cada campaña llega en una de dos formas de contenido, y el SDK renderiza cada una de manera distinta:
| Contenido | Qué es | Cómo se renderiza |
|---|---|---|
| Nativo | Datos estructurados: titular, texto, imagen, botones | Vistas UIKit reales, con el color de tinte, Dynamic Type, el modo oscuro y VoiceOver de tu app. Sin web view. |
| HTML | Marcado, CSS y JavaScript escritos por el autor | Un WKWebView dentro del mensaje. |
Permitir mensajes HTML
Los mensajes HTML están desactivados por defecto. Un mensaje HTML ejecuta JavaScript escrito por el autor dentro de tu app, así que activarlo es una decisión del equipo de la app, no algo que se enciende desde una plataforma de marketing:
let config = JoryioConfig(
inApp: InAppConfig(allowHtmlJsInAppMessages: true) // por defecto: false
)
Joryio.shared.initialize(
sdkKey: "jry_sdk_ios_YOUR_KEY",
apiHost: "api-eu1.joryio.com",
config: config
)
Dejarlo desactivado no desactiva la mensajería in-app. Los mensajes nativos siguen mostrándose, porque son datos que dibujan las propias vistas del SDK, sin ningún intérprete. Las campañas HTML se omiten y se registran.
Si tu política de seguridad prohíbe ejecutar HTML de terceros en el proceso, déjalo desactivado y crea tus campañas como mensajes nativos.
tvOS no tiene web view en absoluto, así que los mensajes HTML nunca se muestran ahí, independientemente de este ajuste. Crea mensajes nativos para destinos tvOS.
Renderizar los mensajes tú mismo
El SDK dibuja los mensajes nativos por ti, pero puedes tomar el control por
completo: la misma vía de escape que otros proveedores llaman custom view
factory. Implementa InAppMessagePresenter y asígnalo:
Joryio.shared.inAppPresenter = MyPresenter()
El descubrimiento automático solo se ejecuta cuando inAppPresenter es nil,
así que el tuyo sustituye al renderizador integrado en vez de competir con él.
Tu presenter recibe la campaña con content ya resuelto: el Liquid se renderiza
en el servidor y los campos nativos llegan como texto plano. Registra lo que
muestres con Joryio.shared.trackInAppImpression(campaignId, action:).
No es algo que configures, pero conviene saberlo cuando algo no se muestra. El SDK encuentra su renderizador integrado por nombre en tiempo de ejecución, y el módulo en el que vive ese nombre depende de cómo lo hayas integrado:
| cómo se empaqueta | clase que busca el SDK | |
|---|---|---|
| SwiftPM | JoryioUI es su propio target | JoryioUI.DefaultInAppMessagePresenter |
| CocoaPods | Joryio/UI es un subspec, y los subspecs comparten el módulo del pod | Joryio.DefaultInAppMessagePresenter |
Se prueban ambos nombres. Si nunca aparece ningún mensaje in-app, busca el aviso
"No in-app presenter found": significa que el producto de UI no está enlazado
(pod 'Joryio/UI', o el producto JoryioUI en SwiftPM), no que la campaña no
haya llegado. Desde fuera, ambos casos se ven igual.
Tu propio presenter no necesita nada de esto: asígnalo y el descubrimiento no llega a ejecutarse.
Tipos de mensaje
El SDK admite 5 tipos de mensaje:
- Modal - Centro de la pantalla con fondo atenuado
- Banner - Parte superior de la pantalla
- Slide-Up - Notificación pequeña desde abajo
- Full-Screen - Mensaje a pantalla completa
- Custom - Tu app decide la ubicación
Límites de frecuencia
Los mensajes respetan las reglas de contacto del espacio de trabajo y los límites de frecuencia de las campañas:
- Máximo de impresiones por período.
- Retraso mínimo entre mensajes.
- Límites de frecuencia por campaña.
Opciones de configuración
Personaliza el comportamiento del SDK con JoryioConfig:
JoryioConfig(
// Identificación de usuario
userId: String?, // Inicializa con un ID de usuario conocido
anonymousId: String?, // ID anónimo personalizado
// Procesamiento por lotes y rendimiento
batchSize: Int, // Predeterminado: 50
flushInterval: TimeInterval, // Predeterminado: 5,0 segundos
sendImmediately: Bool, // Predeterminado: false
maxQueueSize: Int, // Predeterminado: 1000
// Gestión de sesión
sessionTimeout: TimeInterval, // Predeterminado: 1800 (30 minutos)
trackSessionStart: Bool, // Predeterminado: true
// Almacenamiento
persistQueue: Bool, // Predeterminado: true
// Red y reintentos
maxRetries: Int, // Predeterminado: 3
retryBackoffMs: Double, // Predeterminado: 1000.0
requestTimeout: TimeInterval, // Predeterminado: 10.0
// Privacidad y RGPD
respectDoNotTrack: Bool, // Predeterminado: true
optOut: Bool, // Predeterminado: false
trackingConsent: TrackingConsent, // Predeterminado: .granted
// Depuración
enableDebug: Bool, // Predeterminado: false
logLevel: LogLevel // Predeterminado: .error
)
Funciones avanzadas
Gestión de atributos de usuario
// Establece varios atributos
Joryio.shared.setAttributes([
"age": 28,
"city": "San Francisco",
"premium": true
])
// Establece un atributo
Joryio.shared.setAttribute("language", value: "en")
// Incrementa un atributo numérico
Joryio.shared.incrementAttribute("page_views", by: 1)
Joryio.shared.incrementAttribute("total_spent", by: 29.99)
// Elimina un atributo
Joryio.shared.unsetAttribute("temporary_flag")
Controles de privacidad
// Stop collecting. PERSISTED - survives an app restart.
Joryio.shared.optOut()
// Opt back in
Joryio.shared.optIn()
// Check opt-out status
if Joryio.shared.isUserOptedOut() {
print("User has opted out")
}
// Delete everything the SDK stored on this device.
// SEPARATE from optOut(): "stop collecting" and "delete what you have" are
// different requests. This is the one an erasure request needs. It does NOT
// opt the user out - call optOut() as well if that is also intended.
Joryio.shared.wipeData()
// Get identity info
let (userId, anonymousId) = Joryio.shared.getIdentity()
print("User: \(userId ?? "anonymous"), Anonymous ID: \(anonymousId)")
What optOut() does, precisely:
| stops collection | yes - track, identify and setAttributes all become no-ops |
| survives a restart | yes - the flag is stored on the device and read before anything is collected |
| drops what is already queued | yes - queued events and un-acked attribute writes are discarded, not delivered later |
| tells the server | yes - one final $tracking_opted_out profile attribute, best-effort, sent while sending is still permitted |
| deletes stored data | no - use wipeData() |
The $tracking_opted_out attribute is a record, not enforcement: it lands on
the profile so campaigns can exclude on it. Server-side suppression is a
separate setting.
Actualizado en inglés - traducción pendiente.
Vaciado manual de la cola
// Vacía de inmediato (por ejemplo, antes de terminar la app)
Joryio.shared.flush()
// Comprueba el tamaño de la cola
let queueSize = Joryio.shared.getQueueSize()
print("Pending events: \(queueSize)")
Prácticas recomendadas
1. Rastrear eventos significativos
Céntrate en los eventos que importan para tu negocio:
// Bien: eventos específicos y accionables
Joryio.shared.track("Trial Started", properties: ["plan": "premium"])
Joryio.shared.track("Feature Used", properties: ["feature": "export"])
// Evita: eventos demasiado genéricos
Joryio.shared.track("Button Tapped") // Demasiado genérico
2. Gestionar el ciclo de vida del usuario
// Al iniciar sesión
func handleLogin(userId: String, userInfo: UserInfo) {
Joryio.shared.identify(userId)
Joryio.shared.setAttributes([
"email": userInfo.email,
"name": userInfo.name
])
}
// Al cerrar sesión
func handleLogout() {
Joryio.shared.reset()
}
// Al registrarse
func handleSignup(userId: String, userInfo: UserInfo) {
Joryio.shared.alias(userId)
Joryio.shared.identify(userId)
Joryio.shared.setAttributes(userInfo.attributes)
}
3. Vaciar ante eventos críticos
override func applicationWillTerminate(_ application: UIApplication) {
Joryio.shared.flush()
}
Solución de problemas
Los eventos no aparecen
- Comprueba que la clave de SDK sea correcta:
jry_sdk_ios_*. - Activa el registro de depuración:
enableDebug: true. - Comprueba los registros de consola en busca de errores.
- Verifica la conectividad de red.
- Llama a
flush()para enviar de inmediato.
Push no funciona
- Verifica que el certificado APNS esté cargado en el dashboard.
- Comprueba que el token de dispositivo esté registrado.
- Asegúrate de tener los entitlements correctos en Xcode.
- Prueba en el entorno adecuado (desarrollo o producción).
Errores de compilación
- Asegúrate de que el destino de despliegue sea iOS 14.0+.
- Limpia la carpeta de compilación: Cmd+Mayús+K.
- Actualiza las dependencias de Swift Package.
Rastreo de e-commerce
El SDK de iOS incluye un rastreador de e-commerce integrado para eventos de producto, carrito, checkout y pedido. Consulta la guía de rastreo de e-commerce común a todos los SDK para ver la API completa con ejemplos en Swift.
Siguientes pasos
- SDK de Android: integra el SDK de Android.
- Guía de notificaciones push: conoce las campañas push.
- Guía de campañas in-app: crea mensajes in-app.
- Eventos personalizados: rastrea eventos personalizados.
- API de e-commerce: integración de e-commerce del lado del servidor.
Attribute delivery
setAttributes is durable. A write is queued until the server acknowledges it,
so an attribute set while the device is offline is delivered when connectivity
returns rather than dropped.
- Retried on the next
setAttributes, on foreground, and before an in-app sync. - Batched - a burst of calls becomes one request (800ms window). A single write still goes out promptly.
- Persisted in the platform's encrypted store (iOS Keychain, Android
EncryptedSharedPreferences), so a write survives the process being killed.
Cleared the moment the server acks, and purged by
optOut()andwipeData().
Attributes are also used for in-app targeting. The server profile is authoritative: only writes the server has not yet acknowledged can override it, which is what keeps two devices belonging to the same contact from disagreeing about who that contact is.
Actualizado en inglés - traducción pendiente.
Push registration and consent
The SDK registers for remote notifications whether or not the user grants the notification permission. On iOS the two are separate: registering yields a device token without consent, and that token can only ever deliver background (silent) pushes - it cannot display anything the user has not authorised.
This is what lets an in-app message reach a user who declined notifications, and it means a token already exists if they later enable notifications in Settings. Airship and OneSignal behave the same way. Disclose it in your privacy policy.
In-App Messaging
Display targeted in-app messages to users based on their behavior and attributes.
Actualizado en inglés - traducción pendiente.