Add the watch app and its eight complications

The watch receives the phone's payload over WatchConnectivity and writes it
into its own App Group container through the same SnapshotStore, so there is
one wire format and one reader. The agenda, the detail screen and the filter
all read that; the complications read what the app last wrote, since an
extension cannot run a WCSession of its own.

Complication timelines land on event boundaries rather than a schedule, so the
next appointment flips in the exact minute it changes without spending refresh
budget.

CalendarrWatchShared holds what both watch targets need, attached to each the
way Shared/ is attached to the app and the widgets.

recommendations() is implemented because watchOS requires it — iOS has a
default, the watch does not, since the complication gallery has to be
populated. Found by building, not by reading.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
Scarriffle
2026-09-11 17:08:27 +02:00
co-authored by Claude Opus 5
parent 6a7afe0647
commit f9bc1965a5
23 changed files with 1190 additions and 223 deletions
+40 -32
View File
@@ -10,13 +10,13 @@
3934CF4630544C2E003958B8 /* WidgetKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 3934CF4530544C2E003958B8 /* WidgetKit.framework */; };
3934CF4830544C2E003958B8 /* SwiftUI.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 3934CF4730544C2E003958B8 /* SwiftUI.framework */; };
3934CF5530544C2E003958B8 /* CalendarrWatchComplications.appex in Embed Foundation Extensions */ = {isa = PBXBuildFile; fileRef = 3934CF4330544C2E003958B8 /* CalendarrWatchComplications.appex */; settings = {ATTRIBUTES = (RemoveHeadersOnCopy, ); }; };
39E1B0010001000000000001 /* CalendarrWatch.app in Embed Watch Content */ = {isa = PBXBuildFile; fileRef = 3934CF1A30544C01003958B8 /* CalendarrWatch.app */; settings = {ATTRIBUTES = (RemoveHeadersOnCopy, ); }; };
AA9701000001000000000001 /* AppAuth in Frameworks */ = {isa = PBXBuildFile; productRef = AA9701000002000000000001 /* AppAuth */; };
CA1E4D0A0002000000000001 /* CalendarrCore in Frameworks */ = {isa = PBXBuildFile; productRef = CA1E4D0A0001000000000001 /* CalendarrCore */; };
CA1E4D0A0002000000000002 /* CalendarrCore in Frameworks */ = {isa = PBXBuildFile; productRef = CA1E4D0A0001000000000002 /* CalendarrCore */; };
CA1E4D0A0002000000000003 /* CalendarrCore in Frameworks */ = {isa = PBXBuildFile; productRef = CA1E4D0A0001000000000003 /* CalendarrCore */; };
CA1E4D0A0002000000000004 /* CalendarrCore in Frameworks */ = {isa = PBXBuildFile; productRef = CA1E4D0A0001000000000004 /* CalendarrCore */; };
D0001A0CABCDEF0100AB5001 /* CalendarrWidgets.appex in Embed Foundation Extensions */ = {isa = PBXBuildFile; fileRef = D0001A02ABCDEF0100AB5001 /* CalendarrWidgets.appex */; settings = {ATTRIBUTES = (RemoveHeadersOnCopy, ); }; };
39E1B0010001000000000001 /* CalendarrWatch.app in Embed Watch Content */ = {isa = PBXBuildFile; fileRef = 3934CF1A30544C01003958B8 /* CalendarrWatch.app */; settings = {ATTRIBUTES = (RemoveHeadersOnCopy, ); }; };
/* End PBXBuildFile section */
/* Begin PBXContainerItemProxy section */
@@ -48,13 +48,6 @@
remoteGlobalIDString = 3934CF4230544C2E003958B8;
remoteInfo = CalendarrWatchComplications;
};
D0001A0DABCDEF0100AB5001 /* PBXContainerItemProxy */ = {
isa = PBXContainerItemProxy;
containerPortal = C0000301FB4E10100AB5001 /* Project object */;
proxyType = 1;
remoteGlobalIDString = D0001A01ABCDEF0100AB5001;
remoteInfo = CalendarrWidgets;
};
39E1B0010003000000000001 /* PBXContainerItemProxy */ = {
isa = PBXContainerItemProxy;
containerPortal = C0000301FB4E10100AB5001 /* Project object */;
@@ -62,6 +55,13 @@
remoteGlobalIDString = 3934CF1930544C01003958B8;
remoteInfo = CalendarrWatch;
};
D0001A0DABCDEF0100AB5001 /* PBXContainerItemProxy */ = {
isa = PBXContainerItemProxy;
containerPortal = C0000301FB4E10100AB5001 /* Project object */;
proxyType = 1;
remoteGlobalIDString = D0001A01ABCDEF0100AB5001;
remoteInfo = CalendarrWidgets;
};
/* End PBXContainerItemProxy section */
/* Begin PBXCopyFilesBuildPhase section */
@@ -76,17 +76,6 @@
name = "Embed Foundation Extensions";
runOnlyForDeploymentPostprocessing = 0;
};
D0001A0BABCDEF0100AB5001 /* Embed Foundation Extensions */ = {
isa = PBXCopyFilesBuildPhase;
buildActionMask = 2147483647;
dstPath = "";
dstSubfolderSpec = 13;
files = (
D0001A0CABCDEF0100AB5001 /* CalendarrWidgets.appex in Embed Foundation Extensions */,
);
name = "Embed Foundation Extensions";
runOnlyForDeploymentPostprocessing = 0;
};
39E1B0010002000000000001 /* Embed Watch Content */ = {
isa = PBXCopyFilesBuildPhase;
buildActionMask = 2147483647;
@@ -98,6 +87,17 @@
name = "Embed Watch Content";
runOnlyForDeploymentPostprocessing = 0;
};
D0001A0BABCDEF0100AB5001 /* Embed Foundation Extensions */ = {
isa = PBXCopyFilesBuildPhase;
buildActionMask = 2147483647;
dstPath = "";
dstSubfolderSpec = 13;
files = (
D0001A0CABCDEF0100AB5001 /* CalendarrWidgets.appex in Embed Foundation Extensions */,
);
name = "Embed Foundation Extensions";
runOnlyForDeploymentPostprocessing = 0;
};
/* End PBXCopyFilesBuildPhase section */
/* Begin PBXFileReference section */
@@ -158,6 +158,11 @@
path = CalendarrWatchComplications;
sourceTree = "<group>";
};
39E1B0010005000000000001 /* CalendarrWatchShared */ = {
isa = PBXFileSystemSynchronizedRootGroup;
path = CalendarrWatchShared;
sourceTree = "<group>";
};
C0000D01FC4E10100AB5001 /* Calendarr iOS */ = {
isa = PBXFileSystemSynchronizedRootGroup;
path = "Calendarr iOS";
@@ -260,6 +265,7 @@
3934CF4930544C2E003958B8 /* CalendarrWatchComplications */,
3934CF4430544C2E003958B8 /* Frameworks */,
C0000C01FB4E10100AB5001 /* Products */,
39E1B0010005000000000001 /* CalendarrWatchShared */,
);
sourceTree = "<group>";
};
@@ -319,6 +325,7 @@
);
fileSystemSynchronizedGroups = (
3934CF1B30544C01003958B8 /* CalendarrWatch */,
39E1B0010005000000000001 /* CalendarrWatchShared */,
);
name = CalendarrWatch;
packageProductDependencies = (
@@ -388,6 +395,7 @@
);
fileSystemSynchronizedGroups = (
3934CF4930544C2E003958B8 /* CalendarrWatchComplications */,
39E1B0010005000000000001 /* CalendarrWatchShared */,
);
name = CalendarrWatchComplications;
packageProductDependencies = (
@@ -641,16 +649,16 @@
target = 3934CF4230544C2E003958B8 /* CalendarrWatchComplications */;
targetProxy = 3934CF5330544C2E003958B8 /* PBXContainerItemProxy */;
};
D0001A0EABCDEF0100AB5001 /* PBXTargetDependency */ = {
isa = PBXTargetDependency;
target = D0001A01ABCDEF0100AB5001 /* CalendarrWidgets */;
targetProxy = D0001A0DABCDEF0100AB5001 /* PBXContainerItemProxy */;
};
39E1B0010004000000000001 /* PBXTargetDependency */ = {
isa = PBXTargetDependency;
target = 3934CF1930544C01003958B8 /* CalendarrWatch */;
targetProxy = 39E1B0010003000000000001 /* PBXContainerItemProxy */;
};
D0001A0EABCDEF0100AB5001 /* PBXTargetDependency */ = {
isa = PBXTargetDependency;
target = D0001A01ABCDEF0100AB5001 /* CalendarrWidgets */;
targetProxy = D0001A0DABCDEF0100AB5001 /* PBXContainerItemProxy */;
};
/* End PBXTargetDependency section */
/* Begin XCBuildConfiguration section */
@@ -709,6 +717,7 @@
buildSettings = {
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor;
CODE_SIGN_ENTITLEMENTS = CalendarrWatch/CalendarrWatch.entitlements;
CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 1;
DEVELOPMENT_TEAM = PP34X97WS3;
@@ -722,7 +731,6 @@
"@executable_path/Frameworks",
);
MARKETING_VERSION = 1.0;
CODE_SIGN_ENTITLEMENTS = CalendarrWatch/CalendarrWatch.entitlements;
PRODUCT_BUNDLE_IDENTIFIER = com.scarriffleservices.calendarr.ios.watchkitapp;
PRODUCT_NAME = "$(TARGET_NAME)";
SDKROOT = watchos;
@@ -743,6 +751,7 @@
buildSettings = {
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor;
CODE_SIGN_ENTITLEMENTS = CalendarrWatch/CalendarrWatch.entitlements;
CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 1;
DEVELOPMENT_TEAM = PP34X97WS3;
@@ -756,7 +765,6 @@
"@executable_path/Frameworks",
);
MARKETING_VERSION = 1.0;
CODE_SIGN_ENTITLEMENTS = CalendarrWatch/CalendarrWatch.entitlements;
PRODUCT_BUNDLE_IDENTIFIER = com.scarriffleservices.calendarr.ios.watchkitapp;
PRODUCT_NAME = "$(TARGET_NAME)";
SDKROOT = watchos;
@@ -867,6 +875,7 @@
buildSettings = {
ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor;
ASSETCATALOG_COMPILER_WIDGET_BACKGROUND_COLOR_NAME = WidgetBackground;
CODE_SIGN_ENTITLEMENTS = CalendarrWatchComplications/CalendarrWatchComplications.entitlements;
CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 1;
DEVELOPMENT_TEAM = PP34X97WS3;
@@ -881,7 +890,6 @@
"@executable_path/../../../../Frameworks",
);
MARKETING_VERSION = 1.0;
CODE_SIGN_ENTITLEMENTS = CalendarrWatchComplications/CalendarrWatchComplications.entitlements;
PRODUCT_BUNDLE_IDENTIFIER = com.scarriffleservices.calendarr.ios.watchkitapp.complications;
PRODUCT_NAME = "$(TARGET_NAME)";
SDKROOT = watchos;
@@ -901,6 +909,7 @@
buildSettings = {
ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor;
ASSETCATALOG_COMPILER_WIDGET_BACKGROUND_COLOR_NAME = WidgetBackground;
CODE_SIGN_ENTITLEMENTS = CalendarrWatchComplications/CalendarrWatchComplications.entitlements;
CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 1;
DEVELOPMENT_TEAM = PP34X97WS3;
@@ -915,7 +924,6 @@
"@executable_path/../../../../Frameworks",
);
MARKETING_VERSION = 1.0;
CODE_SIGN_ENTITLEMENTS = CalendarrWatchComplications/CalendarrWatchComplications.entitlements;
PRODUCT_BUNDLE_IDENTIFIER = com.scarriffleservices.calendarr.ios.watchkitapp.complications;
PRODUCT_NAME = "$(TARGET_NAME)";
SDKROOT = watchos;
@@ -1329,6 +1337,10 @@
isa = XCSwiftPackageProductDependency;
productName = CalendarrCore;
};
CA1E4D0A0001000000000002 /* CalendarrCore */ = {
isa = XCSwiftPackageProductDependency;
productName = CalendarrCore;
};
CA1E4D0A0001000000000003 /* CalendarrCore */ = {
isa = XCSwiftPackageProductDependency;
productName = CalendarrCore;
@@ -1337,10 +1349,6 @@
isa = XCSwiftPackageProductDependency;
productName = CalendarrCore;
};
CA1E4D0A0001000000000002 /* CalendarrCore */ = {
isa = XCSwiftPackageProductDependency;
productName = CalendarrCore;
};
/* End XCSwiftPackageProductDependency section */
};
rootObject = C0000301FB4E10100AB5001 /* Project object */;