Cómo integrar la API de iCloud Calendar en tu aplicación
- Autores
- Nombre
- Eraldo Forgoli
- Publicado el
Tabla de Contenidos
Usa una sola API para todos los calendarios
Únete a nuestra lista de espera de la API de Calendario Unificada para integrar todos los proveedores de calendarios usando una sola API.
En nuestro artículo anterior, explicamos cómo integrar la API de Google Calendar en tu aplicación, resaltando todos los pasos necesarios para que la integración funcione.
A diferencia de Google Calendar, integrar iCloud Calendar no es tan sencillo, ya que la documentación es escasa y Apple no ha dedicado tanto esfuerzo a detallar lo que un desarrollador debe hacer para integrar iCloud Calendar en una aplicación.
En este artículo profundizaremos en cómo integrar la API de iCloud Calendar en tu aplicación, incluyendo la autenticación, las operaciones compatibles, las limitaciones y las herramientas que puedes usar para facilitar la integración.
¿Qué protocolos y estándares utiliza Apple iCloud Calendar?
Apple iCloud Calendar utiliza el estándar CalDAV para la comunicación de calendarios. CalDAV es una extensión de WebDAV que permite a los clientes gestionar calendarios y eventos en un servidor.
Los eventos se representan en el formato ICS (iCalendar), que es un formato de texto para datos de calendario. Esto significa que tu aplicación puede comunicarse con los calendarios de iCloud a través de HTTP usando solicitudes CalDAV y datos ICS.
La ventaja es que no se trata de una implementación específica de dispositivos u sistemas operativos de Apple; puedes usarla desde cualquier servidor que se ejecute en cualquier plataforma.
La mala noticia es que iCloud no proporciona una API REST para calendarios, por lo que CalDAV es la única forma de integrar iCloud Calendar en tu aplicación.
Recomendamos que la integración sea de servidor a servidor; por eso decimos que CalDAV es el único camino a seguir.
¿Cómo se autentica en iCloud?
Normalmente, cuando quieres integrar una plataforma en tu aplicación (Google Calendar, por ejemplo), debes crear una cuenta de desarrollador en esa plataforma, crear una aplicación, configurar alcances, añadir usuarios de prueba, completar la información de tu app y enviarla para aprobación.
Tras seguir todos estos pasos y obtener la aprobación, puedes dirigir al usuario final a la pantalla de OAuth de la plataforma, donde el usuario debe iniciar sesión en esa plataforma y conceder acceso explícito al alcance que solicita tu aplicación. El usuario final también ve el nombre de tu aplicación y toda la información que proporcionaste al configurar tu aplicación en esa plataforma.
Apple iCloud no funciona así, ya que iCloud no tiene un flujo OAuth estándar como Google Calendar u Outlook. Para conectar el iCloud Calendar de un usuario, debes autenticarte con su Apple ID mediante Basic Authentication sobre SSL. Debido a que la mayoría de las cuentas de iCloud tienen autenticación de dos factores, el usuario debe crear una contraseña específica de la app para tu aplicación desde la configuración de su Apple ID, en lugar de usar su contraseña principal de Apple.
Tu aplicación solicitará al usuario su correo/iCloud ID y esta contraseña específica de la app de 16 caracteres. Con estas credenciales, podrás conectarte al servicio CalDAV de iCloud.
Ejemplo de cómo tu aplicación pediría al usuario que introduzca la dirección de correo y la contraseña específica de la app.
Además de los motivos mencionados, Apple exige contraseñas específicas de la app para el acceso de terceros al calendario a fin de mejorar la seguridad, ya que compartir tu contraseña de iCloud con una aplicación de terceros no es lo ideal.
En segundo plano, la cabecera de tu solicitud HTTP incluirá la contraseña específica de la app codificada en Base64, de la siguiente forma:Authorization: Basic <app-specific-password-here>
¿Qué métodos admite la API de iCloud Calendar?
El servicio CalDAV de iCloud se aloja en caldav.icloud.com
. Tras autenticarte, están disponibles los siguientes métodos:
Listar los calendarios de un usuario
CRUD de eventos
Obtener eventos específicos
Para obtener más información sobre el estándar CalDAV, lee RFC 4791, que explica todos los métodos disponibles, filtros y más.
A continuación, resumo la información más importante que necesitas para tu integración con iCloud Calendar.
Verbos HTTP compatibles
Verbo HTTP | Qué hace en CalDAV | Compatibilidad iCloud | Observaciones |
---|---|---|---|
OPTIONS | Descubre las capacidades del servidor | ✅ | Útil para depurar; no requerido en producción. |
PROPFIND | Busca principals, calendar-home-set , lista calendarios, obtiene propiedades | ✅ | Autentícate antes; usa Depth 0 o 1. |
MKCALENDAR | Crea una nueva colección de calendario | ✅ | Requiere permisos de escritura; ver Sección 5.3.1. |
REPORT | Consulta datos (calendar-query , calendar-multiget , free-busy-query ) | ✅ | Los tres informes son obligatorios y están presentes en iCloud. |
PUT | Sube/reemplaza un recurso .ics (evento/tarea) | ✅ | Debes enviar un VCALENDAR completo; no hay PATCH. |
DELETE | Elimina un evento o calendario | ✅ | Usa If-Match con ETag para mayor seguridad. |
COPY / MOVE | Copia o mueve eventos entre calendarios | ✅ | Sujetos a las mismas precondiciones de PUT. |
GET | Obtiene un recurso .ics individual | ✅ | Devuelve text/calendar y ETag. |
Propiedades de la colección de calendarios
Propiedad | Propósito | Notas específicas de iCloud |
---|---|---|
CALDAV:calendar-description | Descripción legible | Totalmente compatible |
CALDAV:calendar-timezone | Zona horaria predeterminada para consultas | Compatible |
CALDAV:supported-calendar-component-set | Componentes aceptados (VEVENT, VTODO) | Eventos y tareas se guardan en calendarios separados |
CALDAV:supported-calendar-data | MIME/versión permitida (text/calendar 2.0) | Predeterminado |
CALDAV:max-resource-size | Tamaño máximo por evento | Límite aproximado de 20 MB |
CALDAV:min/max-date-time , max-instances , max-attendees-per-instance | Límites adicionales del servidor | Respétalos para evitar errores 403/507 |
La OneCal Unified Calendar API está casi lista; únete a nuestra lista de espera para que te avisemos cuando la lancemos y aproveches las promociones de lanzamiento.
¿Qué bibliotecas puedes usar para simplificar la integración?
Trabajar con CalDAV, ICS, XML y otros detalles propios de iCloud Calendar no resulta ideal, ya que dedicarás mucho tiempo a entender cada método, convertir XML a JSON y usarlo en tu aplicación.
Para una integración más sencilla, recomendamos las siguientes bibliotecas:
tsdav
: Imprescindible si empleas JavaScript/TypeScript. Te permite comunicarte con el servidor de iCloud sin manejar directamente la sintaxis CalDAV. Proporciona una API de alto nivel que envuelve los verbos HTTP y el XML que de otra forma escribirías a mano. Consulta la documentación de tsdav.ical-generator
: Al integrar a través de CalDAV, necesitas subir y reemplazar archivos .ics completos al crear o actualizar eventos. ical-generator facilita la creación correcta de dichos archivos.ical.js
: Parser/engine JavaScript puro creado por Mozilla Calendar para convertir respuestas ICS a clases JS.
Papel de tsdav en la integración con iCloud Calendar
Rol en la pila | Qué hace tsdav | Por qué importa en iCloud |
---|---|---|
Cliente CalDAV/WebDAV | Proporciona una API TypeScript de alto nivel que envuelve los verbos HTTP y el XML necesario. | Evita generar XML manualmente y parsear respuestas multistatus. |
Helpers de descubrimiento | createDAVClient() sigue el flujo de descubrimiento: contacta caldav.icloud.com , encuentra el principal y resuelve calendar-home-set . | Elimina el boilerplate del proceso de descubrimiento propio de iCloud. |
Wrappers de autenticación | Soporta Basic y OAuth 2. Para iCloud se usa { username, password, authMethod: 'Basic' } . | No necesitas codificar Base64 ni añadir cabeceras manualmente. |
Helpers tipados para CRUD | Métodos como fetchCalendars() , createCalendarObject() manejan objetos JS planos. | Implementa CRUD rápido sin preocuparte por la sintaxis XML de la RFC 4791. |
Soporte de sync token | syncCollection() envuelve el REPORT sync-collection y devuelve solo cambios/eliminaciones. | Permite sondear iCloud (no hay push) con una sola línea. |
Compatibilidad Browser + Node | Funciona en Node y navegador gracias a fetch isomórfico. | Útil si parte de tu app vive en una extensión o SPA. |
Proyecto TS moderno | Tipos completos, módulos ES tree-shakable y pocas dependencias. | Fácil de integrar en pipelines modernos. |
tsdav no genera iCalendar. Úsalo junto con ical-generator
(o tu generador favorito) para crear el contenido del evento.
Ejemplo de integración de iCloud Calendar usando tsdav + TypeScript
En este ejemplo se asume que ya has obtenido el nombre de usuario y la contraseña específica de la app del usuario.
Método createClient
que crea el DAVClient
import { DAVCalendar, DAVClient, DAVNamespaceShort, DAVObject } from "tsdav";
const APPLE_DAV_URL = "https://caldav.icloud.com";
function createClient({
username,
password,
}: {
username: string;
password: string;
}) {
const client = new DAVClient({
serverUrl: APPLE_DAV_URL,
credentials: { username, password },
authMethod: "Basic",
defaultAccountType: "caldav",
});
return client;
}
Archivo constants
export const PROD_ID = {
company: "your-company-name-here",
product: "your-product-name-here",
};
Método getCalendars
para obtener todos los calendarios
async getCalendars(
...params: Parameters<typeof DAVClient.prototype.fetchCalendars>
): Promise<DAVCalendar[]> {
// Para simplicidad, inicializamos el cliente en cada llamada const client = createClient({
username: "<correo>",
password: "<contraseña>",
});
return client.fetchCalendars(...params);
}
Método getCalendarById
async getCalendarById(calendarUrl: string): Promise<DAVCalendar> {
const calendars = await getCalendars();
const calendar = calendars.find((el) => el.url === calendarUrl);
if (!calendar) {
throw new Error(`Apple Calendar con id ${calendarUrl} no encontrado`);
}
return calendar;
}
Método getCalendarEvents
para listar eventos
async getCalendarEvents(
calendarUrl: string,
query: GetCalendarEventsQuery = {}
) {
const client = createClient({
username: "<correo>",
password: "<contraseña>",
});
const calendar = await getCalendarById(calendarUrl);
const events = await client.fetchCalendarObjects({
calendar,
timeRange: query.dateRange ?? undefined,
});
return { events, nextSyncToken: calendar.syncToken };
}
Método getEventById
async getEventById(calendarUrl: string, eventId: string) {
const client = createClient({
username: "<correo>",
password: "<contraseña>",
});
const eventUrl = new URL(`${eventId}.ics`, calendarUrl).pathname;
const responses = await client.calendarMultiGet({
url: calendarUrl,
props: {
[`${DAVNamespaceShort.DAV}:getetag`]: {},
[`${DAVNamespaceShort.CALDAV}:calendar-data`]: {},
},
objectUrls: [eventUrl],
depth: "1",
});
if (responses.length === 0) {
throw new Error(`Sin respuesta al obtener ${eventUrl}`);
} else if (responses[0].status >= 400) {
throw new Error(
`Error al obtener evento. Estado: ${responses[0].statusText}`
);
}
const res = responses[0];
const calendarObject: DAVObject = {
url: new URL(res.href ?? "", calendarUrl).href,
etag: `${res.props?.getetag}`,
data: res.props?.calendarData?._cdata ?? res.props?.calendarData,
};
return calendarObject;
}
Método createEvent
async createEvent(calendarUrl: string, data: AppleEvent) {
const client = createClient({
username: "<correo>",
password: "<contraseña>",
});
const eventId = data.id ?? "<genera-id>";
const calendar = ical({
prodId: PROD_ID,
method: ICalCalendarMethod.REQUEST,
});
const event = calendar.createEvent({ ...data, id: eventId });
const response = await client.createCalendarObject({
calendar: { url: calendarUrl },
filename: `${event.id()}.ics`,
iCalString: calendar.toString(),
});
if (!response.ok) {
throw new Error(
`Error al crear evento de Apple: ${response.statusText}`
);
}
return { id: event.id(), eventWithExceptions };
}
Método updateEvent
async updateEvent(calendarUrl: string, eventId: string, data: AppleEvent) {
const client = createClient({
username: "<correo>",
password: "<contraseña>",
});
const originalData = await getEventById(calendarUrl, eventId);
const calendar = ical({
prodId: PROD_ID,
method: ICalCalendarMethod.REQUEST,
});
for (const ev of originalData) {
if (ev.status === ICalEventStatus.CANCELLED) continue;
if (ev.id === eventId) {
calendar.createEvent({ ...ev, ...data, id: eventId, url: null });
} else {
calendar.createEvent({ ...ev, id: eventId, url: null });
}
}
const calendarObjectUrl = new URL(`${eventId}.ics`, calendarUrl);
const response = await client.updateCalendarObject({
calendarObject: {
url: calendarObjectUrl.href,
data: calendar.toString(),
},
});
if (!response.ok) {
throw new Error(
`Error al actualizar evento: ${response.statusText}`
);
}
return getEventById(calendarUrl, eventId);
}
Método deleteEvent
async deleteEvent(calendarUrl: string, eventId: string) {
const client = createClient({
username: "<correo>",
password: "<contraseña>",
});
const calendarObjectUrl = new URL(`${eventId}.ics`, calendarUrl);
const response = await client.deleteCalendarObject({
calendarObject: { url: calendarObjectUrl.href },
});
if (!response.ok) {
throw new Error(
`Error al eliminar evento: ${response.statusText}`
);
}
return { id: eventId };
}
¿Qué limitaciones tiene Apple iCloud Calendar?
Basic Auth con contraseña específica de la app: iCloud no sigue OAuth 2.0; debes usar una contraseña específica.
Sin webhooks/notificaciones push: No puedes registrar webhooks; debes sondear y usar
sync-collection
.Sin soporte PATCH: No se pueden actualizar eventos parcialmente; debes hacer un PUT completo.
Sin control de invitaciones: iCloud gestiona automáticamente las invitaciones y sus actualizaciones.
¿Existe una forma más sencilla de integrar iCloud Calendar?
Integrar iCloud Calendar es complejo y no sigue convenciones estándar. Una opción más simple es usar una Unified Calendar API.
Ventajas de una Unified Calendar API:
Integra iCloud Calendar con una API moderna y documentada.
Menos tiempo de desarrollo y mantenimiento: la API maneja casos límite y clientes.
Añade otros proveedores (Google, Outlook) sin esfuerzo adicional.
Webhooks/push soportados sin implementar sondeo propio.
Usa una Unified Calendar API para integrar iCloud Calendar
Estamos a punto de lanzar la OneCal Unified Calendar API, que te proporcionará todos los beneficios mencionados.
Únete a nuestra lista de espera para que te avisemos en cuanto lancemos y aproveches los descuentos de lanzamiento.