Skip to content

CleanCocoa/swift-icalendar

Repository files navigation

swift-icalendar: Sample implementation of RFC 5545

For the formal specification, see RFC 5545 (there's a copy in this repository).

ics-lint

Demonstration of the package's capabilities: A command-line tool for validating iCalendar files against RFC 5545.

Installation

# Build from source
swift build -c release

# The binary is at .build/release/ics-lint
# Optionally copy to your PATH:
cp .build/release/ics-lint /usr/local/bin/

To build and run immediately:

swift run ics-lint ...

Usage

# Validate a file
ics-lint calendar.ics

# Validate multiple files
ics-lint *.ics

# Show context lines (like ripgrep): 1 before, 2 after
ics-lint -B 1 -A 2 calendar.ics

# JSON output for CI/tooling
ics-lint --format json calendar.ics

# Strict mode: treat warnings as errors
ics-lint --strict calendar.ics

# Quiet mode: only show errors
ics-lint --quiet calendar.ics

# Auto-fix safe issues and output corrected ICS
ics-lint --fix calendar.ics > fixed.ics

Exit Codes

Code Meaning
0 Success (no errors; warnings allowed unless --strict)
1 Validation errors found
2 File not found or unreadable

Example Output

calendar.ics - 0 errors, 2 warnings

WARNING line 9: PRIORITY value 15 is out of range (0-9); clamped to 9
 │
9│ PRIORITY:15
 │
  in VEVENT → PRIORITY

WARNING line 10: SUMMARY value has leading whitespace; trimmed
  │
10│ SUMMARY:  Leading whitespace test
  │
  in VEVENT → SUMMARY

─────────────────────────────────────────────────────────────────
Summary: 1 VCALENDAR, 1 VEVENT | 0 errors, 2 warnings

Library Usage

Add to your Package.swift:

dependencies: [
    .package(url: "https://github.com/your-org/swift-icalendar", from: "1.0.0"),
],
targets: [
    .target(
        name: "YourTarget",
        dependencies: [
            .product(name: "ICalendar", package: "swift-icalendar"),
        ]
    ),
]

Parse and validate:

import ICalendar

let ics = """
BEGIN:VCALENDAR
VERSION:2.0
PRODID:-//Example//EN
BEGIN:VEVENT
UID:[email protected]
DTSTAMP:20250115T080000Z
DTSTART:20250115T090000Z
SUMMARY:Team Meeting
END:VEVENT
END:VCALENDAR
"""

let result = try VCalendar.parse(ics)

switch result {
case .valid(let calendar, let diagnostics):
    for event in calendar.events {
        print(event.summary?.value ?? "No title")
    }
    for diagnostic in diagnostics {
        print("\(diagnostic.severity): \(diagnostic.message)")
    }
case .recovered(let calendar, let diagnostics):
    print("Parsed with \(diagnostics.count) recoverable issues")
case .failed(let diagnostics):
    print("Parse failed: \(diagnostics.first?.message ?? "unknown")")
}

About

GitHub mirror of swift-icalendar

Topics

Resources

Stars

Watchers

Forks

Releases

No releases published

Contributors 3

  •  
  •  
  •  

Languages