Comment intégrer l’API iCloud Calendar dans votre application
- Auteurs
- Nom
- Eraldo Forgoli
- Publié le
Table des matières
Utilisez une API unique pour tous les calendriers
Rejoignez notre liste d’attente pour l’API de calendrier unifiée afin d’intégrer tous les fournisseurs de calendriers avec une seule API.
Dans notre article précédent, nous avons expliqué comment intégrer l’API Google Calendar à votre application, en détaillant toutes les étapes nécessaires pour que l’intégration fonctionne.
Contrairement à Google Calendar, l’intégration du calendrier iCloud n’est pas aussi simple : la documentation est insuffisante et Apple n’a guère précisé ce que les développeurs doivent faire pour intégrer iCloud Calendar dans une application.
Dans cet article, nous allons examiner en profondeur comment intégrer l’API iCloud Calendar à votre application : authentification, opérations prises en charge, limitations et outils pouvant faciliter l’intégration.
Quels protocoles et standards le calendrier Apple iCloud utilise-t-il ?
Apple iCloud Calendar s’appuie sur le standard CalDAV pour la communication calendrier. CalDAV est une extension de WebDAV qui permet aux clients de gérer des calendriers et des événements sur un serveur.
Les événements sont représentés au format ICS (iCalendar), un format texte pour les données calendaires. Votre application peut donc communiquer avec les calendriers iCloud via HTTP en utilisant des requêtes CalDAV et des données ICS.
L’avantage est qu’il ne s’agit pas d’une implémentation spécifique à un appareil ou à un système Apple ; vous pouvez l’utiliser depuis n’importe quel serveur, quel que soit le système d’exploitation.
La mauvaise nouvelle, c’est qu’iCloud ne fournit pas d’API REST pour les calendriers : CalDAV est donc la seule façon d’intégrer iCloud Calendar à votre application.
Nous recommandons une intégration serveur-à-serveur ; c’est pourquoi nous affirmons que CalDAV est la seule voie possible.
Comment s’authentifier auprès d’iCloud ?
Habituellement, pour intégrer une plateforme dans votre application (par exemple Google Calendar), vous devez :
Créer un compte développeur sur la plateforme.
Créer une application, configurer les scopes, ajouter des utilisateurs de test, renseigner les informations de l’app, puis soumettre pour validation.
Après approbation, rediriger l’utilisateur final vers l’écran OAuth de la plateforme, où il se connecte et accorde les accès demandés.
iCloud ne fonctionne pas ainsi : il n’existe pas de flux OAuth standard comme pour Google Calendar ou Outlook. Pour connecter le calendrier iCloud d’un utilisateur, vous devez l’authentifier avec son Apple ID via Basic Auth sur SSL. Comme la plupart des comptes iCloud utilisent l’authentification à deux facteurs, l’utilisateur doit créer un mot de passe spécifique à l’app dans les réglages de son compte Apple ID, au lieu d’utiliser son mot de passe principal.
Votre application demandera donc l’adresse iCloud/Apple ID de l’utilisateur et ce mot de passe spécifique à l’app (16 caractères). Avec ces identifiants, vous pourrez vous connecter au service CalDAV d’iCloud.
Exemple de demande d’adresse e-mail et de mot de passe spécifique à l’app dans votre application.
Outre les raisons évoquées ci-dessus, Apple impose ces mots de passe spécifiques pour renforcer la sécurité ; partager son mot de passe iCloud avec une application tierce n’est clairement pas recommandé.
Sous le capot, l’en-tête de votre requête HTTP inclura un mot de passe encodé en Base64 : Authorization: Basic <mot-de-passe-spécifique-à-l’app>
Quelles méthodes l’API iCloud Calendar prend-elle en charge ?
Le service CalDAV d’iCloud est hébergé sur caldav.icloud.com
. Après authentification, les méthodes suivantes sont disponibles :
Liste des calendriers de l’utilisateur
CRUD sur les événements
Récupération d’événements spécifiques
Pour en savoir plus sur CalDAV, consultez la RFC 4791, qui décrit toutes les méthodes et filtres disponibles.
Voici les informations essentielles pour votre intégration iCloud Calendar :
Verbes HTTP pris en charge
Verbe HTTP | Fonction dans CalDAV | Support iCloud | Particularités |
---|---|---|---|
OPTIONS | Découvre les capacités du serveur | ✅ | Utile pour le débogage ; non requis en production. |
PROPFIND | Recherche de principals, calendar-home-set , liste les calendriers, récupère des propriétés | ✅ | Authentification préalable obligatoire ; utiliser Depth 0 ou 1. |
MKCALENDAR | Crée une nouvelle collection de calendrier | ✅ | Droits d’écriture requis ; voir section 5.3.1. |
REPORT | Interroge des données (calendar-query , calendar-multiget , free-busy-query ) | ✅ | Les trois rapports sont obligatoires et disponibles sur iCloud. |
PUT | Téléverse / remplace une ressource .ics (événement/tâche) | ✅ | Nécessite un VCALENDAR complet ; pas de PATCH. |
DELETE | Supprime un événement ou un calendrier | ✅ | Associer à If-Match ETag pour la sécurité. |
COPY / MOVE | Copie ou déplace des événements entre calendriers | ✅ | Soumis aux mêmes pré-conditions que PUT. |
GET | Récupère une ressource .ics unique | ✅ | Retourne text/calendar et ETag. |
Propriétés d’une collection de calendriers
Propriété | Utilité | Notes spécifiques à iCloud |
---|---|---|
CALDAV:calendar-description | Description lisible | Entièrement pris en charge |
CALDAV:calendar-timezone | Fuseau horaire par défaut pour les requêtes | Pris en charge |
CALDAV:supported-calendar-component-set | Composants acceptés (VEVENT, VTODO) | Événements et tâches dans des calendriers séparés |
CALDAV:supported-calendar-data | MIME/version autorisée (text/calendar 2.0) | Valeur par défaut iCloud |
CALDAV:max-resource-size | Taille maximale par événement | Environ 20 Mo sur iCloud |
CALDAV:min/max-date-time , max-instances , max-attendees-per-instance | Limites serveur diverses | Respecter pour éviter erreurs 403/507 |
L’API OneCal Unified Calendar est presque prête ; inscrivez-vous à la liste d’attente pour être informé du lancement et profiter des promotions.
Quelles bibliothèques peuvent simplifier l’intégration ?
Manipuler CalDAV, ICS, XML et les spécificités d’iCloud est fastidieux : il faut comprendre chaque méthode, convertir le XML en JSON, etc.
Pour une intégration plus fluide, nous recommandons :
tsdav
: indispensable en JavaScript/TypeScript. Fournit une API TS de haut niveau qui encapsule les verbes HTTP et le XML (PROPFIND, REPORT, MKCALENDAR, PUT, DELETE, …). Voir la documentation tsdav.ical-generator
: iCloud impose de téléverser des fichiers .ics complets lors de la création ou de la mise à jour d’événements. Cette bibliothèque génère correctement les fichiers (UID, DTSTART/DTEND, RRULE, time-zones, etc.).ical.js
: moteur JavaScript pur (Mozilla) pour analyser les réponses ICS et les convertir en classes JS.
Tableau récapitulatif du rôle de tsdav :
Rôle dans la pile | Ce que fait tsdav | Pourquoi c’est important pour iCloud |
---|---|---|
Client CalDAV / WebDAV | API TypeScript haut niveau encapsulant verbes HTTP et XML | Focalisez-vous sur la logique métier au lieu de gérer du XML brut. |
Aides à la découverte | createDAVClient() suit automatiquement la découverte CalDAV (caldav.icloud.com → principal utilisateur → calendar-home-set → URL pXX-caldav.icloud.com ) | Supprime le boilerplate propre à iCloud. |
Enveloppes d’authentification | Helpers intégrés Basic / OAuth 2 ; pour iCloud : { username, password, authMethod: 'Basic' } | Pas besoin d’encoder Base64 ni d’ajouter les en-têtes manuellement. |
Helpers typés CRUD | fetchCalendars() , createCalendarObject() , etc., renvoient des objets JS plutôt que du XML | Implémentation rapide du CRUD sans s’occuper de la syntaxe RFC 4791. |
Support des jetons de synchro | syncCollection() encapsule REPORT sync-collection , ne renvoyant que les changements | Implémentez le polling iCloud (pas de push) en une ligne. |
Compatibilité navigateur + Node | Fonctionne côté serveur ou navigateur (fetch isomorphe) | Utile pour extensions ou SPA. |
Projet TypeScript moderne | Types complets, modules ES tree-shakables, dépendances minimes | Intégration facile dans les pipelines modernes. |
tsdav ne génère pas de contenu iCalendar. Utilisez-le avec ical-generator
(ou une autre bibliothèque ICS) pour construire les chaînes d’événement.
Exemple d’intégration iCloud Calendar avec tsdav + TypeScript
Dans cet exemple, on suppose que vous avez déjà obtenu l’e-mail de l’utilisateur et son mot de passe spécifique à l’app.
createClient
: création du client DAV
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;
}
constants
: fichier de constantes
export const PROD_ID = {
company: "your-company-name-here",
product: "your-product-name-here",
};
getCalendars
: récupérer tous les calendriers
async getCalendars(
...params: Parameters<typeof DAVClient.prototype.fetchCalendars>
): Promise<DAVCalendar[]> {
// Vous pouvez abstraire cette initialisation ailleurs. const client = createClient({
username: <email-here>,
password: <password-here>,
});
return client.fetchCalendars(...params);
}
getCalendarById
: récupérer un calendrier par ID
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 with id ${calendarUrl} not found`);
}
return calendar;
}
getCalendarEvents
: lister les événements
async getCalendarEvents(
calendarUrl: string,
query: GetCalendarEventsQuery = {}
) {
const client = createClient({
username: <email-here>,
password: <password-here>,
});
const calendar = await getCalendarById(calendarUrl);
const events = await client.fetchCalendarObjects({
calendar,
timeRange: query.dateRange ?? undefined,
});
return { events, nextSyncToken: calendar.syncToken };
}
getEventById
: récupérer un événement par ID
async getEventById(calendarUrl: string, eventId: string) {
const client = createClient({
username: <email-here>,
password: <password-here>,
});
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(`Received no response while fetching ${eventUrl}`, null);
} else if (responses[0].status >= 400) {
throw new Error(
`Failed to get Apple event by id. Status: ${responses[0].statusText}`,
responses[0]
);
}
const response = responses[0];
const calendarObject: DAVObject = {
url: new URL(response.href ?? "", calendarUrl).href,
etag: `${response.props?.getetag}`,
data:
response.props?.calendarData?._cdata ?? response.props?.calendarData,
};
try {
return calendarObject;
} catch (err: any) {
this.logger.error("Failed to process Apple Response", {
message: err.message,
event: calendarObject,
});
return [];
}
}
createEvent
: créer un événement
async createEvent(calendarUrl: string, data: AppleEvent) {
const client = createClient({
username: <email-here>,
password: <password-here>,
});
const eventId = data.id ?? <generate-id-here>;
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(
`Failed to create Apple event: ${response.statusText}`,
response
);
}
return { id: event.id(), eventWithExceptions };
}
updateEvent
: mettre à jour un événement
async updateEvent(calendarUrl: string, eventId: string, data: AppleEvent) {
const client = createClient({
username: <email-here>,
password: <password-here>,
});
const originalEventData = await getEventById(calendarUrl, eventId);
const calendar = ical({
prodId: PROD_ID,
method: ICalCalendarMethod.REQUEST,
});
for (let event of originalEventData) {
if (event.status === ICalEventStatus.CANCELLED) continue;
if (event.id === eventId) {
calendar.createEvent({ ...event, ...data, id: eventId, url: null });
} else {
calendar.createEvent({ ...event, 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(
`Failed to update Apple event: ${response.statusText}`,
response
);
}
return getEventById(calendarUrl, eventId);
}
deleteEvent
: supprimer un événement
async deleteEvent(calendarUrl: string, eventId: string) {
const client = createClient({
username: <email-here>,
password: <password-here>,
});
const calendarObjectUrl = new URL(`${eventId}.ics`, calendarUrl);
const response = await client.deleteCalendarObject({
calendarObject: { url: calendarObjectUrl.href },
});
if (!response.ok) {
throw new Error(
`Failed to delete Apple event: ${response.statusText}`,
response
);
}
return { id: eventId };
}
Quelles limites présente iCloud Calendar ?
Basic Auth avec mot de passe spécifique à l’app : iCloud n’applique pas OAuth 2.0 ; il faut donc utiliser ce mot de passe spécifique.
Pas de webhooks/notifications push : contrairement à Google Calendar ou Outlook, iCloud n’offre pas la meilleure API calendrier. Impossible de s’abonner aux mises à jour ; il faut recourir au polling périodique et au rapport
sync-collection
.Absence de méthode PATCH : impossible de mettre à jour partiellement un événement ; il faut utiliser PUT complet.
Aucun contrôle sur les invitations : iCloud gère automatiquement les invitations. Si vous créez/modifiez un événement avec des participants, iCloud envoie les invitations ; impossible de contrôler manuellement via Outbox/Inbox CalDAV.
Existe-t-il une solution plus simple pour intégrer iCloud Calendar ?
Intégrer iCloud Calendar est complexe : conventions différentes, méthodes CalDAV inopérantes, etc.
Une approche plus simple est d’utiliser une Unified Calendar API.
Avantages d’une Unified Calendar API
Intégration iCloud via une API documentée et testée conforme aux standards modernes.
Moins de temps de développement et de maintenance : l’API gère déjà clients, cas limites, etc.
Intégration d’autres fournisseurs (Google, Outlook, …) sans effort supplémentaire.
Support des notifications push/webhooks sans devoir coder votre propre polling.
Intégrez iCloud Calendar grâce à une Unified Calendar API
Nous finalisons notre OneCal Unified Calendar API, qui vous apportera tous les avantages cités.
Rejoignez la liste d’attente pour être informé du lancement et profiter des réductions de lancement.