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? {