Saltar al contenido principal

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
¿Cuál elegir?

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:

  1. Archivo → Añadir dependencias de paquetes.
  2. Escribe https://github.com/joryio/joryio-ios.git.
  3. 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
}
Encontrar tu clave de SDK

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
  • $timezone
  • country (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:

  1. Ve a Configuración → Apps → [Tu app].
  2. Abre la pestaña Notificaciones push.
  3. Sube tu certificado APNS (.p12) o clave de autenticación (.p8).
  4. Introduce el ID de equipo y el ID de clave (para .p8).
  5. 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:

ContenidoQué esCómo se renderiza
NativoDatos estructurados: titular, texto, imagen, botonesVistas UIKit reales, con el color de tinte, Dynamic Type, el modo oscuro y VoiceOver de tu app. Sin web view.
HTMLMarcado, CSS y JavaScript escritos por el autorUn 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

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:).

El nombre de la clase cambia según el gestor de paquetes

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 empaquetaclase que busca el SDK
SwiftPMJoryioUI es su propio targetJoryioUI.DefaultInAppMessagePresenter
CocoaPodsJoryio/UI es un subspec, y los subspecs comparten el módulo del podJoryio.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:

  1. Modal - Centro de la pantalla con fondo atenuado
  2. Banner - Parte superior de la pantalla
  3. Slide-Up - Notificación pequeña desde abajo
  4. Full-Screen - Mensaje a pantalla completa
  5. 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 collectionyes - track, identify and setAttributes all become no-ops
survives a restartyes - the flag is stored on the device and read before anything is collected
drops what is already queuedyes - queued events and un-acked attribute writes are discarded, not delivered later
tells the serveryes - one final $tracking_opted_out profile attribute, best-effort, sent while sending is still permitted
deletes stored datano - 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.

nota

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

  1. Comprueba que la clave de SDK sea correcta: jry_sdk_ios_*.
  2. Activa el registro de depuración: enableDebug: true.
  3. Comprueba los registros de consola en busca de errores.
  4. Verifica la conectividad de red.
  5. Llama a flush() para enviar de inmediato.

Push no funciona

  1. Verifica que el certificado APNS esté cargado en el dashboard.
  2. Comprueba que el token de dispositivo esté registrado.
  3. Asegúrate de tener los entitlements correctos en Xcode.
  4. Prueba en el entorno adecuado (desarrollo o producción).

Errores de compilación

  1. Asegúrate de que el destino de despliegue sea iOS 14.0+.
  2. Limpia la carpeta de compilación: Cmd+Mayús+K.
  3. 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

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() and wipeData().

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.

nota

Actualizado en inglés - traducción pendiente.

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.

nota

Actualizado en inglés - traducción pendiente.