A comprehensive Swift 6 library for parsing and creating iCalendar (RFC 5545) events with full support for structured concurrency, Sendable conformance, and modern Swift features.
let calendar = ICalendar.calendar {
CalendarName("My Calendar")
createEvent(summary: "Meeting", startDate: Date(), endDate: Date().addingTimeInterval(3600))
}- RFC Compliant: Full support for multiple iCalendar specifications (see RFC Compliance below)
- Maximum Compatibility: RFC 7986 standard properties with automatic legacy X-WR fallbacks
- Swift 6 Ready: Complete Sendable conformance and structured concurrency support
- Type Safe: Uses structs instead of classes with comprehensive type operations
- Unified Alarm API: Type-safe alarm creation with RFC 5545 compliance and action-specific requirements
- Comprehensive: Support for events, todos, journals, alarms, time zones, and recurrence rules
- Enhanced Properties: RFC 7986 extensions including COLOR, IMAGE, CONFERENCE, GEO, and ATTACH properties
- Binary Attachments: Base64-encoded binary data support for ATTACH and IMAGE properties
- Timezone Integration: Foundation TimeZone integration with VTIMEZONE generation (RFC 7808)
- Builder Pattern: Fluent API for easy calendar and event creation
- Cross-Platform: Works in modern apps (Google Calendar) and legacy systems (older Apple Calendar)
- iOS 13.0+ / macOS 10.15+ / tvOS 13.0+ / watchOS 6.0+
- Swift 6.0+
- Xcode 16.0+
- Linux
Add the following to your Package.swift file:
dependencies: [
.package(url: "https://github.com/thoven87/icalendar-kit.git", from: "1.0.0")
]Or add it through Xcode:
- File β Add Package Dependencies
- Enter the repository URL
- Select the version and target
import ICalendar
// Create calendar with RFC 7986 properties (automatic legacy compatibility)
var calendar = ICalendar(productId: "-//My App//Team Calendar//EN")
calendar.name = "My Team Calendar" // Sets both NAME and X-WR-CALNAME
calendar.calendarDescription = "Team events and meetings" // Sets both DESCRIPTION and X-WR-CALDESC
calendar.refreshInterval = ICalDuration(hours: 6) // Sets both REFRESH-INTERVAL and X-PUBLISHED-TTL
// Create event with unified alarm API
let organizer = ICalAttendee(email: "[email protected]", commonName: "Manager")
let teamMeeting = EventBuilder(summary: "Team Meeting")
.starts(at: Date(), timeZone: .current)
.duration(3600) // 1 hour in seconds
.location("Conference Room A")
.description("Weekly team sync meeting")
.categories("Work", "Meeting")
.confirmed()
// Unified alarm API with type-safe, RFC 5545 compliant alarms
.addAlarm(.display(description: "Meeting starts in 15 minutes"), trigger: .minutesBefore(15))
.addAlarm(.email(description: "Meeting reminder", summary: "Team Meeting", to: organizer), trigger: .minutesBefore(60))
.addAlarm(.audio(), trigger: .minutesBefore(5)) // Sound notification
.buildEvent()
calendar.addEvent(teamMeeting)
// Serialize to iCalendar format (includes both RFC 7986 and legacy properties)
let icsString = try ICalendarSerializer().serialize(calendar)
print(icsString)let icalContent = """
BEGIN:VCALENDAR
VERSION:2.0
PRODID:-//My App//EN
BEGIN:VEVENT
UID:event-123
DTSTAMP:20240101T120000Z
DTSTART:20240101T140000Z
DTEND:20240101T150000Z
SUMMARY:Important Meeting
LOCATION:Room 101
END:VEVENT
END:VCALENDAR
"""
// Direct parsing functions (no client needed in v2.0)
let calendar = try ICalendarKit.parseCalendar(from: icalContent)
print("Found \(calendar.events.count) events")// Create events using EventBuilder with type-safe alarms
let startTime = Date()
let timeZone = TimeZone(identifier: "America/New_York")!
let manager = ICalAttendee(email: "[email protected]", commonName: "Project Manager")
// Meeting with progressive alarms
let meeting = EventBuilder(summary: "Daily Standup")
.starts(at: startTime, timeZone: timeZone)
.duration(1800) // 30 minutes in seconds
.location("Virtual - Zoom")
.description("Daily team standup meeting")
.categories("Work", "Meeting")
.confirmed()
// Multiple alarms with different actions
.addAlarms([
.display(description: "Standup in 15 minutes"),
.display(description: "Standup in 5 minutes"),
.audio() // Sound notification
], triggers: [
.minutesBefore(15),
.minutesBefore(5),
.minutesBefore(2)
])
.buildEvent()
// Project planning with email notification
let projectPlanning = EventBuilder(summary: "Project Planning")
.starts(at: Calendar.current.date(byAdding: .day, value: 1, to: Date())!, timeZone: timeZone)
.duration(7200) // 2 hours in seconds
.location("Conference Room B")
.description("Weekly project planning session")
.organizer(email: "[email protected]", name: "Project Manager")
.addAttendee(email: "[email protected]", name: "Alice", role: .requiredParticipant)
.addAttendee(email: "[email protected]", name: "Bob", role: .optionalParticipant)
.highPriority()
.confirmed()
// Email alarm with all required properties
.addAlarm(.email(
description: "Project planning session tomorrow - please prepare your reports",
summary: "Project Planning Reminder",
to: manager
), trigger: .minutesBefore(1440)) // 24 hours before
.buildEvent()
// Create calendar with maximum compatibility
var calendar = ICalendar(productId: "-//My Company//Project Calendar//EN")
calendar.name = "Project Calendar" // Automatically sets NAME + X-WR-CALNAME
calendar.calendarDescription = "Weekly project events" // Sets DESCRIPTION + X-WR-CALDESC
calendar.color = "#2196F3" // Material Blue
calendar.addEvent(meeting)
calendar.addEvent(projectPlanning)let startTime = Date()
let timeZone = TimeZone(identifier: "America/New_York")!
// Daily standup for 10 occurrences
let dailyStandup = EventBuilder(summary: "Daily Standup")
.starts(at: startTime, timeZone: timeZone)
.duration(1800) // 30 minutes
.location("Team Room")
.description("Daily team standup meeting")
.repeats(every: 1, count: 10) // Daily for 10 days
.categories("Work", "Standup")
.confirmed()
.addAlarm(action: .display, minutesBefore: 5, description: "Standup starting soon")
.buildEvent()
// Weekly team meeting on weekdays (Monday, Wednesday, Friday)
let weeklyMeeting = EventBuilder(summary: "Team Meeting")
.starts(at: startTime, timeZone: timeZone)
.duration(3600) // 1 hour
.location("Conference Room A")
.description("Weekly team sync meeting")
.repeatsWeekly(every: 1, on: [.monday, .wednesday, .friday], count: 8)
.organizer(email: "[email protected]", name: "Team Lead")
.categories("Work", "Team Meeting")
.confirmed()
.addAlarm(action: .display, minutesBefore: 15, description: "Team meeting reminder")
.buildEvent()
// Monthly all-hands meeting
let monthlyAllHands = EventBuilder(summary: "All Hands Meeting")
.starts(at: startTime, timeZone: timeZone)
.duration(7200) // 2 hours
.location("Main Auditorium")
.description("Monthly company-wide meeting")
.repeatsMonthly(every: 1, count: 12) // Monthly for 12 months
.organizer(email: "[email protected]", name: "CEO")
.categories("Company", "All Hands")
.highPriority()
.confirmed()
.addAlarm(action: .display, minutesBefore: 30, description: "All hands meeting starting soon")
.buildEvent()
// Create calendar and add events
var calendar = ICalendar(productId: "-//My Company//Recurring Events//EN")
calendar.name = "Recurring Events Calendar"
calendar.addEvent(dailyStandup)
calendar.addEvent(weeklyMeeting)
calendar.addEvent(monthlyAllHands)// Create meeting with attendees and organizer
let startTime = Date()
let timeZone = TimeZone(identifier: "America/New_York")!
let projectKickoff = EventBuilder(summary: "Project Kickoff")
.starts(at: startTime, timeZone: timeZone)
.duration(7200) // 2 hours
.location("Conference Room A")
.description("Project kickoff meeting to discuss goals and timeline")
.organizer(email: "[email protected]", name: "Meeting Organizer")
.attendee(email: "[email protected]", name: "John Doe", required: true)
.attendee(email: "[email protected]", name: "Jane Smith", required: false)
.attendee(email: "[email protected]", name: "Mike Johnson", required: true)
.categories("Project", "Kickoff")
.highPriority()
.confirmed()
.addAlarm(action: .display, minutesBefore: 15, description: "Meeting starts in 15 minutes")
.addAlarm(action: .display, minutesBefore: 60, description: "Project kickoff in 1 hour")
.buildEvent()
// Create calendar and add the event
var calendar = ICalendar(productId: "-//My Company//Meeting Planner//EN")
calendar.name = "Team Meetings"
calendar.addEvent(projectKickoff)
// All-day event example
let teamOuting = EventBuilder(summary: "Team Building Outing")
.allDay(on: Calendar.current.date(byAdding: .day, value: 7, to: Date())!, timeZone: timeZone)
.location("Adventure Park")
.description("Annual team building activity")
.organizer(email: "[email protected]", name: "HR Department")
.attendee(email: "[email protected]", name: "Team Member 1")
.attendee(email: "[email protected]", name: "Team Member 2")
.categories("Team Building", "Fun")
.confirmed()
.buildEvent()
calendar.addEvent(teamOuting)// Create healthcare calendar with EventBuilder
let startTime = Date()
let timeZone = TimeZone(identifier: "America/New_York")!
// Healthcare event
let consultation = EventBuilder(summary: "Patient Consultation - John Doe")
.starts(at: startTime, timeZone: timeZone)
.duration(3600) // 1 hour
.location("Room 205 - Cardiology")
.description("Routine cardiology consultation")
.categories("Healthcare", "Consultation")
.confirmed()
.privateEvent() // HIPAA compliant
.addAlarm(action: .display, minutesBefore: 15, description: "Patient consultation starting")
.buildEvent()
var healthcareCalendar = ICalendar(productId: "-//City Hospital//Healthcare App//EN")
healthcareCalendar.name = "Cardiology Schedule"
healthcareCalendar.calendarDescription = "City Hospital - Cardiology Department"
healthcareCalendar.addEvent(consultation)
// Corporate team calendar
let sprintPlanning = EventBuilder(summary: "Sprint Planning")
.starts(at: Calendar.current.date(byAdding: .day, value: 1, to: Date())!, timeZone: timeZone)
.duration(7200) // 2 hours
.location("Engineering Conference Room")
.description("Planning for next development sprint")
.organizer(email: "[email protected]", name: "Scrum Master")
.attendee(email: "[email protected]", name: "Developer 1", required: true)
.attendee(email: "[email protected]", name: "Developer 2", required: true)
.attendee(email: "[email protected]", name: "UX Designer", required: false)
.categories("Engineering", "Planning")
.highPriority()
.confirmed()
.addAlarm(action: .display, minutesBefore: 30, description: "Sprint planning starting soon")
.buildEvent()
var corporateCalendar = ICalendar(productId: "-//Acme Corp//Corporate//EN")
corporateCalendar.name = "Engineering Team"
corporateCalendar.calendarDescription = "Acme Corp - Engineering Team Events"
corporateCalendar.addEvent(sprintPlanning)// Multi-timezone calendar with EventBuilder
let pstTimeZone = TimeZone(identifier: "America/Los_Angeles")!
let estTimeZone = TimeZone(identifier: "America/New_York")!
// West Coast event (9 AM PST)
let westCoastStandup = EventBuilder(summary: "West Coast Team Standup")
.starts(at: Date(), timeZone: pstTimeZone)
.duration(3600) // 1 hour
.location("San Francisco Office")
.description("Daily standup for west coast team")
.categories("Work", "Standup")
.repeats(every: 1, count: 30) // Daily for 30 days
.confirmed()
.addAlarm(action: .display, minutesBefore: 5, description: "Standup starting")
.buildEvent()
// East Coast event (2 PM EST - same time as west coast standup)
let eastCoastCall = EventBuilder(summary: "East Coast Client Call")
.starts(at: Calendar.current.date(byAdding: .day, value: 1, to: Date())!, timeZone: estTimeZone)
.duration(3600) // 1 hour
.location("New York Office")
.description("Important client call")
.organizer(email: "[email protected]", name: "Sales Team")
.categories("Sales", "Client")
.highPriority()
.confirmed()
.addAlarm(action: .display, minutesBefore: 15, description: "Client call starting")
.buildEvent()
var multiTimeZoneCalendar = ICalendar(productId: "-//Multi-TZ Company//EN")
multiTimeZoneCalendar.name = "Multi-Timezone Events"
multiTimeZoneCalendar.addEvent(westCoastStandup)
multiTimeZoneCalendar.addEvent(eastCoastCall)// Daily stand up meetings
let startTime = Calendar.current.date(from: DateComponents(year: 2024, month: 1, day: 1, hour: 9))!
let timeZone = TimeZone(identifier: "America/New_York")!
// Weekly stand up on weekdays
let weeklyStandUp = EventBuilder(summary: "Team Stand Up")
.starts(at: startTime, timeZone: timeZone)
.duration(1800) // 30 minutes
.location("Conference Room A")
.description("Daily team stand up meeting")
.repeatsWeekly(every: 1, on: [.monday, .tuesday, .wednesday, .thursday, .friday])
.categories("Work", "Stand Up")
.confirmed()
.addAlarm(action: .display, minutesBefore: 15, description: "Stand up starting soon")
.buildEvent()
// Daily recurring pattern
let dailyStandUp = EventBuilder(summary: "Daily Stand Up")
.starts(at: startTime, timeZone: timeZone)
.duration(900) // 15 minutes
.description("Quick daily sync")
.repeats(every: 1, count: 30) // Daily for 30 days
.categories("Daily", "Stand Up")
.confirmed()
.buildEvent()
// Monthly team sync
let monthlySync = EventBuilder(summary: "Monthly Team Sync")
.starts(at: startTime, timeZone: timeZone)
.duration(3600) // 1 hour
.repeatsMonthly(every: 1, count: 12)
.organizer(email: "[email protected]", name: "Team Manager")
.categories("Team", "Sync")
.confirmed()
.addAlarm(action: .display, minutesBefore: 30, description: "Monthly sync starting")
.buildEvent()
var calendar = ICalendar(productId: "-//My Company//Team Events//EN")
calendar.name = "Team Calendar"
calendar.addEvent(weeklyStandUp)
calendar.addEvent(dailyStandUp)
calendar.addEvent(monthlySync)// Create todo items using the direct function API
let todoCalendar = ICalendar.calendar(productId: "-//Task Manager//EN") {
CalendarName("Project Tasks")
CalendarMethod("PUBLISH")
ICalendarFactory.createTodo(
summary: "Complete project documentation",
dueDate: Date().addingTimeInterval(604800), // 1 week
priority: 1,
description: "Write comprehensive documentation for the new feature"
)
ICalendarFactory.createTodo(
summary: "Code review for PR #123",
startDate: Date(),
dueDate: Date().addingTimeInterval(172800), // 2 days
priority: 3,
description: "Review the authentication module changes"
)
}
### Working with Alarms - Unified Type-Safe API
The unified alarm API provides compile-time safety and RFC 5545 compliance:
```swift
let attendee = ICalAttendee(email: "[email protected]", commonName: "User")
// Create audio attachment from URL
let soundFile = ICalAttachment(uri: "https://example.com/notification.mp3", mediaType: "audio/mp3")
let event = EventBuilder(summary: "Important Meeting")
.starts(at: Date(), timeZone: .current)
.duration(3600)
// DISPLAY alarms - require description
.addAlarm(.display(description: "Meeting in 30 minutes"), trigger: .minutesBefore(30))
.addAlarm(.display(description: "Meeting starting now"), trigger: .eventStart)
// EMAIL alarms - require description, summary, and attendee
.addAlarm(.email(
description: "Don't forget about the important meeting",
summary: "Meeting Reminder",
to: attendee
), trigger: .minutesBefore(60))
// AUDIO alarms - optional attachment (URL or binary data)
.addAlarm(.audio(attachment: soundFile), trigger: .minutesBefore(10)) // Custom sound from URL
.addAlarm(.audio(), trigger: .minutesBefore(2)) // System default sound
// Multiple alarms at once
.addAlarms([
.display(description: "Final warning - 5 minutes"),
.display(description: "Meeting starting - 1 minute"),
.audio()
], triggers: [
.minutesBefore(5),
.minutesBefore(1),
.eventStart
])
// Different trigger types
.addAlarm(.display(description: "Custom timing"),
trigger: .duration(ICalDuration(hours: 1, minutes: 30)))
.addAlarm(.display(description: "Absolute time reminder"),
trigger: .absoluteTime(specificDate, timeZone: .current))
.buildEvent()iCalendar-Kit automatically provides maximum compatibility by setting both modern RFC 7986 properties and their legacy X-WR equivalents:
var calendar = ICalendar(productId: "-//My App//Modern Calendar//EN")
// Setting RFC 7986 properties automatically includes legacy equivalents
calendar.name = "My Calendar" // Sets NAME + X-WR-CALNAME
calendar.calendarDescription = "Description" // Sets DESCRIPTION + X-WR-CALDESC
calendar.refreshInterval = ICalDuration(hours: 6) // Sets REFRESH-INTERVAL + X-PUBLISHED-TTL
// Additional RFC 7986 properties
calendar.color = "#FF5722"
calendar.image = "https://example.com/calendar.png"
calendar.source = "https://api.example.com/calendar.ics"
// Generated ICS includes both for maximum compatibility:
// NAME:My Calendar
// X-WR-CALNAME:My Calendar
// DESCRIPTION:Description
// X-WR-CALDESC:Description
// REFRESH-INTERVAL:PT6H
// X-PUBLISHED-TTL:PT6H// Create alarms using direct functions in v2.0
let calendarWithAlarms = ICalendar.calendar(productId: "-//Alarm Example//EN") {
CalendarName("Events with Alarms")
EventBuilder(summary: "Important Meeting")
.startDate(Date())
.endDate(Date().addingTimeInterval(3600))
.location("Conference Room")
.addAlarm(ICalendarFactory.createDisplayAlarm(
description: "Meeting reminder",
triggerMinutesBefore: 15
))
.addAlarm(ICalendarFactory.createAudioAlarm(
triggerMinutesBefore: 5,
audioFile: "reminder.wav"
))
.addAlarm(ICalendarFactory.createEmailAlarm(
summary: "Meeting Tomorrow",
description: "Don't forget about the important meeting tomorrow",
attendees: [ICalendarFactory.createAttendee(email: "[email protected]")],
triggerMinutesBefore: 1440 // 24 hours
))
}// Old API (v1.x) - still works but deprecated
.addAlarm(action: .display, minutesBefore: 15, description: "Meeting soon")
// New Unified API (v2.0+) - recommended
.addAlarm(.display(description: "Meeting soon"), trigger: .minutesBefore(15))// Old approach - only sets one property
calendar.displayName = "My Calendar" // Only X-WR-CALNAME
calendar.xwrDescription = "Description" // Only X-WR-CALDESC
// New approach - maximum compatibility
calendar.name = "My Calendar" // Sets NAME + X-WR-CALNAME
calendar.calendarDescription = "Description" // Sets DESCRIPTION + X-WR-CALDESC- Use RFC 7986 properties (name, calendarDescription, refreshInterval) for new code
- Leverage the unified alarm API for type safety and RFC compliance
- Let the library handle compatibility - don't manually set both properties
- Test across multiple calendar applications to ensure compatibility
Version 2.0 provides direct control over parsing and serialization behavior:
// Parse with validation (default)
let calendar = try ICalendarKit.parseCalendar(from: icsContent, validateOnParse: true)
// Parse without validation for performance
let fastCalendar = try ICalendarKit.parseCalendar(from: icsContent, validateOnParse: false)
// Serialize with validation (default)
let validatedICS = try ICalendarKit.serializeCalendar(calendar, validateBeforeSerializing: true)
// Serialize without validation for performance
let fastICS = try ICalendarKit.serializeCalendar(calendar, validateBeforeSerializing: false)// Parse and work with multiple calendars using v2.0 API
let icsFiles = ["calendar1.ics", "calendar2.ics", "calendar3.ics"]
var allCalendars: [ICalendar] = []
for filename in icsFiles {
let url = URL(fileURLWithPath: filename)
let calendar = try ICalendarKit.parseCalendar(from: url, validateOnParse: false) // Skip validation for performance
allCalendars.append(calendar)
}
// Serialize multiple calendars together
let combinedICS = try ICalendarKit.serializeCalendars(allCalendars, validateBeforeSerializing: true)
// Create a merged calendar from multiple sources
let mergedCalendar = ICalendar.calendar(productId: "-//Merged Calendar//EN") {
CalendarName("Combined Events")
CalendarDescription("Events from multiple sources")
for calendar in allCalendars {
for event in calendar.events {
event // Add each event to the new calendar
}
}
}The library is built with Swift 6 structured concurrency in mind, with full Sendable conformance:
// Concurrent parsing of multiple calendars using v2.0 API
await withTaskGroup(of: ICalendar.self) { group in
let contents = [content1, content2, content3]
var calendars: [ICalendar] = []
for content in contents {
group.addTask {
return try ICalendarKit.parseCalendar(from: content, validateOnParse: true)
}
}
for await calendar in group {
calendars.append(calendar)
}
return calendars
}
// Process calendars concurrently
await withTaskGroup(of: String.self) { group in
for calendar in calendars {
group.addTask {
return try ICalendarKit.serializeCalendar(calendar, validateBeforeSerializing: true)
}
}
for await serialized in group {
// Handle serialized calendar
print("Processed calendar: \(serialized.prefix(100))...")
}
}// Convert Date to ICalDateTime
let dateTime = Date().asICalDateTime()
let dateOnly = Date().asICalDateOnly()
let utcDateTime = Date().asICalDateTimeUTC()
// Convert TimeInterval to Duration
let duration = TimeInterval(3600).asICalDuration // 1 hour// Filter events by date range
let todayEvents = calendar.events.events(
from: Calendar.current.startOfDay(for: Date()),
to: Calendar.current.date(byAdding: .day, value: 1, to: Date())!
)
// Get recurring events
let recurringEvents = calendar.events.recurringEvents
// Sort events by start date
let sortedEvents = calendar.events.sortedByStartDate// Pre-defined patterns
let daily = RecurrencePatterns.daily(count: 30)
let weekdays = RecurrencePatterns.weekdays(count: 20)
let monthly = RecurrencePatterns.monthly(dayOfMonth: 1, count: 12)
let yearly = RecurrencePatterns.yearly(count: 5)
// Monthly on first Monday
let firstMonday = RecurrencePatterns.monthly(
ordinal: 1,
weekday: .monday,
count: 12
)iCalendar-Kit provides comprehensive support for multiple RFC specifications with automatic compatibility handling:
| RFC | Description | Support Level | Key Features |
|---|---|---|---|
| RFC 5545 | iCalendar Core | β Complete | Events, TODOs, Journals, Alarms, Recurrence |
| RFC 7986 | Calendar Extensions | β Complete | NAME, DESCRIPTION, COLOR, IMAGE properties |
| RFC 6868 | Parameter Encoding | β Complete | Special character encoding in parameters |
| RFC 7808 | Time Zone Data | β Complete | VTIMEZONE generation with automatic TZURL |
Automatic dual-property support for maximum compatibility:
// Setting one property creates both RFC 7986 and legacy versions
calendar.name = "My Calendar"
// Creates: NAME:My Calendar + X-WR-CALNAME:My Calendar
calendar.calendarDescription = "Description"
// Creates: DESCRIPTION:Description + X-WR-CALDESC:Description
calendar.refreshInterval = ICalDuration(hours: 6)
// Creates: REFRESH-INTERVAL:PT6H + X-PUBLISHED-TTL:PT6HThe unified alarm API enforces all RFC 5545 requirements at compile-time:
| Alarm Type | Required Properties | Enforced by API |
|---|---|---|
| DISPLAY | DESCRIPTION | β
.display(description:) |
| DESCRIPTION, SUMMARY, ATTENDEE | β
.email(description:summary:to:) |
|
| AUDIO | None (ATTACH optional) | β
.audio(attachment:) |
| PROCEDURE | ATTACH | β
.procedure(attachment:) |
iCalendar-Kit automatically generates TZURL properties for better timezone handling:
// TimeZoneRegistry automatically adds TZURL to VTIMEZONE components
if let nyTimeZone = TimeZoneRegistry.shared.getTimeZone(for: "America/New_York") {
calendar.addTimeZone(nyTimeZone)
// Generated VTIMEZONE will include:
// TZURL:http://tzurl.org/zoneinfo-outlook/America/New_York
}Benefits of TZURL:
- Automatic Updates: Calendar applications can fetch latest timezone rules
- Government Changes: Handles daylight saving rule changes automatically
- Cross-Platform Consistency: All clients use the same timezone definitions
- Future-Proof: Events remain accurate even after timezone rule updates
Generated TZURL Format:
TZURL:http://tzurl.org/zoneinfo-outlook/America/Los_AngelesTZURL:http://tzurl.org/zoneinfo-outlook/Europe/LondonTZURL:http://tzurl.org/zoneinfo-outlook/Asia/Tokyo
| Application | RFC 7986 Support | Legacy X-WR Support | TZURL Support | iCalendar-Kit Compatibility |
|---|---|---|---|---|
| Google Calendar | β Yes | β Yes | β Yes | β Perfect |
| Apple Calendar (2020+) | β Yes | β Yes | β Yes | β Perfect |
| Apple Calendar (Legacy) | β No | β Yes | πΆ Partial | β Perfect (via X-WR) |
| Outlook | πΆ Partial | β Yes | β Yes | β Perfect |
| CalDAV Servers | πΆ Varies | β Yes | β Yes | β Perfect |
All generated calendars are automatically validated for RFC compliance:
// Automatic validation during serialization
let icsContent = try ICalendarSerializer().serialize(calendar)
// β
Validates all required properties are present
// β
Ensures proper component structure
// β
Verifies alarm requirements are met
// β
Checks recurrence rule validityHere's a comprehensive example showcasing RFC 7986 compatibility, unified alarms, and enterprise features:
import ICalendar
/// Production-ready calendar with maximum compatibility
func createEnterpriseCalendar() throws -> String {
// Create calendar with RFC 7986 properties (automatic legacy compatibility)
var calendar = ICalendar(productId: "-//Enterprise Corp//Business Calendar v2.0//EN")
// RFC 7986 properties (automatically includes X-WR equivalents)
calendar.name = "Enterprise Business Calendar"
calendar.calendarDescription = "Official company calendar with meetings, deadlines, and events"
calendar.refreshInterval = ICalDuration(hours: 6) // Refresh every 6 hours
calendar.color = "#1976D2" // Corporate blue
calendar.source = "https://api.enterprise.com/calendar.ics"
calendar.image = "https://enterprise.com/calendar-icon.png"
// Create attendees
let ceo = ICalAttendee(email: "[email protected]", commonName: "CEO")
let manager = ICalAttendee(email: "[email protected]", commonName: "Project Manager")
let team = ICalAttendee(email: "[email protected]", commonName: "Development Team")
// 1. Executive Meeting with Email Notifications
let executiveMeeting = EventBuilder(summary: "π Quarterly Business Review")
.starts(at: Date().addingTimeInterval(86400), timeZone: .current) // Tomorrow
.duration(7200) // 2 hours
.location("Executive Boardroom")
.description("Comprehensive Q4 performance review with stakeholder presentations")
.organizer(email: "[email protected]", name: "CEO")
.addAttendee(email: "[email protected]", name: "Project Manager", role: .requiredParticipant)
.addAttendee(email: "[email protected]", name: "CFO", role: .requiredParticipant)
.addAttendee(email: "[email protected]", name: "Development Team", role: .optionalParticipant, userType: .group)
.highPriority()
.confirmed()
// Progressive alarm strategy with type-safe API
.addAlarm(.email(
description: "Quarterly review tomorrow - please prepare your reports and presentations",
summary: "Business Review Preparation",
to: ceo
), trigger: .minutesBefore(1440)) // 24 hours
.addAlarm(.display(description: "Business review in 2 hours - final preparations"), trigger: .minutesBefore(120))
.addAlarm(.display(description: "Business review in 30 minutes"), trigger: .minutesBefore(30))
.addAlarm(.audio(), trigger: .minutesBefore(10)) // Sound notification
.buildEvent()
// 2. Recurring Team Standup
let dailyStandup = EventBuilder(summary: "π Daily Team Standup")
.starts(at: nextMondayAt9AM(), timeZone: .current)
.duration(900) // 15 minutes
.location("Conference Room A / Virtual")
.description("Daily team coordination and blocker discussion")
.organizer(email: "[email protected]", name: "Project Manager")
.addAttendee(email: "[email protected]", name: "Developer 1", role: .requiredParticipant)
.addAttendee(email: "[email protected]", name: "Developer 2", role: .requiredParticipant)
.addAttendee(email: "[email protected]", name: "UX Designer", role: .optionalParticipant)
.repeatsWeekly(every: 1, on: [.monday, .tuesday, .wednesday, .thursday, .friday])
.confirmed()
// Multiple alarms for recurring events
.addAlarms([
.display(description: "Standup in 10 minutes"),
.display(description: "Standup starting now"),
.audio()
], triggers: [
.minutesBefore(10),
.minutesBefore(1),
.eventStart
])
.buildEvent()
// 3. Project Deadline with Escalating Notifications
let projectDeadline = EventBuilder(summary: "β οΈ Project Alpha Deadline")
.starts(at: Calendar.current.date(byAdding: .weekOfYear, value: 2, to: Date())!, timeZone: .current)
.duration(0) // All-day deadline
.description("Final deadline for Project Alpha deliverables")
.organizer(email: "[email protected]", name: "Project Manager")
.addAttendee(email: "[email protected]", name: "Development Team", role: .requiredParticipant, userType: .group)
.highPriority()
.confirmed()
// Escalating deadline reminders
.addAlarm(.email(
description: "Project Alpha deadline in 1 week - please ensure all deliverables are on track",
summary: "Project Deadline - 1 Week Warning",
to: manager
), trigger: .minutesBefore(10080)) // 1 week
.addAlarm(.display(description: "Project Alpha deadline in 3 days"), trigger: .minutesBefore(4320)) // 3 days
.addAlarm(.display(description: "Project Alpha deadline tomorrow"), trigger: .minutesBefore(1440)) // 1 day
.addAlarm(.display(description: "Project Alpha deadline in 2 hours"), trigger: .minutesBefore(120)) // 2 hours
.buildEvent()
// Add timezone components with automatic TZURL generation (RFC 7808)
if let estTimeZone = TimeZoneRegistry.shared.getTimeZone(for: "America/New_York") {
calendar.addTimeZone(estTimeZone)
// Automatically includes: TZURL:http://tzurl.org/zoneinfo-outlook/America/New_York
}
if let utcTimeZone = TimeZoneRegistry.shared.getTimeZone(for: "UTC") {
calendar.addTimeZone(utcTimeZone)
// Automatically includes: TZURL:http://tzurl.org/zoneinfo-outlook/UTC
}
// Add events to calendar
calendar.addEvent(executiveMeeting)
calendar.addEvent(dailyStandup)
calendar.addEvent(projectDeadline)
// Serialize with validation
return try ICalendarSerializer().serialize(calendar)
}
// Helper function
private func nextMondayAt9AM() -> Date {
let calendar = Calendar.current
let now = Date()
let nextMonday = calendar.nextDate(after: now, matching: DateComponents(weekday: 2), matchingPolicy: .nextTime)!
return calendar.date(bySettingHour: 9, minute: 0, second: 0, of: nextMonday)!
}
// Usage
do {
let enterpriseCalendar = try createEnterpriseCalendar()
print("Generated enterprise calendar with maximum compatibility:")
print("- RFC 7986 + X-WR legacy properties")
print("- Type-safe alarms with RFC 5545 compliance")
print("- Automatic TZURL generation for timezone updates")
print("- Works in all major calendar applications")
// Save to file
try enterpriseCalendar.write(to: URL(fileURLWithPath: "enterprise_calendar.ics"), atomically: true, encoding: .utf8)
} catch {
print("Error creating calendar: \(error)")
}do {
let calendar = try client.parseCalendar(from: icalContent)
let serialized = try client.serializeCalendar(calendar)
} catch ICalendarError.invalidFormat(let message) {
print("Invalid format: \(message)")
} catch ICalendarError.missingRequiredProperty(let property) {
print("Missing required property: \(property)")
} catch ICalendarError.invalidPropertyValue(let property, let value) {
print("Invalid value '\(value)' for property '\(property)'")
} catch {
print("Unexpected error: \(error)")
}// Validate a calendar
try client.validateCalendar(calendar)
// Validate email addresses
ValidationUtilities.isValidEmail("[email protected]") // true
// Validate other properties
ValidationUtilities.isValidPriority(5) // true (0-9)
ValidationUtilities.isValidPercentComplete(75) // true (0-100)// Outlook-compatible format
let outlookFormat = serializer.serializeForOutlook(calendar)
// Google Calendar format
let googleFormat = serializer.serializeForGoogle(calendar)
// Pretty-printed format for debugging
let prettyFormat = serializer.serializePretty(calendar)let stats = client.getCalendarStatistics(calendar)
print(stats.description)
// Output:
// Calendar Statistics:
// - Events: 5
// - Todos: 3
// - Journals: 1
// - Time Zones: 2
// - Total Attendees: 12
// - Events with Alarms: 4
// - Recurring Events: 2This library includes comprehensive support for RFC 7986 extensions and popular X-WR (Apple/CalDAV) extensions:
// Create calendar with RFC 7986 properties
var calendar = ICalendar(productId: "-//My Company//My Calendar//EN")
// RFC 7986 calendar properties
calendar.name = "My Calendar"
calendar.calendarDescription = "Personal events and appointments"
// Add RFC 7986 extended properties
calendar.properties.append(contentsOf: [
ICalProperty(name: "COLOR", value: "blue"),
ICalProperty(name: "REFRESH-INTERVAL", value: "PT1H"),
ICalProperty(name: "SOURCE", value: "https://example.com/calendar.ics"),
ICalProperty(name: "IMAGE", value: "https://example.com/calendar-icon.png")
])// Create event with RFC 7986 extensions
let conference = EventBuilder(summary: "International Conference 2024")
.starts(at: Date(), timeZone: TimeZone.current)
.duration(28800) // 8 hours
.location("Convention Center")
.description("Annual technology conference")
.categories("Conference", "Technology")
.confirmed()
.buildEvent()
// Add RFC 7986 extended properties to the event
conference.properties.append(contentsOf: [
ICalProperty(name: "COLOR", value: "red"),
ICalProperty(name: "GEO", value: "37.7749;-122.4194"),
ICalProperty(name: "IMAGE", value: "https://conference.com/logo.png"),
ICalProperty(name: "CONFERENCE", value: "https://meet.google.com/conference-room")
])
calendar.addEvent(conference)// Todo with RFC 7986 extensions
var todo = ICalTodo()
todo.summary = "Design review"
todo.dueDate = ICalDateTime(date: Date().addingTimeInterval(604800)) // 1 week
todo.properties.append(contentsOf: [
ICalProperty(name: "COLOR", value: "purple"),
ICalProperty(name: "IMAGE", value: "https://company.com/design-spec.pdf"),
ICalProperty(name: "CONFERENCE", value: "https://company.slack.com/channels/design")
])
// Journal with extensions
var journal = ICalJournal()
journal.summary = "Trip notes"
journal.description = "Notes from business trip to New York"
journal.dateTimeStart = ICalDateTime(date: Date())
journal.properties.append(contentsOf: [
ICalProperty(name: "COLOR", value: "green"),
ICalProperty(name: "GEO", value: "40.7128;-74.0060"),
ICalProperty(name: "IMAGE", value: "https://photos.com/trip-photo.jpg")
])
calendar.addTodo(todo)
calendar.addJournal(journal)// Create calendar with X-WR extensions (Apple/CalDAV)
var calendar = ICalendar(productId: "-//My Company//Work Calendar//EN")
// X-WR calendar extensions
calendar.properties.append(contentsOf: [
ICalProperty(name: "X-WR-CALNAME", value: "Work Calendar"),
ICalProperty(name: "X-WR-CALDESC", value: "Work-related events"),
ICalProperty(name: "X-WR-TIMEZONE", value: "America/New_York"),
ICalProperty(name: "X-WR-RELCALID", value: "parent-cal-123"),
ICalProperty(name: "X-PUBLISHED-TTL", value: "PT1H")
])// Create comprehensive calendar with all extensions
var extendedCalendar = ICalendar(productId: "-//Corporate//Extended Calendar//EN")
extendedCalendar.name = "Extended Calendar"
extendedCalendar.calendarDescription = "Full-featured calendar"
// Add comprehensive properties
extendedCalendar.properties.append(contentsOf: [
// RFC 7986 properties
ICalProperty(name: "COLOR", value: "corporate-blue"),
ICalProperty(name: "REFRESH-INTERVAL", value: "P1D"),
ICalProperty(name: "SOURCE", value: "https://api.company.com/calendar.ics"),
// X-WR extensions
ICalProperty(name: "X-WR-CALNAME", value: "Corp Cal"),
ICalProperty(name: "X-WR-TIMEZONE", value: "America/Los_Angeles"),
ICalProperty(name: "METHOD", value: "PUBLISH")
])The library provides comprehensive timezone support:
// Add timezone components to calendar
if let nyTimeZone = TimeZoneRegistry.shared.getTimeZone(for: "America/New_York") {
calendar.addTimeZone(nyTimeZone)
}
if let londonTimeZone = TimeZoneRegistry.shared.getTimeZone(for: "Europe/London") {
calendar.addTimeZone(londonTimeZone)
}
// Create events with specific timezones
let nyEvent = EventBuilder(summary: "New York Meeting")
.starts(at: Date(), timeZone: TimeZone(identifier: "America/New_York")!)
.duration(3600)
.buildEvent()
let londonEvent = EventBuilder(summary: "London Conference Call")
.starts(at: Date().addingTimeInterval(3600), timeZone: TimeZone(identifier: "Europe/London")!)
.duration(1800)
.buildEvent()
calendar.addEvent(nyEvent)
calendar.addEvent(londonEvent)The library supports both URI references and base64-encoded binary attachments:
// Create event with attachments
let presentationEvent = EventBuilder(summary: "Quarterly Presentation")
.starts(at: Date(), timeZone: TimeZone.current)
.duration(5400) // 90 minutes
.location("Board Room")
.buildEvent()
// Add URI attachment
let uriAttachment = ICalAttachment()
uriAttachment.uri = "https://example.com/presentation.pdf"
uriAttachment.parameters["FMTTYPE"] = "application/pdf"
presentationEvent.attachments.append(uriAttachment)
// Add binary attachment with base64 encoding (if you have image data)
let imageData = Data(/* your image data */)
let binaryAttachment = ICalAttachment()
binaryAttachment.binaryData = imageData
binaryAttachment.parameters["ENCODING"] = "BASE64"
binaryAttachment.parameters["FMTTYPE"] = "image/png"
presentationEvent.attachments.append(binaryAttachment)
calendar.addEvent(presentationEvent)// Geographic coordinates helper
extension ICalProperty {
static func geo(latitude: Double, longitude: Double) -> ICalProperty {
return ICalProperty(name: "GEO", value: "\(latitude);\(longitude)")
}
static func refreshInterval(hours: Int) -> ICalProperty {
return ICalProperty(name: "REFRESH-INTERVAL", value: "PT\(hours)H")
}
}
// Usage examples
let locationEvent = EventBuilder(summary: "San Francisco Meetup")
.starts(at: Date(), timeZone: TimeZone.current)
.duration(7200)
.location("Golden Gate Park")
.buildEvent()
// Add geographic coordinates
locationEvent.properties.append(.geo(latitude: 37.7749, longitude: -122.4194))
// Add refresh interval to calendar
calendar.properties.append(.refreshInterval(hours: 2))
calendar.addEvent(locationEvent)This section showcases the comprehensive RFC implementation capabilities:
iCalendar Kit integrates features from multiple RFCs for enterprise functionality:
import ICalendar
// RFC 5545 + RFC 7986 Integration
var calendar = ICalendar(productId: "-//Corporate//Events//EN")
// RFC 5545: Core calendar properties
calendar.name = "Corporate Events"
calendar.calendarDescription = "Company-wide events and meetings"
calendar.method = "PUBLISH"
// RFC 7986: Enhanced properties
calendar.properties.append(contentsOf: [
ICalProperty(name: "COLOR", value: "blue"),
ICalProperty(name: "REFRESH-INTERVAL", value: "PT1H"),
ICalProperty(name: "SOURCE", value: "https://company.com/calendar.ics")
])
// Create enhanced event
let conference = EventBuilder(summary: "International Conference 2024")
.starts(at: Date(), timeZone: TimeZone.current)
.duration(7200) // 2 hours
.location("Convention Center")
.description("Annual technology conference")
.categories("Conference", "Technology")
.confirmed()
.buildEvent()
// Add RFC 7986 enhancements
conference.properties.append(contentsOf: [
ICalProperty(name: "IMAGE", value: "https://company.com/event-banner.jpg"),
ICalProperty(name: "GEO", value: "40.7589;-73.9851"),
ICalProperty(name: "CONFERENCE", value: "https://zoom.us/j/123456789")
])
calendar.addEvent(conference)Built-in analysis using RFC-compliant properties:
// Calendar statistics
extension ICalendar {
var eventCount: Int { events.count }
var todoCount: Int { todos.count }
var recurringEventCount: Int {
events.filter { $0.recurrenceRule != nil }.count
}
func eventsByCategory() -> [String: [ICalEvent]] {
Dictionary(grouping: events) { event in
event.categories.first ?? "Uncategorized"
}
}
func eventsByLocation() -> [String: [ICalEvent]] {
Dictionary(grouping: events) { event in
event.location ?? "No Location"
}
}
}
// Usage
print("Total Events: \(calendar.eventCount)")
print("Recurring Events: \(calendar.recurringEventCount)")
let categoryStats = calendar.eventsByCategory()
for (category, events) in categoryStats {
print("Category: \(category) - Events: \(events.count)")
}Comprehensive timezone handling with multiple zones:
// Multiple timezone calendar
var globalCalendar = ICalendar(productId: "-//Global Corp//Multi-TZ//EN")
// Add timezone components
let timezones = ["America/New_York", "Europe/London", "Asia/Tokyo"]
for tzId in timezones {
if let tz = TimeZoneRegistry.shared.getTimeZone(for: tzId) {
globalCalendar.addTimeZone(tz)
}
}
// Create events across timezones
let nycEvent = EventBuilder(summary: "NYC Team Meeting")
.starts(at: Date(), timeZone: TimeZone(identifier: "America/New_York")!)
.duration(3600)
.organizer(email: "[email protected]", name: "NYC Team")
.buildEvent()
let londonEvent = EventBuilder(summary: "London Strategy Session")
.starts(at: Date().addingTimeInterval(3600), timeZone: TimeZone(identifier: "Europe/London")!)
.duration(5400) // 90 minutes
.organizer(email: "[email protected]", name: "London Team")
.buildEvent()
globalCalendar.addEvent(nycEvent)
globalCalendar.addEvent(londonEvent)Complex recurrence handling with proper RRULE generation:
// Quarterly business reviews (first Monday of quarter)
let quarterlyReview = EventBuilder(summary: "Quarterly Business Review")
.starts(at: Date(), timeZone: TimeZone.current)
.duration(10800) // 3 hours
.location("Executive Conference Room")
.description("Quarterly review meeting")
.organizer(email: "[email protected]", name: "Executive Team")
.categories("Business", "Review")
.highPriority()
.confirmed()
.buildEvent()
// Custom recurrence rule - every 3 months on first Monday
quarterlyReview.recurrenceRule = ICalRecurrenceRule(
frequency: .monthly,
interval: 3,
byWeekday: [.monday],
bySetpos: [1]
)
// Holiday schedule with exceptions
let holidayEvent = EventBuilder(summary: "Company Holiday")
.starts(at: Date(), timeZone: TimeZone.current)
.duration(86400) // All day
.description("Company-wide holiday")
.categories("Holiday")
.buildEvent()
// Yearly recurrence
holidayEvent.recurrenceRule = ICalRecurrenceRule(
frequency: .yearly,
byMonth: [12],
byMonthday: [25]
)
calendar.addEvent(quarterlyReview)
calendar.addEvent(holidayEvent)Multiple export formats with full RFC compliance:
// Standard iCalendar format (RFC 5545)
let serializer = ICalendarSerializer()
let icsString = try serializer.serialize(calendar)
let icsData = icsString.data(using: .utf8)!
// Save to file
try icsData.write(to: URL(fileURLWithPath: "calendar.ics"))
// Different serialization options
let compactSerializer = ICalendarSerializer(options: .compact)
let compactICS = try compactSerializer.serialize(calendar)
// Platform-specific formatting
let outlookSerializer = ICalendarSerializer()
let outlookICS = outlookSerializer.serializeForOutlook(calendar)
let googleICS = outlookSerializer.serializeForGoogle(calendar)
// Statistics
let stats = serializer.getStatistics(calendar)
print("Generated \(stats.totalLines) lines")
print("Total size: \(stats.totalCharacters) characters")Built-in validation for RFC compliance and security:
// Calendar validation using the parser
let parser = ICalendarParser()
do {
try parser.validate(calendar)
print("Calendar is RFC 5545 compliant")
} catch {
print("Validation error: \(error)")
}
// Content sanitization helpers
extension String {
func sanitized() -> String {
// Remove potential script content
return self
.replacingOccurrences(of: "<script", with: "<script", options: .caseInsensitive)
.replacingOccurrences(of: "javascript:", with: "", options: .caseInsensitive)
}
}
// Secure event creation
let secureEvent = EventBuilder(summary: "Secure Meeting".sanitized())
.description("Meeting description".sanitized())
.location("Conference Room A".sanitized())
.starts(at: Date(), timeZone: TimeZone.current)
.duration(3600)
.buildEvent()
// Validate serialized output
let serializer = ICalendarSerializer(options: .init(validateBeforeSerializing: true))
let safeICS = try serializer.serialize(calendar)This library provides comprehensive support for all major iCalendar specifications, making it one of the most complete iCalendar implementations available for Swift:
-
RFC 5545 - Internet Calendaring and Scheduling Core Object Specification (iCalendar)
- Complete VCALENDAR, VEVENT, VTODO, VJOURNAL, VALARM, VTIMEZONE components
- All standard properties with proper validation and formatting
- Full recurrence rules (RRULE) with all BY* parameters
- ATTACH property with both URI and base64 binary support
- Comprehensive timezone definitions and references
-
RFC 7986 - New Properties for iCalendar
- NAME, COLOR, IMAGE, SOURCE, REFRESH-INTERVAL properties
- CONFERENCE property for video/audio meeting integration
- Enhanced calendar-level DESCRIPTION, UID, LAST-MODIFIED
- DISPLAY, EMAIL, FEATURE, LABEL parameters
-
RFC 6868 - Parameter Value Encoding in iCalendar and vCard
- Complete parameter value escaping and encoding
- Support for special characters, newlines, and quotes in parameters
-
RFC 7808 - Time Zone Data Distribution Service
- TZURL property with automatic generation for timezone identifiers
- Full integration with tzurl.org service URLs
- Foundation TimeZone integration with round-trip compatibility
- X-LIC-LOCATION support for Lotus Notes compatibility
- RFC 5546 - iCalendar Transport-Independent Interoperability Protocol (iTIP)
- β Complete METHOD support: REQUEST, REPLY, CANCEL, PUBLISH, COUNTER, DECLINECOUNTER, REFRESH
- β Full iTIP workflow validation with proper message structure validation
- β Advanced ATTENDEE handling with DELEGATED-FROM/DELEGATED-TO parameters
- β Automatic SEQUENCE handling and incrementation for updates
- β Meeting workflow helpers: Accept/decline invitations, counter-proposals, refresh requests
- β Participation status management with proper PARTSTAT updates
-
RFC 7529 - Non-Gregorian Recurrence Rules
- β RSCALE parameter for Hebrew, Islamic, Chinese, Buddhist, Japanese, Persian, Indian, Coptic, and Ethiopic calendars
- β Foundation Calendar integration leveraging Apple's comprehensive calendar system
- β Non-Gregorian recurrence calculations with proper date handling
- Impact: Essential for international and religious calendar applications
-
RFC 9074 - VALARM Extensions for iCalendar
- β PROXIMITY-TRIGGER for location-based alarms (entering/leaving areas)
- β ACKNOWLEDGED property for alarm acknowledgment tracking
- β RELATED-TO support for alarm relationships and dependencies
- β Enhanced alarm actions with proximity-based triggers
- Impact: Modern mobile alarm management with location awareness
-
RFC 9073 - Event Publishing Extensions
- β STRUCTURED-DATA property for JSON/XML metadata embedding
- β VVENUE component for structured venue information
- β VLOCATION component for enhanced location details
- β VRESOURCE component for equipment and resource management
- β Rich metadata support for modern event publishing
- Impact: Social media integration, rich event data, resource booking
-
RFC 9253 - Support for iCalendar Relationships
- β Enhanced RELATED-TO types: FINISHTOSTART, FINISHTOFINISH, STARTTOSTART, STARTTOFINISH, DEPENDSON, BLOCKS
- β LINK property for external resource references with full parameter support
- β CONCEPT property for semantic categorization and tagging
- β REFID property for reference identifiers and component grouping
- Impact: Advanced project management, task dependencies, semantic organization
-
RFC 7953 - Calendar Availability
- β VAVAILABILITY component for publishing availability information
- β AVAILABLE/BUSY components for detailed time slot definitions
- β Free/busy time management with comprehensive period support
- β Integration with scheduling systems
- Impact: Meeting room booking, scheduling systems, availability publishing
-
RFC 6047 - iCalendar Message-Based Interoperability Protocol (iMIP)
- β Email transport bindings for iTIP message delivery
- β MIME structure generation with proper content types
- β Automatic email subject generation based on iTIP methods
- β Message-ID and threading support for conversation tracking
- Impact: Email-based calendar invitation systems
- Apple/CalDAV Extensions (Complete)
- X-WR-CALNAME, X-WR-CALDESC, X-WR-TIMEZONE
- X-WR-RELCALID, X-PUBLISHED-TTL
- X-LIC-LOCATION for Lotus Notes compatibility
- Swift 6 Sendable Compliance - Full structured concurrency support
- Foundation Integration - Leverages TimeZone, Calendar, and Date APIs
- Zero Code Duplication - Reuses Apple's robust calendar implementations
- Round-trip Compatibility - Serialize β Parse β Serialize produces identical output
- Comprehensive Testing - 100% test coverage with complex integration scenarios
The library includes comprehensive test suites with 100% coverage validating:
- Timezone handling accuracy - Critical timezone parsing, serialization, and TZID parameter support
- Round-trip serialization fidelity - Ensures no data loss during parse/serialize cycles
- Property parameter encoding - Proper escaping of special characters per RFC 6868
- Binary data handling - Base64 encoding/decoding with MIME type support
- RFC 5545 compliance - All component structures, properties, and validation rules
- RFC 7529 non-Gregorian calendars - Hebrew, Islamic, Chinese calendar recurrence calculations
- RFC 5546 iTIP workflows - Complete message validation for all iTIP methods
- RFC 9074 advanced alarms - Proximity triggers and acknowledgment tracking
- RFC 9073 event publishing - Structured data, venue, and resource component handling
- RFC 9253 relationships - Enhanced relationship types and external linking
- RFC 7953 availability - Free/busy time management and scheduling
- RFC 6047 iMIP transport - Email binding and MIME structure generation
- Complex multi-RFC scenarios - Events combining multiple RFC features
- Real-world usage patterns - Meeting invitations, resource booking, international calendars
- Error handling and edge cases - Malformed input, missing properties, timezone edge cases
- Memory efficiency - Optimized for server-side Swift applications
- Concurrency safety - Full Swift 6 structured concurrency support
- Large calendar handling - Efficient parsing and serialization of complex calendars
- Fork the repository
- Create a feature branch
- Add tests for new functionality
- Ensure all tests pass:
swift test - Submit a pull request
RFC Enhancement Contributions Welcome! If you'd like to implement support for additional RFCs, please:
- Open an issue to discuss the scope
- Reference the specific RFC sections
- Include comprehensive test coverage
- Follow existing architectural patterns
This project is licensed under the MIT License. See LICENSE for details.