So integrieren Sie die iCloud Calendar API in Ihre App

Autoren
Veröffentlicht am

In unserem vorherigen Artikel haben wir erläutert, wie Sie die Google Calendar API in Ihre App integrieren und dabei alle nötigen Schritte zur erfolgreichen Integration aufgezeigt.

Im Gegensatz zu Google Calendar ist die Integration des iCloud-Kalenders nicht so geradlinig, da es kaum Dokumentation gibt und Apple nicht genau darlegt, was Entwickler tun müssen, um iCloud Calendar in eine Anwendung einzubinden.

In diesem Artikel tauchen wir tief in die Integration der iCloud Calendar API ein – einschließlich Authentifizierung, unterstützter Funktionen, Einschränkungen und Tools, die die Integration vereinfachen.

Welche Protokolle und Standards verwendet Apple iCloud Calendar?

Apple iCloud Calendar nutzt den CalDAV-Standard (eine Erweiterung von WebDAV) für die Kalenderkommunikation.

Ereignisse werden im ICS- (iCalendar-) Format gespeichert – einem textbasierten Format für Kalenderdaten. Ihre Anwendung kann also via HTTP mit CalDAV-Anfragen und ICS-Daten mit iCloud-Kalendern kommunizieren.

Vorteil: Die Implementierung ist nicht an Apple-Geräte oder -Betriebssysteme gebunden und kann auf jedem Server unter jedem Betriebssystem eingesetzt werden.

Nachteil: iCloud stellt keine REST-API für Kalender bereit; CalDAV ist daher der einzige Weg, iCloud Calendar in Ihre App einzubinden.

Wir empfehlen eine reine Server-zu-Server-Integration – deshalb führt an CalDAV kein Weg vorbei.

Wie authentifiziert man sich bei iCloud?

Normalerweise legt man bei einer Plattform (z. B. Google Calendar) ein Entwicklerkonto an, erstellt eine App, konfiguriert Scopes, fügt Testnutzer hinzu, trägt App-Informationen ein und lässt alles genehmigen. Danach leitet man den Endnutzer über den OAuth-Dialog zur Einwilligung.

Bei Apple iCloud funktioniert das anders: iCloud bietet keinen standardisierten OAuth-Flow wie Google Calendar oder Outlook. Um den Kalender eines Nutzers zu verbinden, müssen Sie dessen Apple-ID über Basic Authentication (SSL) ansprechen. Da die meisten iCloud-Konten die Zwei-Faktor-Authentifizierung aktiviert haben, muss der Nutzer in seinen Apple-ID-Einstellungen ein app-spezifisches Passwort erstellen, statt sein Hauptpasswort zu nutzen.

Ihre Anwendung fragt also die iCloud-E-Mail/Apple-ID sowie dieses 16-stellige app-spezifische Passwort ab. Mit diesen Daten verbinden Sie sich mit dem iCloud-CalDAV-Dienst.

Beispiel, wie Ihre App E-Mail-Adresse und app-spezifisches Passwort abfragt.

apple-enter-app-specific-password.webp

Apple verlangt app-spezifische Passwörter, weil das Weitergeben des Hauptpassworts an Drittanbieter unsicher wäre.

Im HTTP-Header Ihrer Anfrage steht dann:

Authorization: Basic <app-specific-password-here>

Welche Methoden unterstützt die iCloud Calendar API?

Der iCloud-CalDAV-Dienst ist unter caldav.icloud.com erreichbar. Nach der Authentifizierung stehen diese Operationen bereit:

  • Auflisten der Kalender eines Nutzers

  • CRUD-Operationen für Ereignisse

  • Abrufen einzelner Ereignisse

Weitere Details finden Sie in RFC 4791. Nachfolgend eine Zusammenfassung der wichtigsten Punkte für Ihre iCloud-Integration.

Unterstützte HTTP-Verben

HTTP-VerbFunktion in CalDAViCloud-UnterstützungBesonderheiten
OPTIONSServer-Fähigkeiten ermittelnGut zum Debuggen; zur Laufzeit meist nicht nötig
PROPFINDPrincipals ermitteln, calendar-home-set abrufen, Kalender auflisten, Eigenschaften lesenAuthentifizierung erforderlich; Depth 0 oder 1
MKCALENDARNeue Kalender-Collection anlegenSchreibrechte erforderlich; siehe Abschnitt 5.3.1
REPORTDaten abfragen (calendar-query, calendar-multiget, free-busy-query)Alle drei Reports werden unterstützt
PUTEine .ics-Ressource (Ereignis/Aufgabe) hochladen/ersetzenKomplettes VCALENDAR senden; kein PATCH
DELETEEreignis oder Kalender löschenMit If-Match-ETag kombinieren
COPY / MOVEEreignisse zwischen Kalendern kopieren/verschiebenGleiche Voraussetzungen wie PUT
GETEine .ics-Ressource abrufenLiefert text/calendar und ETag

Eigenschaften einer Kalender-Collection

EigenschaftZweckiCloud-spezifische Hinweise
CALDAV:calendar-descriptionMenschlich lesbare BeschreibungVollständig unterstützt
CALDAV:calendar-timezoneStandard-Zeitzone für AbfragenUnterstützt
CALDAV:supported-calendar-component-setAkzeptierte Komponenten (VEVENT, VTODO)Ereignisse und Aufgaben liegen in separaten Kalendern
CALDAV:supported-calendar-dataErlaubte MIME/Version (text/calendar 2.0)Standardwert bei iCloud
CALDAV:max-resource-sizeMax. Größe pro EreignisiCloud-Limit ca. 20 MB
CALDAV:min/max-date-time, max-instances, max-attendees-per-instanceDiverse ServerlimitsEinhaltung verhindert 403/507-Fehler

Die OneCal Unified Calendar API steht kurz vor dem Start – tragen Sie sich in unsere Warteliste ein, um informiert zu werden und von Einführungsrabatten zu profitieren.

Welche Bibliotheken erleichtern die Integration?

CalDAV, ICS, XML und iCloud-Spezifika manuell umzusetzen ist aufwendig. Wir empfehlen:

Rolle im StackWas tsdav machtWarum das für iCloud wichtig ist
CalDAV/WebDAV-ClientBietet eine High-Level-TypeScript-API, die alle HTTP-Verben und XML-Operationen kapseltFokus auf Business-Logik statt auf rohe XML-Generierung
Discovery-HelfercreateDAVClient() führt automatisch den iCloud-Discovery-Flow aus: Verbindung zu caldav.icloud.com, Principal ermitteln, calendar-home-set auflösen, endgültige Basis-URL speichernSpart Boilerplate für die zweistufige Discovery
Auth-WrapperUnterstützt Basic und OAuth 2 (für iCloud: { username, password, authMethod: 'Basic' })Kein manuelles Base64-Kodieren der Zugangsdaten
Typisierte CRUD-HelferfetchCalendars(), fetchCalendarObjects(), createCalendarObject(), updateCalendarObject(), deleteCalendarObject() liefern/akzeptieren reine JS-ObjekteSchnelle CRUD-Implementierung ohne RFC-4791-XML
Sync-Token-SupportsyncCollection() kapselt den REPORT sync-collection, verfolgt Tokens und liefert nur Änderungen/LöschungenEffizientes Polling (iCloud hat kein Push)
Browser + NodeLäuft in Node und im Browser dank isomorphic fetchNützlich bei Browser-Extensions oder SPAs
Modernes TS-ProjektVollständige Typdefinitionen, tree-shakable ES-Module, minimale AbhängigkeitenLeicht in moderne Build-Pipelines einzubinden

tsdav erstellt keine iCalendar-Dateien. Nutzen Sie dazu ical-generator (oder eine andere ICS-Bibliothek) zur Erstellung der Event-Payloads.

Weitere nützliche Bibliotheken:

  • ical-generator – Erstellt komplette .ics-Dateien mitsamt Headern, UID, RRULE, Zeitzonen usw.

  • ical.js – Reine JavaScript-Engine (Mozilla-Projekt) zum Parsen von ICS-Antworten in JS-Objekte.

Beispiel: iCloud-Integration mit tsdav + TypeScript

In diesem Beispiel haben Sie bereits den Benutzernamen und das app-spezifische Passwort des Nutzers.

createClient DAVClient erzeugen

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 Konstanten

export const PROD_ID = {
company: "your-company-name-here",
product: "your-product-name-here",
};

getCalendars Alle Kalender abrufen

async getCalendars(
...params: Parameters<typeof DAVClient.prototype.fetchCalendars>
): Promise<DAVCalendar[]> {

// You can abstract this initialization into another method.
// For the sake of simplicity, we'll initialize the client on each method.
const client = createClient({
username: <email-here>,
password: <password-here>,
});

return client.fetchCalendars(...params);
}

getCalendarById Kalender per ID abrufen

async function getCalendarById(calendarUrl: string): Promise<DAVCalendar> {
 const calendars = await getCalendars();
 const calendar = calendars.find((el) => el.url === calendarUrl);
 if (!calendar) {
 throw new Error(`Apple-Kalender mit ID ${calendarUrl} nicht gefunden`);
}

 return calendar;
}

getCalendarEvents Ereignisse eines Kalenders abrufen

async function 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 Ereignis per ID abrufen

async function 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(`Keine Antwort beim Abrufen von ${eventUrl}`);
} else if (responses[0].status >= 400) {
 throw new Error(
 `Fehler beim Abrufen des Apple-Ereignisses. Status: ${responses[0].statusText}`
);
}

 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,
};

 return calendarObject;
}

createEvent Ereignis erstellen

async function 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(
 `Fehler beim Erstellen des Apple-Ereignisses: ${response.statusText}`
);
}

 return { id: event.id(), eventWithExceptions };
}

updateEvent Ereignis aktualisieren

async function 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(
 `Fehler beim Aktualisieren des Apple-Ereignisses: ${response.statusText}`
);
}

 return getEventById(calendarUrl, eventId);
}

deleteEvent Ereignis löschen

async function 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(
 `Fehler beim Löschen des Apple-Ereignisses: ${response.statusText}`
);
}

 return { id: eventId };
}

Welche Einschränkungen hat Apple iCloud Calendar?

  1. Basic Auth mit app-spezifischem Passwort – Kein OAuth 2.0.

  2. Keine Webhooks/Push-Benachrichtigungen – Es müssen Polling-Lösungen mit sync-collection gebaut werden.

  3. Kein PATCH – Ereignisse müssen vollständig per PUT überschrieben werden.

  4. Keine manuelle Kontrolle über Einladungen – iCloud versendet Einladungen automatisch (Outbox/Inbox nicht verfügbar).

Gibt es einen einfacheren Weg, iCloud Calendar zu integrieren?

Die manuelle Integration von iCloud Calendar ist komplex und weicht von gängigen Kalender-Standards ab. Eine Unified Calendar API bietet:

  • Gut dokumentierte, getestete API nach modernen Standards

  • Weniger Entwicklungs- und Wartungsaufwand

  • Integration weiterer Kalenderanbieter (Google Calendar, Outlook) ohne Zusatzarbeit

  • Push-Benachrichtigungen ohne eigene Polling-Lösung

Unified Calendar API Example

Nutzen Sie eine Unified Calendar API für die iCloud-Integration

Unsere OneCal Unified Calendar API steht kurz vor dem Start. Melden Sie sich für die Warteliste an, um sofort benachrichtigt zu werden und von Einführungsrabatten zu profitieren.