diff --git a/Calendarr iOS.xcodeproj/project.pbxproj b/Calendarr iOS.xcodeproj/project.pbxproj
index b2b3269..a3e28b3 100644
--- a/Calendarr iOS.xcodeproj/project.pbxproj
+++ b/Calendarr iOS.xcodeproj/project.pbxproj
@@ -490,10 +490,12 @@
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor;
CODE_SIGN_ENTITLEMENTS = "Calendarr iOS/Calendarr iOS.entitlements";
+ "CODE_SIGN_ENTITLEMENTS[sdk=macosx*]" = "Calendarr iOS/Calendarr iOS-Catalyst.entitlements";
CODE_SIGN_STYLE = Automatic;
DEVELOPMENT_TEAM = PP34X97WS3;
ENABLE_PREVIEWS = YES;
GENERATE_INFOPLIST_FILE = YES;
+ ENABLE_HARDENED_RUNTIME = YES;
INFOPLIST_KEY_CFBundleDisplayName = Calendarr;
INFOPLIST_KEY_CFBundleName = Calendarr;
INFOPLIST_KEY_ITSAppUsesNonExemptEncryption = NO;
@@ -533,10 +535,12 @@
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor;
CODE_SIGN_ENTITLEMENTS = "Calendarr iOS/Calendarr iOS.entitlements";
+ "CODE_SIGN_ENTITLEMENTS[sdk=macosx*]" = "Calendarr iOS/Calendarr iOS-Catalyst.entitlements";
CODE_SIGN_STYLE = Automatic;
DEVELOPMENT_TEAM = PP34X97WS3;
ENABLE_PREVIEWS = YES;
GENERATE_INFOPLIST_FILE = YES;
+ ENABLE_HARDENED_RUNTIME = YES;
INFOPLIST_KEY_CFBundleDisplayName = Calendarr;
INFOPLIST_KEY_CFBundleName = Calendarr;
INFOPLIST_KEY_ITSAppUsesNonExemptEncryption = NO;
@@ -575,6 +579,7 @@
buildSettings = {
ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES;
CODE_SIGN_ENTITLEMENTS = CalendarrWidgets/CalendarrWidgets.entitlements;
+ "CODE_SIGN_ENTITLEMENTS[sdk=macosx*]" = "CalendarrWidgets/CalendarrWidgets-Catalyst.entitlements";
CODE_SIGN_STYLE = Automatic;
DEVELOPMENT_TEAM = PP34X97WS3;
GENERATE_INFOPLIST_FILE = NO;
@@ -608,6 +613,7 @@
buildSettings = {
ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES;
CODE_SIGN_ENTITLEMENTS = CalendarrWidgets/CalendarrWidgets.entitlements;
+ "CODE_SIGN_ENTITLEMENTS[sdk=macosx*]" = "CalendarrWidgets/CalendarrWidgets-Catalyst.entitlements";
CODE_SIGN_STYLE = Automatic;
DEVELOPMENT_TEAM = PP34X97WS3;
GENERATE_INFOPLIST_FILE = NO;
diff --git a/Calendarr iOS/Calendarr iOS-Catalyst.entitlements b/Calendarr iOS/Calendarr iOS-Catalyst.entitlements
new file mode 100644
index 0000000..f8ac65f
--- /dev/null
+++ b/Calendarr iOS/Calendarr iOS-Catalyst.entitlements
@@ -0,0 +1,46 @@
+
+
+
+
+
+ com.apple.security.app-sandbox
+
+
+
+ com.apple.security.network.client
+
+
+
+ com.apple.security.personal-information.addressbook
+
+
+
+ com.apple.security.files.user-selected.read-write
+
+
+
+ com.apple.security.application-groups
+
+ PP34X97WS3.group.com.scarriffleservices.calendarr
+
+
+
+ keychain-access-groups
+
+ $(AppIdentifierPrefix)com.scarriffleservices.calendarr.ios
+ $(AppIdentifierPrefix)com.scarriffleservices.calendarr.shared
+
+
+
diff --git a/CalendarrWidgets/CalendarrWidgets-Catalyst.entitlements b/CalendarrWidgets/CalendarrWidgets-Catalyst.entitlements
new file mode 100644
index 0000000..8d85469
--- /dev/null
+++ b/CalendarrWidgets/CalendarrWidgets-Catalyst.entitlements
@@ -0,0 +1,19 @@
+
+
+
+
+
+ com.apple.security.app-sandbox
+
+
+
+ com.apple.security.application-groups
+
+ PP34X97WS3.group.com.scarriffleservices.calendarr
+
+
+
diff --git a/Shared/WidgetData.swift b/Shared/WidgetData.swift
index b78bcf6..0a4aea0 100644
--- a/Shared/WidgetData.swift
+++ b/Shared/WidgetData.swift
@@ -4,9 +4,30 @@ import WidgetKit
#endif
/// App-Group identifier shared between the main app and the widget extension.
-/// IMPORTANT: This must match the App Group capability in BOTH targets
-/// and the App Group ID registered in the Apple Developer Portal.
-let widgetAppGroupID = "group.com.scarriffleservices.calendarr"
+///
+/// IMPORTANT: this must stay byte-identical to `com.apple.security.application-groups`
+/// in the matching .entitlements file for the platform being built, and match the
+/// App Group registered in the Apple Developer portal.
+///
+/// The identifier registered in the portal never changes — only the string the
+/// *runtime* expects does. macOS (including Mac Catalyst) requires the Team ID
+/// prefix; iOS forbids it. Get this wrong and `containerURL(forSecurityApplication‑
+/// GroupIdentifier:)` returns nil, every read and write below quietly no-ops, and
+/// the widgets show placeholder content forever with no error anywhere.
+enum CalendarrAppGroup {
+ /// As registered in the Apple Developer portal.
+ static let unprefixed = "group.com.scarriffleservices.calendarr"
+ /// Team ID — the value `$(AppIdentifierPrefix)` expands to at build time.
+ static let teamID = "PP34X97WS3"
+
+ #if os(macOS) || targetEnvironment(macCatalyst)
+ static let current = "\(teamID).\(unprefixed)"
+ #else
+ static let current = unprefixed
+ #endif
+}
+
+let widgetAppGroupID = CalendarrAppGroup.current
/// Lightweight calendar descriptor stored alongside the event cache so the
/// widget configuration intent can offer calendar options without a network call.
@@ -108,7 +129,18 @@ enum WidgetStore {
private static let calendarsFilename = "widget-calendars.json"
private static var containerURL: URL? {
- FileManager.default.containerURL(forSecurityApplicationGroupIdentifier: widgetAppGroupID)
+ let url = FileManager.default.containerURL(forSecurityApplicationGroupIdentifier: widgetAppGroupID)
+ #if DEBUG
+ if url == nil {
+ // A nil container is always a build-configuration bug: the entitlement
+ // is missing, or its value does not match widgetAppGroupID. It is
+ // otherwise completely silent, so make it loud while developing.
+ assertionFailure("App Group container unavailable for \(widgetAppGroupID) — "
+ + "check com.apple.security.application-groups in the "
+ + "entitlements for this platform.")
+ }
+ #endif
+ return url
}
private static var cacheURL: URL? {