Files
Calendarr/Sources/CalendarrCore/AppGroup.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

32 lines
1.4 KiB
Swift
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import Foundation
/// The App Group the Calendarr apps share.
///
/// The identifier registered in the Apple Developer portal never changes. What
/// changes is the string the *runtime* expects: macOS — including Mac Catalyst —
/// requires the Team ID prefix, and iOS forbids it.
///
/// Getting this wrong fails silently. `containerURL(forSecurityApplication‑
/// GroupIdentifier:)` returns nil, every read and write no-ops, and widgets show
/// placeholder content forever with no error raised anywhere. `current` must
/// therefore stay byte-identical to `com.apple.security.application-groups` in
/// the entitlements file used for the platform being built.
public enum CalendarrAppGroup {
/// As registered in the Apple Developer portal.
public static let unprefixed = "group.com.scarriffleservices.calendarr"
/// Team ID — the value `$(AppIdentifierPrefix)` expands to at build time.
public static let teamID = "PP34X97WS3"
/// The value to hand to `FileManager.containerURL(forSecurityApplicationGroupIdentifier:)`.
#if os(macOS) || targetEnvironment(macCatalyst)
public static let current = "\(teamID).\(unprefixed)"
#else
public static let current = unprefixed
#endif
/// Keychain access group shared across apps from this team. Requires
/// `keychain-access-groups` in the entitlements of every app that uses it.
public static let sharedKeychainGroup = "\(teamID).com.scarriffleservices.calendarr.shared"
}