Files
Calendarr/Package.swift
T
ScarriffleandClaude Opus 5 ab2328969e Add CalendarrCore: the shared read contract for the calendar snapshot
Calendarr already publishes an event snapshot into its App Group container for
the widgets to render. That channel is what a second Mac app needs too, so this
turns an internal detail into a documented contract rather than something a
second app reverse-engineers and then drifts from.

Foundation only — no SwiftUI, no UIKit, no AppKit — so one source compiles for
iOS, Mac Catalyst and native macOS. Building against the native macOS SDK is the
point: it fails loudly on a UIKit leak that a Catalyst build would accept.

Three things earn their place here rather than being copied per app:

CalendarrAppGroup resolves the container identifier per platform. macOS requires
the Team ID prefix and iOS forbids it, and a mismatch fails silently — nil
container, every read and write a no-op, widgets blank forever.

CalendarKey normalises the two ways the server reports calendar_id (bare for
CalDAV, "local-3" style for everything else). Normalising them differently is
what made per-calendar filtering quietly do nothing for some sources.

The snapshot schema is versioned, with the coverage window now explicit. That
window is the easiest thing to get wrong downstream: outside it the snapshot
carries no information, which is not the same as no events, and rendering an
empty week for an uncovered date produces a screen that looks right and is
false. covers() exists so consumers can tell the two apart.

Reads return a named result rather than an optional. "Never ran", "signed out",
"too new to read" and "unreadable" need different messages, and a bare nil is
how the App Group misconfiguration used to hide.

Writing is behind @_spi(Writer) so a read-only consumer cannot corrupt the
shared cache. Writes post a Darwin notification, the one cross-process signal
that works between two sandboxed apps without an extra entitlement.

The six colour values stay flat on the wire instead of nesting under `theme`,
which would read better. A v1 reader requires them flat, so nesting would have
broken every older build on the first v2 write; `theme` is a computed view.
Tests pin both directions of compatibility, since the two apps ship
independently.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-10 17:30:23 +02:00

34 lines
1.2 KiB
Swift

// swift-tools-version: 6.0
import PackageDescription
let package = Package(
name: "CalendarrKit",
platforms: [
.iOS(.v17),
.macOS(.v14),
.macCatalyst(.v17),
],
products: [
// Foundation only — deliberately no SwiftUI, no UIKit, no AppKit, so it
// compiles for iOS, Mac Catalyst and native macOS from one source.
.library(name: "CalendarrCore", targets: ["CalendarrCore"]),
],
targets: [
.target(
name: "CalendarrCore",
// Matches the apps, which build in Swift 5 language mode. Note that
// this package deliberately does NOT set defaultIsolation(MainActor),
// even though the Calendarr iOS app target does: the widget extension
// and the read-only consumer app have no MainActor default, and making
// the package MainActor-by-default would force `await` at every
// SnapshotStore.read() call site.
swiftSettings: [.swiftLanguageMode(.v5)]
),
.testTarget(
name: "CalendarrCoreTests",
dependencies: ["CalendarrCore"],
swiftSettings: [.swiftLanguageMode(.v5)]
),
]
)