Archived
Compare commits
14
Commits
ddda0ecd3e
..
main
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
31d7a0080e | ||
|
|
e5b3e549e8 | ||
|
|
465c465af2 | ||
|
|
6e91eaaaa2 | ||
|
|
1a1230a796 | ||
|
|
2c26be2ab5 | ||
|
|
938ca7ca98 | ||
|
|
b489f10c28 | ||
|
|
fdae680e02 | ||
|
|
e7fbe5c092 | ||
|
|
d0cff20be8 | ||
|
|
f9bc1965a5 | ||
|
|
6a7afe0647 | ||
|
|
9394a80ea2 |
@@ -7,10 +7,16 @@
|
||||
objects = {
|
||||
|
||||
/* Begin PBXBuildFile section */
|
||||
D0001A0CABCDEF0100AB5001 /* CalendarrWidgets.appex in Embed Foundation Extensions */ = {isa = PBXBuildFile; fileRef = D0001A02ABCDEF0100AB5001 /* CalendarrWidgets.appex */; settings = {ATTRIBUTES = (RemoveHeadersOnCopy, ); }; };
|
||||
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 */; platformFilters = (ios, ); 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 */; };
|
||||
AA9701000001000000000001 /* AppAuth in Frameworks */ = {isa = PBXBuildFile; productRef = AA9701000002000000000001 /* AppAuth */; };
|
||||
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, ); }; };
|
||||
/* End PBXBuildFile section */
|
||||
|
||||
/* Begin PBXContainerItemProxy section */
|
||||
@@ -21,6 +27,34 @@
|
||||
remoteGlobalIDString = C0000A01FB4E10100AB5001;
|
||||
remoteInfo = "Calendarr iOS";
|
||||
};
|
||||
3934CF2730544C02003958B8 /* PBXContainerItemProxy */ = {
|
||||
isa = PBXContainerItemProxy;
|
||||
containerPortal = C0000301FB4E10100AB5001 /* Project object */;
|
||||
proxyType = 1;
|
||||
remoteGlobalIDString = 3934CF1930544C01003958B8;
|
||||
remoteInfo = CalendarrWatch;
|
||||
};
|
||||
3934CF3130544C02003958B8 /* PBXContainerItemProxy */ = {
|
||||
isa = PBXContainerItemProxy;
|
||||
containerPortal = C0000301FB4E10100AB5001 /* Project object */;
|
||||
proxyType = 1;
|
||||
remoteGlobalIDString = 3934CF1930544C01003958B8;
|
||||
remoteInfo = CalendarrWatch;
|
||||
};
|
||||
3934CF5330544C2E003958B8 /* PBXContainerItemProxy */ = {
|
||||
isa = PBXContainerItemProxy;
|
||||
containerPortal = C0000301FB4E10100AB5001 /* Project object */;
|
||||
proxyType = 1;
|
||||
remoteGlobalIDString = 3934CF4230544C2E003958B8;
|
||||
remoteInfo = CalendarrWatchComplications;
|
||||
};
|
||||
39E1B0010003000000000001 /* PBXContainerItemProxy */ = {
|
||||
isa = PBXContainerItemProxy;
|
||||
containerPortal = C0000301FB4E10100AB5001 /* Project object */;
|
||||
proxyType = 1;
|
||||
remoteGlobalIDString = 3934CF1930544C01003958B8;
|
||||
remoteInfo = CalendarrWatch;
|
||||
};
|
||||
D0001A0DABCDEF0100AB5001 /* PBXContainerItemProxy */ = {
|
||||
isa = PBXContainerItemProxy;
|
||||
containerPortal = C0000301FB4E10100AB5001 /* Project object */;
|
||||
@@ -31,6 +65,28 @@
|
||||
/* End PBXContainerItemProxy section */
|
||||
|
||||
/* Begin PBXCopyFilesBuildPhase section */
|
||||
3934CF5A30544C2E003958B8 /* Embed Foundation Extensions */ = {
|
||||
isa = PBXCopyFilesBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
dstPath = "";
|
||||
dstSubfolderSpec = 13;
|
||||
files = (
|
||||
3934CF5530544C2E003958B8 /* CalendarrWatchComplications.appex in Embed Foundation Extensions */,
|
||||
);
|
||||
name = "Embed Foundation Extensions";
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
39E1B0010002000000000001 /* Embed Watch Content */ = {
|
||||
isa = PBXCopyFilesBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
dstPath = "$(CONTENTS_FOLDER_PATH)/Watch";
|
||||
dstSubfolderSpec = 16;
|
||||
files = (
|
||||
39E1B0010001000000000001 /* CalendarrWatch.app in Embed Watch Content */,
|
||||
);
|
||||
name = "Embed Watch Content";
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
D0001A0BABCDEF0100AB5001 /* Embed Foundation Extensions */ = {
|
||||
isa = PBXCopyFilesBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
@@ -46,11 +102,24 @@
|
||||
|
||||
/* Begin PBXFileReference section */
|
||||
3927C7BC2FB99D0E00EAD8ED /* Calendarr iOSTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = "Calendarr iOSTests.xctest"; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||
3934CF1A30544C01003958B8 /* CalendarrWatch.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = CalendarrWatch.app; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||
3934CF2630544C02003958B8 /* CalendarrWatchTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = CalendarrWatchTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||
3934CF3030544C02003958B8 /* CalendarrWatchUITests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = CalendarrWatchUITests.xctest; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||
3934CF4330544C2E003958B8 /* CalendarrWatchComplications.appex */ = {isa = PBXFileReference; explicitFileType = "wrapper.app-extension"; includeInIndex = 0; path = CalendarrWatchComplications.appex; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||
3934CF4530544C2E003958B8 /* WidgetKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = WidgetKit.framework; path = System/Library/Frameworks/WidgetKit.framework; sourceTree = SDKROOT; };
|
||||
3934CF4730544C2E003958B8 /* SwiftUI.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = SwiftUI.framework; path = System/Library/Frameworks/SwiftUI.framework; sourceTree = SDKROOT; };
|
||||
C0000B01FC4E10100AB5001 /* Calendarr iOS.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = "Calendarr iOS.app"; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||
D0001A02ABCDEF0100AB5001 /* CalendarrWidgets.appex */ = {isa = PBXFileReference; explicitFileType = "wrapper.app-extension"; includeInIndex = 0; path = CalendarrWidgets.appex; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||
/* End PBXFileReference section */
|
||||
|
||||
/* Begin PBXFileSystemSynchronizedBuildFileExceptionSet section */
|
||||
3934CF5630544C2E003958B8 /* Exceptions for "CalendarrWatchComplications" folder in "CalendarrWatchComplications" target */ = {
|
||||
isa = PBXFileSystemSynchronizedBuildFileExceptionSet;
|
||||
membershipExceptions = (
|
||||
Info.plist,
|
||||
);
|
||||
target = 3934CF4230544C2E003958B8 /* CalendarrWatchComplications */;
|
||||
};
|
||||
D0001A0FABCDEF0100AB5001 /* Exceptions for "CalendarrWidgets" folder in "CalendarrWidgets" target */ = {
|
||||
isa = PBXFileSystemSynchronizedBuildFileExceptionSet;
|
||||
membershipExceptions = (
|
||||
@@ -66,6 +135,34 @@
|
||||
path = "Calendarr iOSTests";
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
3934CF1B30544C01003958B8 /* CalendarrWatch */ = {
|
||||
isa = PBXFileSystemSynchronizedRootGroup;
|
||||
path = CalendarrWatch;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
3934CF2930544C02003958B8 /* CalendarrWatchTests */ = {
|
||||
isa = PBXFileSystemSynchronizedRootGroup;
|
||||
path = CalendarrWatchTests;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
3934CF3330544C02003958B8 /* CalendarrWatchUITests */ = {
|
||||
isa = PBXFileSystemSynchronizedRootGroup;
|
||||
path = CalendarrWatchUITests;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
3934CF4930544C2E003958B8 /* CalendarrWatchComplications */ = {
|
||||
isa = PBXFileSystemSynchronizedRootGroup;
|
||||
exceptions = (
|
||||
3934CF5630544C2E003958B8 /* Exceptions for "CalendarrWatchComplications" folder in "CalendarrWatchComplications" target */,
|
||||
);
|
||||
path = CalendarrWatchComplications;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
39E1B0010005000000000001 /* CalendarrWatchShared */ = {
|
||||
isa = PBXFileSystemSynchronizedRootGroup;
|
||||
path = CalendarrWatchShared;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
C0000D01FC4E10100AB5001 /* Calendarr iOS */ = {
|
||||
isa = PBXFileSystemSynchronizedRootGroup;
|
||||
path = "Calendarr iOS";
|
||||
@@ -94,6 +191,38 @@
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
3934CF1730544C01003958B8 /* Frameworks */ = {
|
||||
isa = PBXFrameworksBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
CA1E4D0A0002000000000003 /* CalendarrCore in Frameworks */,
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
3934CF2330544C02003958B8 /* Frameworks */ = {
|
||||
isa = PBXFrameworksBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
3934CF2D30544C02003958B8 /* Frameworks */ = {
|
||||
isa = PBXFrameworksBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
3934CF4030544C2E003958B8 /* Frameworks */ = {
|
||||
isa = PBXFrameworksBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
3934CF4830544C2E003958B8 /* SwiftUI.framework in Frameworks */,
|
||||
3934CF4630544C2E003958B8 /* WidgetKit.framework in Frameworks */,
|
||||
CA1E4D0A0002000000000004 /* CalendarrCore in Frameworks */,
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
C0000801FB4E10100AB5001 /* Frameworks */ = {
|
||||
isa = PBXFrameworksBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
@@ -114,6 +243,15 @@
|
||||
/* End PBXFrameworksBuildPhase section */
|
||||
|
||||
/* Begin PBXGroup section */
|
||||
3934CF4430544C2E003958B8 /* Frameworks */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
3934CF4530544C2E003958B8 /* WidgetKit.framework */,
|
||||
3934CF4730544C2E003958B8 /* SwiftUI.framework */,
|
||||
);
|
||||
name = Frameworks;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
C0000201FB4E10100AB5001 = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
@@ -121,7 +259,13 @@
|
||||
D0001A03ABCDEF0100AB5001 /* CalendarrWidgets */,
|
||||
D0001A04ABCDEF0100AB5001 /* Shared */,
|
||||
3927C7BD2FB99D0F00EAD8ED /* Calendarr iOSTests */,
|
||||
3934CF1B30544C01003958B8 /* CalendarrWatch */,
|
||||
3934CF2930544C02003958B8 /* CalendarrWatchTests */,
|
||||
3934CF3330544C02003958B8 /* CalendarrWatchUITests */,
|
||||
3934CF4930544C2E003958B8 /* CalendarrWatchComplications */,
|
||||
3934CF4430544C2E003958B8 /* Frameworks */,
|
||||
C0000C01FB4E10100AB5001 /* Products */,
|
||||
39E1B0010005000000000001 /* CalendarrWatchShared */,
|
||||
);
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
@@ -131,6 +275,10 @@
|
||||
C0000B01FC4E10100AB5001 /* Calendarr iOS.app */,
|
||||
3927C7BC2FB99D0E00EAD8ED /* Calendarr iOSTests.xctest */,
|
||||
D0001A02ABCDEF0100AB5001 /* CalendarrWidgets.appex */,
|
||||
3934CF1A30544C01003958B8 /* CalendarrWatch.app */,
|
||||
3934CF2630544C02003958B8 /* CalendarrWatchTests.xctest */,
|
||||
3934CF3030544C02003958B8 /* CalendarrWatchUITests.xctest */,
|
||||
3934CF4330544C2E003958B8 /* CalendarrWatchComplications.appex */,
|
||||
);
|
||||
name = Products;
|
||||
sourceTree = "<group>";
|
||||
@@ -161,6 +309,102 @@
|
||||
productReference = 3927C7BC2FB99D0E00EAD8ED /* Calendarr iOSTests.xctest */;
|
||||
productType = "com.apple.product-type.bundle.unit-test";
|
||||
};
|
||||
3934CF1930544C01003958B8 /* CalendarrWatch */ = {
|
||||
isa = PBXNativeTarget;
|
||||
buildConfigurationList = 3934CF3C30544C02003958B8 /* Build configuration list for PBXNativeTarget "CalendarrWatch" */;
|
||||
buildPhases = (
|
||||
3934CF1630544C01003958B8 /* Sources */,
|
||||
3934CF1730544C01003958B8 /* Frameworks */,
|
||||
3934CF1830544C01003958B8 /* Resources */,
|
||||
3934CF5A30544C2E003958B8 /* Embed Foundation Extensions */,
|
||||
);
|
||||
buildRules = (
|
||||
);
|
||||
dependencies = (
|
||||
3934CF5430544C2E003958B8 /* PBXTargetDependency */,
|
||||
);
|
||||
fileSystemSynchronizedGroups = (
|
||||
3934CF1B30544C01003958B8 /* CalendarrWatch */,
|
||||
39E1B0010005000000000001 /* CalendarrWatchShared */,
|
||||
);
|
||||
name = CalendarrWatch;
|
||||
packageProductDependencies = (
|
||||
CA1E4D0A0001000000000003 /* CalendarrCore */,
|
||||
);
|
||||
productName = CalendarrWatch;
|
||||
productReference = 3934CF1A30544C01003958B8 /* CalendarrWatch.app */;
|
||||
productType = "com.apple.product-type.application";
|
||||
};
|
||||
3934CF2530544C02003958B8 /* CalendarrWatchTests */ = {
|
||||
isa = PBXNativeTarget;
|
||||
buildConfigurationList = 3934CF3D30544C02003958B8 /* Build configuration list for PBXNativeTarget "CalendarrWatchTests" */;
|
||||
buildPhases = (
|
||||
3934CF2230544C02003958B8 /* Sources */,
|
||||
3934CF2330544C02003958B8 /* Frameworks */,
|
||||
3934CF2430544C02003958B8 /* Resources */,
|
||||
);
|
||||
buildRules = (
|
||||
);
|
||||
dependencies = (
|
||||
3934CF2830544C02003958B8 /* PBXTargetDependency */,
|
||||
);
|
||||
fileSystemSynchronizedGroups = (
|
||||
3934CF2930544C02003958B8 /* CalendarrWatchTests */,
|
||||
);
|
||||
name = CalendarrWatchTests;
|
||||
packageProductDependencies = (
|
||||
);
|
||||
productName = CalendarrWatchTests;
|
||||
productReference = 3934CF2630544C02003958B8 /* CalendarrWatchTests.xctest */;
|
||||
productType = "com.apple.product-type.bundle.unit-test";
|
||||
};
|
||||
3934CF2F30544C02003958B8 /* CalendarrWatchUITests */ = {
|
||||
isa = PBXNativeTarget;
|
||||
buildConfigurationList = 3934CF3E30544C02003958B8 /* Build configuration list for PBXNativeTarget "CalendarrWatchUITests" */;
|
||||
buildPhases = (
|
||||
3934CF2C30544C02003958B8 /* Sources */,
|
||||
3934CF2D30544C02003958B8 /* Frameworks */,
|
||||
3934CF2E30544C02003958B8 /* Resources */,
|
||||
);
|
||||
buildRules = (
|
||||
);
|
||||
dependencies = (
|
||||
3934CF3230544C02003958B8 /* PBXTargetDependency */,
|
||||
);
|
||||
fileSystemSynchronizedGroups = (
|
||||
3934CF3330544C02003958B8 /* CalendarrWatchUITests */,
|
||||
);
|
||||
name = CalendarrWatchUITests;
|
||||
packageProductDependencies = (
|
||||
);
|
||||
productName = CalendarrWatchUITests;
|
||||
productReference = 3934CF3030544C02003958B8 /* CalendarrWatchUITests.xctest */;
|
||||
productType = "com.apple.product-type.bundle.ui-testing";
|
||||
};
|
||||
3934CF4230544C2E003958B8 /* CalendarrWatchComplications */ = {
|
||||
isa = PBXNativeTarget;
|
||||
buildConfigurationList = 3934CF5730544C2E003958B8 /* Build configuration list for PBXNativeTarget "CalendarrWatchComplications" */;
|
||||
buildPhases = (
|
||||
3934CF3F30544C2E003958B8 /* Sources */,
|
||||
3934CF4030544C2E003958B8 /* Frameworks */,
|
||||
3934CF4130544C2E003958B8 /* Resources */,
|
||||
);
|
||||
buildRules = (
|
||||
);
|
||||
dependencies = (
|
||||
);
|
||||
fileSystemSynchronizedGroups = (
|
||||
3934CF4930544C2E003958B8 /* CalendarrWatchComplications */,
|
||||
39E1B0010005000000000001 /* CalendarrWatchShared */,
|
||||
);
|
||||
name = CalendarrWatchComplications;
|
||||
packageProductDependencies = (
|
||||
CA1E4D0A0001000000000004 /* CalendarrCore */,
|
||||
);
|
||||
productName = CalendarrWatchComplications;
|
||||
productReference = 3934CF4330544C2E003958B8 /* CalendarrWatchComplications.appex */;
|
||||
productType = "com.apple.product-type.app-extension";
|
||||
};
|
||||
C0000A01FB4E10100AB5001 /* Calendarr iOS */ = {
|
||||
isa = PBXNativeTarget;
|
||||
buildConfigurationList = C0001601FB4E10100AB5001 /* Build configuration list for PBXNativeTarget "Calendarr iOS" */;
|
||||
@@ -169,11 +413,13 @@
|
||||
C0000801FB4E10100AB5001 /* Frameworks */,
|
||||
C0000901FB4E10100AB5001 /* Resources */,
|
||||
D0001A0BABCDEF0100AB5001 /* Embed Foundation Extensions */,
|
||||
39E1B0010002000000000001 /* Embed Watch Content */,
|
||||
);
|
||||
buildRules = (
|
||||
);
|
||||
dependencies = (
|
||||
D0001A0EABCDEF0100AB5001 /* PBXTargetDependency */,
|
||||
39E1B0010004000000000001 /* PBXTargetDependency */,
|
||||
);
|
||||
fileSystemSynchronizedGroups = (
|
||||
C0000D01FC4E10100AB5001 /* Calendarr iOS */,
|
||||
@@ -219,13 +465,27 @@
|
||||
isa = PBXProject;
|
||||
attributes = {
|
||||
BuildIndependentTargetsInParallel = 1;
|
||||
LastSwiftUpdateCheck = 2650;
|
||||
LastSwiftUpdateCheck = 2700;
|
||||
LastUpgradeCheck = 2640;
|
||||
TargetAttributes = {
|
||||
3927C7BB2FB99D0E00EAD8ED = {
|
||||
CreatedOnToolsVersion = 26.5;
|
||||
TestTargetID = C0000A01FB4E10100AB5001;
|
||||
};
|
||||
3934CF1930544C01003958B8 = {
|
||||
CreatedOnToolsVersion = 27.0;
|
||||
};
|
||||
3934CF2530544C02003958B8 = {
|
||||
CreatedOnToolsVersion = 27.0;
|
||||
TestTargetID = 3934CF1930544C01003958B8;
|
||||
};
|
||||
3934CF2F30544C02003958B8 = {
|
||||
CreatedOnToolsVersion = 27.0;
|
||||
TestTargetID = 3934CF1930544C01003958B8;
|
||||
};
|
||||
3934CF4230544C2E003958B8 = {
|
||||
CreatedOnToolsVersion = 27.0;
|
||||
};
|
||||
C0000A01FB4E10100AB5001 = {
|
||||
CreatedOnToolsVersion = 26.4.1;
|
||||
};
|
||||
@@ -256,6 +516,10 @@
|
||||
C0000A01FB4E10100AB5001 /* Calendarr iOS */,
|
||||
D0001A01ABCDEF0100AB5001 /* CalendarrWidgets */,
|
||||
3927C7BB2FB99D0E00EAD8ED /* Calendarr iOSTests */,
|
||||
3934CF1930544C01003958B8 /* CalendarrWatch */,
|
||||
3934CF2530544C02003958B8 /* CalendarrWatchTests */,
|
||||
3934CF2F30544C02003958B8 /* CalendarrWatchUITests */,
|
||||
3934CF4230544C2E003958B8 /* CalendarrWatchComplications */,
|
||||
);
|
||||
};
|
||||
/* End PBXProject section */
|
||||
@@ -268,6 +532,34 @@
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
3934CF1830544C01003958B8 /* Resources */ = {
|
||||
isa = PBXResourcesBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
3934CF2430544C02003958B8 /* Resources */ = {
|
||||
isa = PBXResourcesBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
3934CF2E30544C02003958B8 /* Resources */ = {
|
||||
isa = PBXResourcesBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
3934CF4130544C2E003958B8 /* Resources */ = {
|
||||
isa = PBXResourcesBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
C0000901FB4E10100AB5001 /* Resources */ = {
|
||||
isa = PBXResourcesBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
@@ -292,6 +584,34 @@
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
3934CF1630544C01003958B8 /* Sources */ = {
|
||||
isa = PBXSourcesBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
3934CF2230544C02003958B8 /* Sources */ = {
|
||||
isa = PBXSourcesBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
3934CF2C30544C02003958B8 /* Sources */ = {
|
||||
isa = PBXSourcesBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
3934CF3F30544C2E003958B8 /* Sources */ = {
|
||||
isa = PBXSourcesBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
C0000701FB4E10100AB5001 /* Sources */ = {
|
||||
isa = PBXSourcesBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
@@ -314,6 +634,29 @@
|
||||
target = C0000A01FB4E10100AB5001 /* Calendarr iOS */;
|
||||
targetProxy = 3927C7C02FB99D0F00EAD8ED /* PBXContainerItemProxy */;
|
||||
};
|
||||
3934CF2830544C02003958B8 /* PBXTargetDependency */ = {
|
||||
isa = PBXTargetDependency;
|
||||
target = 3934CF1930544C01003958B8 /* CalendarrWatch */;
|
||||
targetProxy = 3934CF2730544C02003958B8 /* PBXContainerItemProxy */;
|
||||
};
|
||||
3934CF3230544C02003958B8 /* PBXTargetDependency */ = {
|
||||
isa = PBXTargetDependency;
|
||||
target = 3934CF1930544C01003958B8 /* CalendarrWatch */;
|
||||
targetProxy = 3934CF3130544C02003958B8 /* PBXContainerItemProxy */;
|
||||
};
|
||||
3934CF5430544C2E003958B8 /* PBXTargetDependency */ = {
|
||||
isa = PBXTargetDependency;
|
||||
target = 3934CF4230544C2E003958B8 /* CalendarrWatchComplications */;
|
||||
targetProxy = 3934CF5330544C2E003958B8 /* PBXContainerItemProxy */;
|
||||
};
|
||||
39E1B0010004000000000001 /* PBXTargetDependency */ = {
|
||||
isa = PBXTargetDependency;
|
||||
platformFilters = (
|
||||
ios,
|
||||
);
|
||||
target = 3934CF1930544C01003958B8 /* CalendarrWatch */;
|
||||
targetProxy = 39E1B0010003000000000001 /* PBXContainerItemProxy */;
|
||||
};
|
||||
D0001A0EABCDEF0100AB5001 /* PBXTargetDependency */ = {
|
||||
isa = PBXTargetDependency;
|
||||
target = D0001A01ABCDEF0100AB5001 /* CalendarrWidgets */;
|
||||
@@ -372,6 +715,232 @@
|
||||
};
|
||||
name = Release;
|
||||
};
|
||||
3934CF3630544C02003958B8 /* Debug */ = {
|
||||
isa = XCBuildConfiguration;
|
||||
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 = 7;
|
||||
DEVELOPMENT_TEAM = PP34X97WS3;
|
||||
ENABLE_PREVIEWS = YES;
|
||||
GENERATE_INFOPLIST_FILE = YES;
|
||||
INFOPLIST_KEY_CFBundleDisplayName = Calendarr;
|
||||
INFOPLIST_KEY_UISupportedInterfaceOrientations = "UIInterfaceOrientationPortrait UIInterfaceOrientationPortraitUpsideDown";
|
||||
INFOPLIST_KEY_WKCompanionAppBundleIdentifier = com.scarriffleservices.calendarr.ios;
|
||||
LD_RUNPATH_SEARCH_PATHS = (
|
||||
"$(inherited)",
|
||||
"@executable_path/Frameworks",
|
||||
);
|
||||
MARKETING_VERSION = 3.5;
|
||||
PRODUCT_BUNDLE_IDENTIFIER = com.scarriffleservices.calendarr.ios.watchkitapp;
|
||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||
SDKROOT = watchos;
|
||||
SKIP_INSTALL = YES;
|
||||
STRING_CATALOG_GENERATE_SYMBOLS = YES;
|
||||
SWIFT_APPROACHABLE_CONCURRENCY = YES;
|
||||
SWIFT_DEFAULT_ACTOR_ISOLATION = MainActor;
|
||||
SWIFT_EMIT_LOC_STRINGS = YES;
|
||||
SWIFT_UPCOMING_FEATURE_MEMBER_IMPORT_VISIBILITY = YES;
|
||||
SWIFT_VERSION = 5.0;
|
||||
TARGETED_DEVICE_FAMILY = 4;
|
||||
WATCHOS_DEPLOYMENT_TARGET = 26.0;
|
||||
};
|
||||
name = Debug;
|
||||
};
|
||||
3934CF3730544C02003958B8 /* Release */ = {
|
||||
isa = XCBuildConfiguration;
|
||||
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 = 7;
|
||||
DEVELOPMENT_TEAM = PP34X97WS3;
|
||||
ENABLE_PREVIEWS = YES;
|
||||
GENERATE_INFOPLIST_FILE = YES;
|
||||
INFOPLIST_KEY_CFBundleDisplayName = Calendarr;
|
||||
INFOPLIST_KEY_UISupportedInterfaceOrientations = "UIInterfaceOrientationPortrait UIInterfaceOrientationPortraitUpsideDown";
|
||||
INFOPLIST_KEY_WKCompanionAppBundleIdentifier = com.scarriffleservices.calendarr.ios;
|
||||
LD_RUNPATH_SEARCH_PATHS = (
|
||||
"$(inherited)",
|
||||
"@executable_path/Frameworks",
|
||||
);
|
||||
MARKETING_VERSION = 3.5;
|
||||
PRODUCT_BUNDLE_IDENTIFIER = com.scarriffleservices.calendarr.ios.watchkitapp;
|
||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||
SDKROOT = watchos;
|
||||
SKIP_INSTALL = YES;
|
||||
STRING_CATALOG_GENERATE_SYMBOLS = YES;
|
||||
SWIFT_APPROACHABLE_CONCURRENCY = YES;
|
||||
SWIFT_DEFAULT_ACTOR_ISOLATION = MainActor;
|
||||
SWIFT_EMIT_LOC_STRINGS = YES;
|
||||
SWIFT_UPCOMING_FEATURE_MEMBER_IMPORT_VISIBILITY = YES;
|
||||
SWIFT_VERSION = 5.0;
|
||||
TARGETED_DEVICE_FAMILY = 4;
|
||||
WATCHOS_DEPLOYMENT_TARGET = 26.0;
|
||||
};
|
||||
name = Release;
|
||||
};
|
||||
3934CF3830544C02003958B8 /* Debug */ = {
|
||||
isa = XCBuildConfiguration;
|
||||
buildSettings = {
|
||||
BUNDLE_LOADER = "$(TEST_HOST)";
|
||||
CODE_SIGN_STYLE = Automatic;
|
||||
CURRENT_PROJECT_VERSION = 7;
|
||||
DEVELOPMENT_TEAM = PP34X97WS3;
|
||||
GENERATE_INFOPLIST_FILE = YES;
|
||||
MARKETING_VERSION = 3.5;
|
||||
PRODUCT_BUNDLE_IDENTIFIER = "com.scarriffleservices.calendarr.ios.watchkitapp.CalendarrWatch-Watch-AppTests";
|
||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||
SDKROOT = watchos;
|
||||
STRING_CATALOG_GENERATE_SYMBOLS = NO;
|
||||
SWIFT_APPROACHABLE_CONCURRENCY = YES;
|
||||
SWIFT_EMIT_LOC_STRINGS = NO;
|
||||
SWIFT_UPCOMING_FEATURE_MEMBER_IMPORT_VISIBILITY = YES;
|
||||
SWIFT_VERSION = 5.0;
|
||||
TARGETED_DEVICE_FAMILY = 4;
|
||||
TEST_HOST = "$(BUILT_PRODUCTS_DIR)/CalendarrWatch.app/$(BUNDLE_EXECUTABLE_FOLDER_PATH)/CalendarrWatch";
|
||||
WATCHOS_DEPLOYMENT_TARGET = 26.0;
|
||||
};
|
||||
name = Debug;
|
||||
};
|
||||
3934CF3930544C02003958B8 /* Release */ = {
|
||||
isa = XCBuildConfiguration;
|
||||
buildSettings = {
|
||||
BUNDLE_LOADER = "$(TEST_HOST)";
|
||||
CODE_SIGN_STYLE = Automatic;
|
||||
CURRENT_PROJECT_VERSION = 7;
|
||||
DEVELOPMENT_TEAM = PP34X97WS3;
|
||||
GENERATE_INFOPLIST_FILE = YES;
|
||||
MARKETING_VERSION = 3.5;
|
||||
PRODUCT_BUNDLE_IDENTIFIER = "com.scarriffleservices.calendarr.ios.watchkitapp.CalendarrWatch-Watch-AppTests";
|
||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||
SDKROOT = watchos;
|
||||
STRING_CATALOG_GENERATE_SYMBOLS = NO;
|
||||
SWIFT_APPROACHABLE_CONCURRENCY = YES;
|
||||
SWIFT_EMIT_LOC_STRINGS = NO;
|
||||
SWIFT_UPCOMING_FEATURE_MEMBER_IMPORT_VISIBILITY = YES;
|
||||
SWIFT_VERSION = 5.0;
|
||||
TARGETED_DEVICE_FAMILY = 4;
|
||||
TEST_HOST = "$(BUILT_PRODUCTS_DIR)/CalendarrWatch.app/$(BUNDLE_EXECUTABLE_FOLDER_PATH)/CalendarrWatch";
|
||||
WATCHOS_DEPLOYMENT_TARGET = 26.0;
|
||||
};
|
||||
name = Release;
|
||||
};
|
||||
3934CF3A30544C02003958B8 /* Debug */ = {
|
||||
isa = XCBuildConfiguration;
|
||||
buildSettings = {
|
||||
CODE_SIGN_STYLE = Automatic;
|
||||
CURRENT_PROJECT_VERSION = 7;
|
||||
DEVELOPMENT_TEAM = PP34X97WS3;
|
||||
GENERATE_INFOPLIST_FILE = YES;
|
||||
MARKETING_VERSION = 3.5;
|
||||
PRODUCT_BUNDLE_IDENTIFIER = "com.scarriffleservices.calendarr.ios.watchkitapp.CalendarrWatch-Watch-AppUITests";
|
||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||
SDKROOT = watchos;
|
||||
STRING_CATALOG_GENERATE_SYMBOLS = NO;
|
||||
SWIFT_APPROACHABLE_CONCURRENCY = YES;
|
||||
SWIFT_EMIT_LOC_STRINGS = NO;
|
||||
SWIFT_UPCOMING_FEATURE_MEMBER_IMPORT_VISIBILITY = YES;
|
||||
SWIFT_VERSION = 5.0;
|
||||
TARGETED_DEVICE_FAMILY = 4;
|
||||
TEST_TARGET_NAME = CalendarrWatch;
|
||||
WATCHOS_DEPLOYMENT_TARGET = 26.0;
|
||||
};
|
||||
name = Debug;
|
||||
};
|
||||
3934CF3B30544C02003958B8 /* Release */ = {
|
||||
isa = XCBuildConfiguration;
|
||||
buildSettings = {
|
||||
CODE_SIGN_STYLE = Automatic;
|
||||
CURRENT_PROJECT_VERSION = 7;
|
||||
DEVELOPMENT_TEAM = PP34X97WS3;
|
||||
GENERATE_INFOPLIST_FILE = YES;
|
||||
MARKETING_VERSION = 3.5;
|
||||
PRODUCT_BUNDLE_IDENTIFIER = "com.scarriffleservices.calendarr.ios.watchkitapp.CalendarrWatch-Watch-AppUITests";
|
||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||
SDKROOT = watchos;
|
||||
STRING_CATALOG_GENERATE_SYMBOLS = NO;
|
||||
SWIFT_APPROACHABLE_CONCURRENCY = YES;
|
||||
SWIFT_EMIT_LOC_STRINGS = NO;
|
||||
SWIFT_UPCOMING_FEATURE_MEMBER_IMPORT_VISIBILITY = YES;
|
||||
SWIFT_VERSION = 5.0;
|
||||
TARGETED_DEVICE_FAMILY = 4;
|
||||
TEST_TARGET_NAME = CalendarrWatch;
|
||||
WATCHOS_DEPLOYMENT_TARGET = 26.0;
|
||||
};
|
||||
name = Release;
|
||||
};
|
||||
3934CF5830544C2E003958B8 /* Debug */ = {
|
||||
isa = XCBuildConfiguration;
|
||||
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 = 7;
|
||||
DEVELOPMENT_TEAM = PP34X97WS3;
|
||||
GENERATE_INFOPLIST_FILE = YES;
|
||||
INFOPLIST_FILE = CalendarrWatchComplications/Info.plist;
|
||||
INFOPLIST_KEY_CFBundleDisplayName = Calendarr;
|
||||
INFOPLIST_KEY_NSHumanReadableCopyright = "";
|
||||
LD_RUNPATH_SEARCH_PATHS = (
|
||||
"$(inherited)",
|
||||
"@executable_path/Frameworks",
|
||||
"@executable_path/../../Frameworks",
|
||||
"@executable_path/../../../../Frameworks",
|
||||
);
|
||||
MARKETING_VERSION = 3.5;
|
||||
PRODUCT_BUNDLE_IDENTIFIER = com.scarriffleservices.calendarr.ios.watchkitapp.complications;
|
||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||
SDKROOT = watchos;
|
||||
SKIP_INSTALL = YES;
|
||||
STRING_CATALOG_GENERATE_SYMBOLS = YES;
|
||||
SWIFT_APPROACHABLE_CONCURRENCY = YES;
|
||||
SWIFT_EMIT_LOC_STRINGS = YES;
|
||||
SWIFT_UPCOMING_FEATURE_MEMBER_IMPORT_VISIBILITY = YES;
|
||||
SWIFT_VERSION = 5.0;
|
||||
TARGETED_DEVICE_FAMILY = 4;
|
||||
WATCHOS_DEPLOYMENT_TARGET = 26.0;
|
||||
};
|
||||
name = Debug;
|
||||
};
|
||||
3934CF5930544C2E003958B8 /* Release */ = {
|
||||
isa = XCBuildConfiguration;
|
||||
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 = 7;
|
||||
DEVELOPMENT_TEAM = PP34X97WS3;
|
||||
GENERATE_INFOPLIST_FILE = YES;
|
||||
INFOPLIST_FILE = CalendarrWatchComplications/Info.plist;
|
||||
INFOPLIST_KEY_CFBundleDisplayName = Calendarr;
|
||||
INFOPLIST_KEY_NSHumanReadableCopyright = "";
|
||||
LD_RUNPATH_SEARCH_PATHS = (
|
||||
"$(inherited)",
|
||||
"@executable_path/Frameworks",
|
||||
"@executable_path/../../Frameworks",
|
||||
"@executable_path/../../../../Frameworks",
|
||||
);
|
||||
MARKETING_VERSION = 3.5;
|
||||
PRODUCT_BUNDLE_IDENTIFIER = com.scarriffleservices.calendarr.ios.watchkitapp.complications;
|
||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||
SDKROOT = watchos;
|
||||
SKIP_INSTALL = YES;
|
||||
STRING_CATALOG_GENERATE_SYMBOLS = YES;
|
||||
SWIFT_APPROACHABLE_CONCURRENCY = YES;
|
||||
SWIFT_EMIT_LOC_STRINGS = YES;
|
||||
SWIFT_UPCOMING_FEATURE_MEMBER_IMPORT_VISIBILITY = YES;
|
||||
SWIFT_VERSION = 5.0;
|
||||
TARGETED_DEVICE_FAMILY = 4;
|
||||
WATCHOS_DEPLOYMENT_TARGET = 26.0;
|
||||
};
|
||||
name = Release;
|
||||
};
|
||||
C0001401FB4E10100AB5001 /* Debug */ = {
|
||||
isa = XCBuildConfiguration;
|
||||
buildSettings = {
|
||||
@@ -521,11 +1090,13 @@
|
||||
INFOPLIST_KEY_UILaunchScreen_Generation = YES;
|
||||
INFOPLIST_KEY_UIStatusBarStyle = UIStatusBarStyleDarkContent;
|
||||
INFOPLIST_KEY_UISupportedInterfaceOrientations = UIInterfaceOrientationPortrait;
|
||||
INFOPLIST_KEY_UISupportedInterfaceOrientations_iPad = "UIInterfaceOrientationPortrait UIInterfaceOrientationPortraitUpsideDown UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight";
|
||||
IPHONEOS_DEPLOYMENT_TARGET = 26.0;
|
||||
LD_RUNPATH_SEARCH_PATHS = (
|
||||
"$(inherited)",
|
||||
"@executable_path/Frameworks",
|
||||
);
|
||||
MARKETING_VERSION = 3.6;
|
||||
OTHER_LDFLAGS = (
|
||||
"$(inherited)",
|
||||
"-ObjC",
|
||||
@@ -542,7 +1113,7 @@
|
||||
SWIFT_EMIT_LOC_STRINGS = YES;
|
||||
SWIFT_UPCOMING_FEATURE_MEMBER_IMPORT_VISIBILITY = YES;
|
||||
SWIFT_VERSION = 5.0;
|
||||
TARGETED_DEVICE_FAMILY = 1;
|
||||
TARGETED_DEVICE_FAMILY = "1,2";
|
||||
"TARGETED_DEVICE_FAMILY[sdk=macosx*]" = 2;
|
||||
};
|
||||
name = Debug;
|
||||
@@ -571,11 +1142,13 @@
|
||||
INFOPLIST_KEY_UILaunchScreen_Generation = YES;
|
||||
INFOPLIST_KEY_UIStatusBarStyle = UIStatusBarStyleDarkContent;
|
||||
INFOPLIST_KEY_UISupportedInterfaceOrientations = UIInterfaceOrientationPortrait;
|
||||
INFOPLIST_KEY_UISupportedInterfaceOrientations_iPad = "UIInterfaceOrientationPortrait UIInterfaceOrientationPortraitUpsideDown UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight";
|
||||
IPHONEOS_DEPLOYMENT_TARGET = 26.0;
|
||||
LD_RUNPATH_SEARCH_PATHS = (
|
||||
"$(inherited)",
|
||||
"@executable_path/Frameworks",
|
||||
);
|
||||
MARKETING_VERSION = 3.6;
|
||||
OTHER_LDFLAGS = (
|
||||
"$(inherited)",
|
||||
"-ObjC",
|
||||
@@ -592,7 +1165,7 @@
|
||||
SWIFT_EMIT_LOC_STRINGS = YES;
|
||||
SWIFT_UPCOMING_FEATURE_MEMBER_IMPORT_VISIBILITY = YES;
|
||||
SWIFT_VERSION = 5.0;
|
||||
TARGETED_DEVICE_FAMILY = 1;
|
||||
TARGETED_DEVICE_FAMILY = "1,2";
|
||||
"TARGETED_DEVICE_FAMILY[sdk=macosx*]" = 2;
|
||||
};
|
||||
name = Release;
|
||||
@@ -627,7 +1200,7 @@
|
||||
SWIFT_EMIT_LOC_STRINGS = YES;
|
||||
SWIFT_OPTIMIZATION_LEVEL = "-Onone";
|
||||
SWIFT_VERSION = 5.0;
|
||||
TARGETED_DEVICE_FAMILY = 1;
|
||||
TARGETED_DEVICE_FAMILY = "1,2";
|
||||
"TARGETED_DEVICE_FAMILY[sdk=macosx*]" = 2;
|
||||
};
|
||||
name = Debug;
|
||||
@@ -661,7 +1234,7 @@
|
||||
SWIFT_COMPILATION_MODE = wholemodule;
|
||||
SWIFT_EMIT_LOC_STRINGS = YES;
|
||||
SWIFT_VERSION = 5.0;
|
||||
TARGETED_DEVICE_FAMILY = 1;
|
||||
TARGETED_DEVICE_FAMILY = "1,2";
|
||||
"TARGETED_DEVICE_FAMILY[sdk=macosx*]" = 2;
|
||||
};
|
||||
name = Release;
|
||||
@@ -678,6 +1251,42 @@
|
||||
defaultConfigurationIsVisible = 0;
|
||||
defaultConfigurationName = Release;
|
||||
};
|
||||
3934CF3C30544C02003958B8 /* Build configuration list for PBXNativeTarget "CalendarrWatch" */ = {
|
||||
isa = XCConfigurationList;
|
||||
buildConfigurations = (
|
||||
3934CF3630544C02003958B8 /* Debug */,
|
||||
3934CF3730544C02003958B8 /* Release */,
|
||||
);
|
||||
defaultConfigurationIsVisible = 0;
|
||||
defaultConfigurationName = Release;
|
||||
};
|
||||
3934CF3D30544C02003958B8 /* Build configuration list for PBXNativeTarget "CalendarrWatchTests" */ = {
|
||||
isa = XCConfigurationList;
|
||||
buildConfigurations = (
|
||||
3934CF3830544C02003958B8 /* Debug */,
|
||||
3934CF3930544C02003958B8 /* Release */,
|
||||
);
|
||||
defaultConfigurationIsVisible = 0;
|
||||
defaultConfigurationName = Release;
|
||||
};
|
||||
3934CF3E30544C02003958B8 /* Build configuration list for PBXNativeTarget "CalendarrWatchUITests" */ = {
|
||||
isa = XCConfigurationList;
|
||||
buildConfigurations = (
|
||||
3934CF3A30544C02003958B8 /* Debug */,
|
||||
3934CF3B30544C02003958B8 /* Release */,
|
||||
);
|
||||
defaultConfigurationIsVisible = 0;
|
||||
defaultConfigurationName = Release;
|
||||
};
|
||||
3934CF5730544C2E003958B8 /* Build configuration list for PBXNativeTarget "CalendarrWatchComplications" */ = {
|
||||
isa = XCConfigurationList;
|
||||
buildConfigurations = (
|
||||
3934CF5830544C2E003958B8 /* Debug */,
|
||||
3934CF5930544C2E003958B8 /* Release */,
|
||||
);
|
||||
defaultConfigurationIsVisible = 0;
|
||||
defaultConfigurationName = Release;
|
||||
};
|
||||
C0000601FB4E10100AB5001 /* Build configuration list for PBXProject "Calendarr iOS" */ = {
|
||||
isa = XCConfigurationList;
|
||||
buildConfigurations = (
|
||||
@@ -707,22 +1316,6 @@
|
||||
};
|
||||
/* End XCConfigurationList section */
|
||||
|
||||
/* Begin XCSwiftPackageProductDependency section */
|
||||
CA1E4D0A0001000000000001 /* CalendarrCore */ = {
|
||||
isa = XCSwiftPackageProductDependency;
|
||||
productName = CalendarrCore;
|
||||
};
|
||||
CA1E4D0A0001000000000002 /* CalendarrCore */ = {
|
||||
isa = XCSwiftPackageProductDependency;
|
||||
productName = CalendarrCore;
|
||||
};
|
||||
AA9701000002000000000001 /* AppAuth */ = {
|
||||
isa = XCSwiftPackageProductDependency;
|
||||
package = AA9701000003000000000001 /* XCRemoteSwiftPackageReference "AppAuth-iOS" */;
|
||||
productName = AppAuth;
|
||||
};
|
||||
/* End XCSwiftPackageProductDependency section */
|
||||
|
||||
/* Begin XCLocalSwiftPackageReference section */
|
||||
39450EFC302A266A0090DDD7 /* XCLocalSwiftPackageReference "../CalendarrKit" */ = {
|
||||
isa = XCLocalSwiftPackageReference;
|
||||
@@ -740,6 +1333,30 @@
|
||||
};
|
||||
};
|
||||
/* End XCRemoteSwiftPackageReference section */
|
||||
|
||||
/* Begin XCSwiftPackageProductDependency section */
|
||||
AA9701000002000000000001 /* AppAuth */ = {
|
||||
isa = XCSwiftPackageProductDependency;
|
||||
package = AA9701000003000000000001 /* XCRemoteSwiftPackageReference "AppAuth-iOS" */;
|
||||
productName = AppAuth;
|
||||
};
|
||||
CA1E4D0A0001000000000001 /* CalendarrCore */ = {
|
||||
isa = XCSwiftPackageProductDependency;
|
||||
productName = CalendarrCore;
|
||||
};
|
||||
CA1E4D0A0001000000000002 /* CalendarrCore */ = {
|
||||
isa = XCSwiftPackageProductDependency;
|
||||
productName = CalendarrCore;
|
||||
};
|
||||
CA1E4D0A0001000000000003 /* CalendarrCore */ = {
|
||||
isa = XCSwiftPackageProductDependency;
|
||||
productName = CalendarrCore;
|
||||
};
|
||||
CA1E4D0A0001000000000004 /* CalendarrCore */ = {
|
||||
isa = XCSwiftPackageProductDependency;
|
||||
productName = CalendarrCore;
|
||||
};
|
||||
/* End XCSwiftPackageProductDependency section */
|
||||
};
|
||||
rootObject = C0000301FB4E10100AB5001 /* Project object */;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,15 @@
|
||||
{
|
||||
"originHash" : "478d8419d86ca78c1539d3775396b811ce835c77379f2af98e26eefd35ee2311",
|
||||
"pins" : [
|
||||
{
|
||||
"identity" : "appauth-ios",
|
||||
"kind" : "remoteSourceControl",
|
||||
"location" : "https://github.com/openid/AppAuth-iOS.git",
|
||||
"state" : {
|
||||
"revision" : "2781038865a80e2c425a1da12cc1327bcd56501f",
|
||||
"version" : "1.7.6"
|
||||
}
|
||||
}
|
||||
],
|
||||
"version" : 3
|
||||
}
|
||||
@@ -0,0 +1,111 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<Scheme
|
||||
LastUpgradeVersion = "2700"
|
||||
version = "1.7">
|
||||
<BuildAction
|
||||
parallelizeBuildables = "YES"
|
||||
buildImplicitDependencies = "YES"
|
||||
buildArchitectures = "Automatic">
|
||||
<BuildActionEntries>
|
||||
<BuildActionEntry
|
||||
buildForTesting = "YES"
|
||||
buildForRunning = "YES"
|
||||
buildForProfiling = "YES"
|
||||
buildForArchiving = "YES"
|
||||
buildForAnalyzing = "YES">
|
||||
<BuildableReference
|
||||
BuildableIdentifier = "primary"
|
||||
BlueprintIdentifier = "3934CF1930544C01003958B8"
|
||||
BuildableName = "CalendarrWatch.app"
|
||||
ReferencedContainer = "container:Calendarr iOS.xcodeproj">
|
||||
</BuildableReference>
|
||||
</BuildActionEntry>
|
||||
<BuildActionEntry
|
||||
buildForTesting = "YES"
|
||||
buildForRunning = "YES"
|
||||
buildForProfiling = "YES"
|
||||
buildForArchiving = "YES"
|
||||
buildForAnalyzing = "YES">
|
||||
<BuildableReference
|
||||
BuildableIdentifier = "primary"
|
||||
BlueprintIdentifier = "C0000A01FB4E10100AB5001"
|
||||
BuildableName = "Calendarr iOS.app"
|
||||
ReferencedContainer = "container:Calendarr iOS.xcodeproj">
|
||||
</BuildableReference>
|
||||
</BuildActionEntry>
|
||||
</BuildActionEntries>
|
||||
</BuildAction>
|
||||
<TestAction
|
||||
buildConfiguration = "Debug"
|
||||
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
|
||||
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
|
||||
shouldUseLaunchSchemeArgsEnv = "YES"
|
||||
shouldAutocreateTestPlan = "YES">
|
||||
<Testables>
|
||||
<TestableReference
|
||||
skipped = "NO"
|
||||
parallelizable = "YES">
|
||||
<BuildableReference
|
||||
BuildableIdentifier = "primary"
|
||||
BlueprintIdentifier = "3934CF2530544C02003958B8"
|
||||
BuildableName = "CalendarrWatchTests.xctest"
|
||||
ReferencedContainer = "container:Calendarr iOS.xcodeproj">
|
||||
</BuildableReference>
|
||||
</TestableReference>
|
||||
<TestableReference
|
||||
skipped = "NO"
|
||||
parallelizable = "YES">
|
||||
<BuildableReference
|
||||
BuildableIdentifier = "primary"
|
||||
BlueprintIdentifier = "3934CF2F30544C02003958B8"
|
||||
BuildableName = "CalendarrWatchUITests.xctest"
|
||||
ReferencedContainer = "container:Calendarr iOS.xcodeproj">
|
||||
</BuildableReference>
|
||||
</TestableReference>
|
||||
</Testables>
|
||||
</TestAction>
|
||||
<LaunchAction
|
||||
buildConfiguration = "Debug"
|
||||
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
|
||||
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
|
||||
launchStyle = "0"
|
||||
useCustomWorkingDirectory = "NO"
|
||||
ignoresPersistentStateOnLaunch = "NO"
|
||||
debugDocumentVersioning = "YES"
|
||||
debugServiceExtension = "internal"
|
||||
allowLocationSimulation = "YES"
|
||||
queueDebuggingEnabled = "No">
|
||||
<BuildableProductRunnable
|
||||
runnableDebuggingMode = "0">
|
||||
<BuildableReference
|
||||
BuildableIdentifier = "primary"
|
||||
BlueprintIdentifier = "3934CF1930544C01003958B8"
|
||||
BuildableName = "CalendarrWatch.app"
|
||||
ReferencedContainer = "container:Calendarr iOS.xcodeproj">
|
||||
</BuildableReference>
|
||||
</BuildableProductRunnable>
|
||||
</LaunchAction>
|
||||
<ProfileAction
|
||||
buildConfiguration = "Release"
|
||||
shouldUseLaunchSchemeArgsEnv = "YES"
|
||||
savedToolIdentifier = ""
|
||||
useCustomWorkingDirectory = "NO"
|
||||
debugDocumentVersioning = "YES">
|
||||
<BuildableProductRunnable
|
||||
runnableDebuggingMode = "0">
|
||||
<BuildableReference
|
||||
BuildableIdentifier = "primary"
|
||||
BlueprintIdentifier = "3934CF1930544C01003958B8"
|
||||
BuildableName = "CalendarrWatch.app"
|
||||
ReferencedContainer = "container:Calendarr iOS.xcodeproj">
|
||||
</BuildableReference>
|
||||
</BuildableProductRunnable>
|
||||
</ProfileAction>
|
||||
<AnalyzeAction
|
||||
buildConfiguration = "Debug">
|
||||
</AnalyzeAction>
|
||||
<ArchiveAction
|
||||
buildConfiguration = "Release"
|
||||
revealArchiveInOrganizer = "YES">
|
||||
</ArchiveAction>
|
||||
</Scheme>
|
||||
@@ -0,0 +1,122 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<Scheme
|
||||
LastUpgradeVersion = "2700"
|
||||
wasCreatedForAppExtension = "YES"
|
||||
version = "2.0">
|
||||
<BuildAction
|
||||
parallelizeBuildables = "YES"
|
||||
buildImplicitDependencies = "YES"
|
||||
buildArchitectures = "Automatic">
|
||||
<BuildActionEntries>
|
||||
<BuildActionEntry
|
||||
buildForTesting = "YES"
|
||||
buildForRunning = "YES"
|
||||
buildForProfiling = "YES"
|
||||
buildForArchiving = "YES"
|
||||
buildForAnalyzing = "YES">
|
||||
<BuildableReference
|
||||
BuildableIdentifier = "primary"
|
||||
BlueprintIdentifier = "3934CF4230544C2E003958B8"
|
||||
BuildableName = "CalendarrWatchComplications.appex"
|
||||
ReferencedContainer = "container:Calendarr iOS.xcodeproj">
|
||||
</BuildableReference>
|
||||
</BuildActionEntry>
|
||||
<BuildActionEntry
|
||||
buildForTesting = "YES"
|
||||
buildForRunning = "YES"
|
||||
buildForProfiling = "YES"
|
||||
buildForArchiving = "YES"
|
||||
buildForAnalyzing = "YES">
|
||||
<BuildableReference
|
||||
BuildableIdentifier = "primary"
|
||||
BlueprintIdentifier = "C0000A01FB4E10100AB5001"
|
||||
BuildableName = "Calendarr iOS.app"
|
||||
ReferencedContainer = "container:Calendarr iOS.xcodeproj">
|
||||
</BuildableReference>
|
||||
</BuildActionEntry>
|
||||
<BuildActionEntry
|
||||
buildForTesting = "YES"
|
||||
buildForRunning = "YES"
|
||||
buildForProfiling = "YES"
|
||||
buildForArchiving = "YES"
|
||||
buildForAnalyzing = "YES">
|
||||
<BuildableReference
|
||||
BuildableIdentifier = "primary"
|
||||
BlueprintIdentifier = "3934CF1930544C01003958B8"
|
||||
BuildableName = "CalendarrWatch.app"
|
||||
ReferencedContainer = "container:Calendarr iOS.xcodeproj">
|
||||
</BuildableReference>
|
||||
</BuildActionEntry>
|
||||
</BuildActionEntries>
|
||||
</BuildAction>
|
||||
<TestAction
|
||||
buildConfiguration = "Debug"
|
||||
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
|
||||
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
|
||||
shouldUseLaunchSchemeArgsEnv = "YES"
|
||||
shouldAutocreateTestPlan = "YES">
|
||||
</TestAction>
|
||||
<LaunchAction
|
||||
buildConfiguration = "Debug"
|
||||
selectedDebuggerIdentifier = ""
|
||||
selectedLauncherIdentifier = "Xcode.IDEFoundation.Launcher.PosixSpawn"
|
||||
launchStyle = "0"
|
||||
askForAppToLaunch = "Yes"
|
||||
useCustomWorkingDirectory = "NO"
|
||||
ignoresPersistentStateOnLaunch = "NO"
|
||||
debugDocumentVersioning = "YES"
|
||||
debugServiceExtension = "internal"
|
||||
allowLocationSimulation = "YES"
|
||||
launchAutomaticallySubstyle = "2"
|
||||
queueDebuggingEnabled = "No">
|
||||
<BuildableProductRunnable
|
||||
runnableDebuggingMode = "0">
|
||||
<BuildableReference
|
||||
BuildableIdentifier = "primary"
|
||||
BlueprintIdentifier = "C0000A01FB4E10100AB5001"
|
||||
BuildableName = "Calendarr iOS.app"
|
||||
ReferencedContainer = "container:Calendarr iOS.xcodeproj">
|
||||
</BuildableReference>
|
||||
</BuildableProductRunnable>
|
||||
<EnvironmentVariables>
|
||||
<EnvironmentVariable
|
||||
key = "_XCWidgetKind"
|
||||
value = ""
|
||||
isEnabled = "YES">
|
||||
</EnvironmentVariable>
|
||||
<EnvironmentVariable
|
||||
key = "_XCWidgetDefaultView"
|
||||
value = "timeline"
|
||||
isEnabled = "YES">
|
||||
</EnvironmentVariable>
|
||||
<EnvironmentVariable
|
||||
key = "_XCWidgetFamily"
|
||||
value = "systemMedium"
|
||||
isEnabled = "YES">
|
||||
</EnvironmentVariable>
|
||||
</EnvironmentVariables>
|
||||
</LaunchAction>
|
||||
<ProfileAction
|
||||
buildConfiguration = "Release"
|
||||
shouldUseLaunchSchemeArgsEnv = "YES"
|
||||
savedToolIdentifier = ""
|
||||
useCustomWorkingDirectory = "NO"
|
||||
debugDocumentVersioning = "YES">
|
||||
<BuildableProductRunnable
|
||||
runnableDebuggingMode = "0">
|
||||
<BuildableReference
|
||||
BuildableIdentifier = "primary"
|
||||
BlueprintIdentifier = "3934CF4230544C2E003958B8"
|
||||
BuildableName = "CalendarrWatchComplications.appex"
|
||||
ReferencedContainer = "container:Calendarr iOS.xcodeproj">
|
||||
</BuildableReference>
|
||||
</BuildableProductRunnable>
|
||||
</ProfileAction>
|
||||
<AnalyzeAction
|
||||
buildConfiguration = "Debug">
|
||||
</AnalyzeAction>
|
||||
<ArchiveAction
|
||||
buildConfiguration = "Release"
|
||||
revealArchiveInOrganizer = "YES">
|
||||
</ArchiveAction>
|
||||
</Scheme>
|
||||
@@ -0,0 +1,111 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<Scheme
|
||||
LastUpgradeVersion = "2700"
|
||||
wasCreatedForAppExtension = "YES"
|
||||
version = "2.0">
|
||||
<BuildAction
|
||||
parallelizeBuildables = "YES"
|
||||
buildImplicitDependencies = "YES"
|
||||
buildArchitectures = "Automatic">
|
||||
<BuildActionEntries>
|
||||
<BuildActionEntry
|
||||
buildForTesting = "YES"
|
||||
buildForRunning = "YES"
|
||||
buildForProfiling = "YES"
|
||||
buildForArchiving = "YES"
|
||||
buildForAnalyzing = "YES">
|
||||
<BuildableReference
|
||||
BuildableIdentifier = "primary"
|
||||
BlueprintIdentifier = "D0001A01ABCDEF0100AB5001"
|
||||
BuildableName = "CalendarrWidgets.appex"
|
||||
ReferencedContainer = "container:Calendarr iOS.xcodeproj">
|
||||
</BuildableReference>
|
||||
</BuildActionEntry>
|
||||
<BuildActionEntry
|
||||
buildForTesting = "YES"
|
||||
buildForRunning = "YES"
|
||||
buildForProfiling = "YES"
|
||||
buildForArchiving = "YES"
|
||||
buildForAnalyzing = "YES">
|
||||
<BuildableReference
|
||||
BuildableIdentifier = "primary"
|
||||
BlueprintIdentifier = "C0000A01FB4E10100AB5001"
|
||||
BuildableName = "Calendarr iOS.app"
|
||||
ReferencedContainer = "container:Calendarr iOS.xcodeproj">
|
||||
</BuildableReference>
|
||||
</BuildActionEntry>
|
||||
</BuildActionEntries>
|
||||
</BuildAction>
|
||||
<TestAction
|
||||
buildConfiguration = "Debug"
|
||||
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
|
||||
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
|
||||
shouldUseLaunchSchemeArgsEnv = "YES"
|
||||
shouldAutocreateTestPlan = "YES">
|
||||
</TestAction>
|
||||
<LaunchAction
|
||||
buildConfiguration = "Debug"
|
||||
selectedDebuggerIdentifier = ""
|
||||
selectedLauncherIdentifier = "Xcode.IDEFoundation.Launcher.PosixSpawn"
|
||||
launchStyle = "0"
|
||||
askForAppToLaunch = "Yes"
|
||||
useCustomWorkingDirectory = "NO"
|
||||
ignoresPersistentStateOnLaunch = "NO"
|
||||
debugDocumentVersioning = "YES"
|
||||
debugServiceExtension = "internal"
|
||||
allowLocationSimulation = "YES"
|
||||
launchAutomaticallySubstyle = "2"
|
||||
queueDebuggingEnabled = "No">
|
||||
<BuildableProductRunnable
|
||||
runnableDebuggingMode = "0">
|
||||
<BuildableReference
|
||||
BuildableIdentifier = "primary"
|
||||
BlueprintIdentifier = "C0000A01FB4E10100AB5001"
|
||||
BuildableName = "Calendarr iOS.app"
|
||||
ReferencedContainer = "container:Calendarr iOS.xcodeproj">
|
||||
</BuildableReference>
|
||||
</BuildableProductRunnable>
|
||||
<EnvironmentVariables>
|
||||
<EnvironmentVariable
|
||||
key = "_XCWidgetKind"
|
||||
value = ""
|
||||
isEnabled = "YES">
|
||||
</EnvironmentVariable>
|
||||
<EnvironmentVariable
|
||||
key = "_XCWidgetDefaultView"
|
||||
value = "timeline"
|
||||
isEnabled = "YES">
|
||||
</EnvironmentVariable>
|
||||
<EnvironmentVariable
|
||||
key = "_XCWidgetFamily"
|
||||
value = "systemMedium"
|
||||
isEnabled = "YES">
|
||||
</EnvironmentVariable>
|
||||
</EnvironmentVariables>
|
||||
</LaunchAction>
|
||||
<ProfileAction
|
||||
buildConfiguration = "Release"
|
||||
shouldUseLaunchSchemeArgsEnv = "YES"
|
||||
savedToolIdentifier = ""
|
||||
useCustomWorkingDirectory = "NO"
|
||||
debugDocumentVersioning = "YES"
|
||||
askForAppToLaunch = "Yes"
|
||||
launchAutomaticallySubstyle = "2">
|
||||
<BuildableProductRunnable
|
||||
runnableDebuggingMode = "0">
|
||||
<BuildableReference
|
||||
BuildableIdentifier = "primary"
|
||||
BlueprintIdentifier = "C0000A01FB4E10100AB5001"
|
||||
BuildableName = "Calendarr iOS.app"
|
||||
ReferencedContainer = "container:Calendarr iOS.xcodeproj">
|
||||
</BuildableReference>
|
||||
</BuildableProductRunnable>
|
||||
</ProfileAction>
|
||||
<AnalyzeAction
|
||||
buildConfiguration = "Debug">
|
||||
</AnalyzeAction>
|
||||
<ArchiveAction
|
||||
buildConfiguration = "Release"
|
||||
revealArchiveInOrganizer = "YES">
|
||||
</ArchiveAction>
|
||||
</Scheme>
|
||||
+21
-1
@@ -9,11 +9,31 @@
|
||||
<key>orderHint</key>
|
||||
<integer>0</integer>
|
||||
</dict>
|
||||
<key>CalendarrWidgets.xcscheme_^#shared#^_</key>
|
||||
<key>CalendarrWatch Watch App.xcscheme_^#shared#^_</key>
|
||||
<dict>
|
||||
<key>orderHint</key>
|
||||
<integer>3</integer>
|
||||
</dict>
|
||||
<key>CalendarrWatch.xcscheme_^#shared#^_</key>
|
||||
<dict>
|
||||
<key>orderHint</key>
|
||||
<integer>2</integer>
|
||||
</dict>
|
||||
<key>CalendarrWatchComplications.xcscheme_^#shared#^_</key>
|
||||
<dict>
|
||||
<key>orderHint</key>
|
||||
<integer>3</integer>
|
||||
</dict>
|
||||
<key>CalendarrWatchExtension.xcscheme_^#shared#^_</key>
|
||||
<dict>
|
||||
<key>orderHint</key>
|
||||
<integer>4</integer>
|
||||
</dict>
|
||||
<key>CalendarrWidgets.xcscheme_^#shared#^_</key>
|
||||
<dict>
|
||||
<key>orderHint</key>
|
||||
<integer>4</integer>
|
||||
</dict>
|
||||
</dict>
|
||||
<key>SuppressBuildableAutocreation</key>
|
||||
<dict>
|
||||
|
||||
@@ -91,11 +91,32 @@ struct CalEvent: Identifiable, Hashable {
|
||||
else if let n = json["id"] as? Int { id = String(n) }
|
||||
else { return nil }
|
||||
|
||||
let isAllDay = json["allDay"] as? Bool ?? false
|
||||
let startDate = parseDate(startStr, allDay: isAllDay)
|
||||
let endDate = parseDate(endStr, allDay: isAllDay)
|
||||
// A date-only string is an all-day event whether or not the flag says
|
||||
// so — `parseDate` already treats it that way. Trusting the flag alone
|
||||
// is what put "00:00" in front of birthdays everywhere they are shown.
|
||||
let startIsDateOnly: Bool = {
|
||||
let clean = startStr.trimmingCharacters(in: .whitespaces)
|
||||
return clean.count == 10 && !clean.contains("T")
|
||||
}()
|
||||
let declaredAllDay = (json["allDay"] as? Bool ?? false) || startIsDateOnly
|
||||
let startDate = parseDate(startStr, allDay: declaredAllDay)
|
||||
let endDate = parseDate(endStr, allDay: declaredAllDay)
|
||||
guard let s = startDate, let e = endDate else { return nil }
|
||||
|
||||
// An event running midnight to midnight across one or more whole days
|
||||
// is all-day whatever the payload claims. Without this, a six-day
|
||||
// holiday sent without the flag renders as "00:00 – 00:00" — a time
|
||||
// range that is technically true and tells the reader nothing.
|
||||
let cal = Calendar.current
|
||||
// Requiring the end to land exactly on midnight too was too strict: a
|
||||
// holiday stored as 16th 00:00 to 21st 01:00 slipped through and showed
|
||||
// up as a one-hour slot. Starting on a day boundary and running into a
|
||||
// later day is enough.
|
||||
let spansWholeDays = e > s
|
||||
&& cal.startOfDay(for: s) == s
|
||||
&& cal.startOfDay(for: e) > cal.startOfDay(for: s)
|
||||
let isAllDay = declaredAllDay || spansWholeDays
|
||||
|
||||
return CalEvent(
|
||||
id: id,
|
||||
url: json["url"] as? String ?? "",
|
||||
|
||||
@@ -0,0 +1,11 @@
|
||||
{
|
||||
"colors" : [
|
||||
{
|
||||
"idiom" : "universal"
|
||||
}
|
||||
],
|
||||
"info" : {
|
||||
"author" : "xcode",
|
||||
"version" : 1
|
||||
}
|
||||
}
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 22 KiB |
@@ -0,0 +1,14 @@
|
||||
{
|
||||
"images" : [
|
||||
{
|
||||
"filename" : "AppIcon-1024.png",
|
||||
"idiom" : "universal",
|
||||
"platform" : "watchos",
|
||||
"size" : "1024x1024"
|
||||
}
|
||||
],
|
||||
"info" : {
|
||||
"author" : "xcode",
|
||||
"version" : 1
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
{
|
||||
"info" : {
|
||||
"author" : "xcode",
|
||||
"version" : 1
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||
<plist version="1.0">
|
||||
<dict>
|
||||
<key>com.apple.security.application-groups</key>
|
||||
<array>
|
||||
<string>group.com.scarriffleservices.calendarr</string>
|
||||
</array>
|
||||
</dict>
|
||||
</plist>
|
||||
@@ -0,0 +1,19 @@
|
||||
import SwiftUI
|
||||
|
||||
@main
|
||||
struct CalendarrWatchApp: App {
|
||||
@State private var model = WatchSnapshotModel()
|
||||
|
||||
init() {
|
||||
// The session has to be live before the first payload arrives, and a
|
||||
// complication push can wake this app with no UI on screen at all.
|
||||
WatchSnapshotReceiver.shared.activate()
|
||||
}
|
||||
|
||||
var body: some Scene {
|
||||
WindowGroup {
|
||||
WatchAgendaView()
|
||||
.environment(model)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,114 @@
|
||||
import SwiftUI
|
||||
import CalendarrCore
|
||||
|
||||
/// The whole app: a scrolling agenda grouped by day, plus a way to reach the
|
||||
/// calendar filter. Read-only by design — the snapshot carries neither
|
||||
/// recurrence rules nor permissions, so it is deliberately not enough to edit.
|
||||
///
|
||||
/// Deliberately does **not** apply `snapshot.theme` to the background. The
|
||||
/// always-on display and OLED make black the right choice here; the theme only
|
||||
/// contributes the per-event accent colours.
|
||||
struct WatchAgendaView: View {
|
||||
@Environment(WatchSnapshotModel.self) private var model
|
||||
@State private var showingFilter = false
|
||||
|
||||
private var calendar: Calendar {
|
||||
var calendar = Calendar(identifier: .gregorian)
|
||||
calendar.locale = WatchL10n.locale(model.language)
|
||||
return calendar
|
||||
}
|
||||
|
||||
var body: some View {
|
||||
NavigationStack {
|
||||
content
|
||||
.navigationTitle(WatchL10n.t("watch.title", model.language))
|
||||
.toolbar {
|
||||
ToolbarItem(placement: .topBarTrailing) {
|
||||
Button { showingFilter = true } label: {
|
||||
Image(systemName: "line.3.horizontal.decrease.circle")
|
||||
}
|
||||
.accessibilityLabel(WatchL10n.t("watch.calendars", model.language))
|
||||
}
|
||||
}
|
||||
.sheet(isPresented: $showingFilter) { WatchCalendarFilterView() }
|
||||
}
|
||||
.onAppear { WatchSnapshotReceiver.shared.requestRefresh() }
|
||||
}
|
||||
|
||||
@ViewBuilder
|
||||
private var content: some View {
|
||||
if let snapshot = model.visible, !groups(in: snapshot).isEmpty {
|
||||
List {
|
||||
ForEach(groups(in: snapshot), id: \.day) { group in
|
||||
Section(WatchEventFormat.dayHeader(group.day, language: model.language,
|
||||
calendar: calendar)) {
|
||||
ForEach(group.events) { event in
|
||||
NavigationLink {
|
||||
WatchEventDetailView(event: event,
|
||||
calendarName: model.calendarName(for: event.calendarKey),
|
||||
language: model.language)
|
||||
} label: {
|
||||
WatchEventRow(event: event, language: model.language)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Section {
|
||||
VStack(alignment: .leading, spacing: 2) {
|
||||
Text("\(WatchL10n.t("watch.as_of", model.language)) \(WatchEventFormat.time(snapshot.writtenAt, language: model.language))")
|
||||
// Beyond the coverage window the snapshot carries no
|
||||
// information, which is not the same as "nothing
|
||||
// scheduled". Saying so is what keeps the empty tail of
|
||||
// the list from reading as a free afternoon.
|
||||
Text("\(WatchL10n.t("watch.no_information", model.language)) \(coverageEndLine(snapshot))")
|
||||
if let error = WatchSnapshotReceiver.shared.lastError {
|
||||
Text(error).foregroundStyle(.orange)
|
||||
}
|
||||
}
|
||||
.font(.system(size: 10))
|
||||
.foregroundStyle(.tertiary)
|
||||
}
|
||||
}
|
||||
.listStyle(.carousel)
|
||||
} else {
|
||||
WatchStatusView(result: model.result, language: model.language)
|
||||
}
|
||||
}
|
||||
|
||||
private struct DayGroup {
|
||||
let day: Date
|
||||
let events: [SnapshotEvent]
|
||||
}
|
||||
|
||||
/// The last day the snapshot can speak for, formatted for the footer.
|
||||
private func coverageEndLine(_ snapshot: CalendarrSnapshot) -> String {
|
||||
let formatter = DateFormatter()
|
||||
formatter.locale = WatchL10n.locale(model.language)
|
||||
formatter.setLocalizedDateFormatFromTemplate("d MMM")
|
||||
let lastCovered = calendar.date(byAdding: .day, value: -1, to: snapshot.coverageEnd)
|
||||
return formatter.string(from: lastCovered ?? snapshot.coverageEnd)
|
||||
}
|
||||
|
||||
/// Group the covered window by day. Days the snapshot cannot speak for are
|
||||
/// simply absent — never rendered as "nothing scheduled", which would look
|
||||
/// correct and be false.
|
||||
private func groups(in snapshot: CalendarrSnapshot) -> [DayGroup] {
|
||||
let calendar = self.calendar
|
||||
let from = max(snapshot.coverageStart, calendar.startOfDay(for: Date()))
|
||||
var buckets: [Date: [SnapshotEvent]] = [:]
|
||||
|
||||
for event in snapshot.events where event.end > from {
|
||||
var day = calendar.startOfDay(for: max(event.start, from))
|
||||
// A multi-day event appears on each day it covers.
|
||||
while day < event.end && day < snapshot.coverageEnd {
|
||||
buckets[day, default: []].append(event)
|
||||
guard let next = calendar.date(byAdding: .day, value: 1, to: day) else { break }
|
||||
day = next
|
||||
}
|
||||
}
|
||||
|
||||
return buckets
|
||||
.map { DayGroup(day: $0.key, events: $0.value.sorted { $0.start < $1.start }) }
|
||||
.sorted { $0.day < $1.day }
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,49 @@
|
||||
import SwiftUI
|
||||
import CalendarrCore
|
||||
|
||||
/// Narrows what this watch shows, on top of what the phone already filtered.
|
||||
/// One toggle here affects the agenda and every complication at once.
|
||||
struct WatchCalendarFilterView: View {
|
||||
@Environment(WatchSnapshotModel.self) private var model
|
||||
|
||||
var body: some View {
|
||||
NavigationStack {
|
||||
List {
|
||||
if model.calendars.isEmpty {
|
||||
Text(WatchL10n.t("watch.waiting", model.language))
|
||||
.font(.footnote)
|
||||
.foregroundStyle(.secondary)
|
||||
} else {
|
||||
ForEach(model.calendars) { calendar in
|
||||
row(calendar)
|
||||
}
|
||||
}
|
||||
}
|
||||
.navigationTitle(WatchL10n.t("watch.calendars", model.language))
|
||||
}
|
||||
}
|
||||
|
||||
private func row(_ calendar: SnapshotCalendar) -> some View {
|
||||
let shown = !model.isHidden(calendar.id)
|
||||
return Button {
|
||||
model.setHidden(calendar.id, shown)
|
||||
} label: {
|
||||
HStack(spacing: 8) {
|
||||
// A checkbox, not a Toggle: a switch reads as a state indicator
|
||||
// you wait on rather than a control you tap.
|
||||
Image(systemName: shown ? "checkmark.square.fill" : "square")
|
||||
.font(.system(size: 16))
|
||||
.foregroundStyle(shown ? Color.accentColor : Color.secondary)
|
||||
Circle()
|
||||
.fill(Color(watchHex: calendar.colorHex))
|
||||
.frame(width: 7, height: 7)
|
||||
Text(calendar.name)
|
||||
.font(.system(size: 14))
|
||||
.lineLimit(1)
|
||||
Spacer(minLength: 0)
|
||||
}
|
||||
}
|
||||
.buttonStyle(.plain)
|
||||
.accessibilityAddTraits(shown ? [.isSelected] : [])
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,60 @@
|
||||
import SwiftUI
|
||||
import CalendarrCore
|
||||
|
||||
/// Everything the snapshot carries about one event, which is deliberately not
|
||||
/// everything the server knows: notes, recurrence and permissions are dropped
|
||||
/// on the way out, so there is nothing here to edit.
|
||||
struct WatchEventDetailView: View {
|
||||
let event: SnapshotEvent
|
||||
/// Nil when the calendar list has not arrived yet, which is possible right
|
||||
/// after a first install.
|
||||
let calendarName: String?
|
||||
let language: String
|
||||
|
||||
var body: some View {
|
||||
ScrollView {
|
||||
VStack(alignment: .leading, spacing: 10) {
|
||||
HStack(alignment: .top, spacing: 6) {
|
||||
Circle()
|
||||
.fill(Color(watchHex: event.colorHex))
|
||||
.frame(width: 8, height: 8)
|
||||
.padding(.top, 5)
|
||||
Text(event.title)
|
||||
.font(.headline)
|
||||
.fixedSize(horizontal: false, vertical: true)
|
||||
}
|
||||
|
||||
Label(WatchEventFormat.timeRange(event, language: language),
|
||||
systemImage: "clock")
|
||||
.font(.footnote)
|
||||
|
||||
Label(dateLine, systemImage: "calendar")
|
||||
.font(.footnote)
|
||||
.foregroundStyle(.secondary)
|
||||
|
||||
if !event.location.isEmpty {
|
||||
Label(event.location, systemImage: "mappin.and.ellipse")
|
||||
.font(.footnote)
|
||||
.foregroundStyle(.secondary)
|
||||
.fixedSize(horizontal: false, vertical: true)
|
||||
}
|
||||
|
||||
if let calendarName {
|
||||
Label(calendarName, systemImage: "list.bullet.rectangle")
|
||||
.font(.footnote)
|
||||
.foregroundStyle(.secondary)
|
||||
.lineLimit(1)
|
||||
}
|
||||
}
|
||||
.frame(maxWidth: .infinity, alignment: .leading)
|
||||
.padding(.horizontal, 4)
|
||||
}
|
||||
}
|
||||
|
||||
private var dateLine: String {
|
||||
let formatter = DateFormatter()
|
||||
formatter.locale = WatchL10n.locale(language)
|
||||
formatter.setLocalizedDateFormatFromTemplate("EEEE d MMMM")
|
||||
return formatter.string(from: event.start)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,89 @@
|
||||
import SwiftUI
|
||||
import CalendarrCore
|
||||
|
||||
/// One event: a colour bar for the calendar, the time range, the title.
|
||||
struct WatchEventRow: View {
|
||||
let event: SnapshotEvent
|
||||
let language: String
|
||||
|
||||
var body: some View {
|
||||
HStack(alignment: .top, spacing: 7) {
|
||||
RoundedRectangle(cornerRadius: 2)
|
||||
.fill(Color(watchHex: event.colorHex))
|
||||
.frame(width: 3)
|
||||
VStack(alignment: .leading, spacing: 1) {
|
||||
Text(WatchEventFormat.timeRange(event, language: language))
|
||||
.font(.system(size: 11))
|
||||
.foregroundStyle(.secondary)
|
||||
Text(event.title)
|
||||
.font(.system(size: 14))
|
||||
.lineLimit(2)
|
||||
}
|
||||
Spacer(minLength: 0)
|
||||
}
|
||||
.padding(.vertical, 2)
|
||||
.fixedSize(horizontal: false, vertical: true)
|
||||
}
|
||||
}
|
||||
|
||||
extension Color {
|
||||
/// Local copy of the hex initialiser: `CalendarrCore` is Foundation-only by
|
||||
/// design, so colour parsing cannot live there.
|
||||
init(watchHex hex: String) {
|
||||
let cleaned = hex.trimmingCharacters(in: CharacterSet.alphanumerics.inverted)
|
||||
var value: UInt64 = 0
|
||||
Scanner(string: cleaned).scanHexInt64(&value)
|
||||
guard cleaned.count == 6 else { self = .gray; return }
|
||||
self.init(red: Double((value >> 16) & 0xFF) / 255,
|
||||
green: Double((value >> 8) & 0xFF) / 255,
|
||||
blue: Double(value & 0xFF) / 255)
|
||||
}
|
||||
}
|
||||
|
||||
enum WatchEventFormat {
|
||||
static func timeRange(_ event: SnapshotEvent, language: String,
|
||||
calendar: Calendar = Calendar(identifier: .gregorian)) -> String {
|
||||
if event.isAllDay { return WatchL10n.t("watch.allday", language) }
|
||||
|
||||
let time = DateFormatter()
|
||||
time.locale = WatchL10n.locale(language)
|
||||
time.dateFormat = "HH:mm"
|
||||
|
||||
let start = time.string(from: event.start)
|
||||
guard event.end > event.start else { return start }
|
||||
|
||||
// Without the weekday, an event running from Wednesday to Monday reads
|
||||
// as a one-hour slot: "00:00 – 01:00" is true and useless.
|
||||
if calendar.startOfDay(for: event.end) > calendar.startOfDay(for: event.start) {
|
||||
let day = DateFormatter()
|
||||
day.locale = WatchL10n.locale(language)
|
||||
day.setLocalizedDateFormatFromTemplate("EEE")
|
||||
return "\(day.string(from: event.start)) \(start) – "
|
||||
+ "\(day.string(from: event.end)) \(time.string(from: event.end))"
|
||||
}
|
||||
return "\(start) – \(time.string(from: event.end))"
|
||||
}
|
||||
|
||||
static func time(_ date: Date, language: String) -> String {
|
||||
let formatter = DateFormatter()
|
||||
formatter.locale = WatchL10n.locale(language)
|
||||
formatter.dateFormat = "HH:mm"
|
||||
return formatter.string(from: date)
|
||||
}
|
||||
|
||||
/// "Heute", "Morgen", else "Do 11. Sep".
|
||||
static func dayHeader(_ day: Date, language: String,
|
||||
calendar: Calendar, now: Date = Date()) -> String {
|
||||
if calendar.isDate(day, inSameDayAs: now) {
|
||||
return WatchL10n.t("watch.today", language)
|
||||
}
|
||||
if let tomorrow = calendar.date(byAdding: .day, value: 1, to: calendar.startOfDay(for: now)),
|
||||
calendar.isDate(day, inSameDayAs: tomorrow) {
|
||||
return WatchL10n.t("watch.tomorrow", language)
|
||||
}
|
||||
let formatter = DateFormatter()
|
||||
formatter.locale = WatchL10n.locale(language)
|
||||
formatter.setLocalizedDateFormatFromTemplate("EEE d MMM")
|
||||
return formatter.string(from: day)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,44 @@
|
||||
import SwiftUI
|
||||
import CalendarrCore
|
||||
|
||||
/// Every reason there is nothing to show, named. "Never received", "signed out"
|
||||
/// and "genuinely empty" call for different sentences, and showing the wrong one
|
||||
/// sends the user looking in the wrong place.
|
||||
struct WatchStatusView: View {
|
||||
let result: SnapshotReadResult
|
||||
let language: String
|
||||
|
||||
var body: some View {
|
||||
VStack(spacing: 8) {
|
||||
Image(systemName: symbol)
|
||||
.font(.system(size: 22))
|
||||
.foregroundStyle(.secondary)
|
||||
Text(message)
|
||||
.font(.footnote)
|
||||
.multilineTextAlignment(.center)
|
||||
.foregroundStyle(.secondary)
|
||||
}
|
||||
.padding(.horizontal, 6)
|
||||
.frame(maxWidth: .infinity, maxHeight: .infinity)
|
||||
}
|
||||
|
||||
private var symbol: String {
|
||||
switch result {
|
||||
case .ok: return "calendar"
|
||||
case .loggedOut: return "person.crop.circle.badge.xmark"
|
||||
case .neverWritten: return "iphone.and.arrow.forward"
|
||||
case .incompatible: return "arrow.down.circle"
|
||||
case .unreadable: return "exclamationmark.triangle"
|
||||
}
|
||||
}
|
||||
|
||||
private var message: String {
|
||||
switch result {
|
||||
case .ok: return WatchL10n.t("watch.no_events", language)
|
||||
case .loggedOut: return WatchL10n.t("watch.sign_in", language)
|
||||
case .neverWritten: return WatchL10n.t("watch.waiting", language)
|
||||
case .incompatible: return WatchL10n.t("watch.update", language)
|
||||
case .unreadable: return WatchL10n.t("watch.unreadable", language)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,52 @@
|
||||
import Foundation
|
||||
import WidgetKit
|
||||
import CalendarrCore
|
||||
|
||||
/// What the watch views read. Holds the last received snapshot, the calendar
|
||||
/// list, and this watch's filter.
|
||||
@MainActor
|
||||
@Observable
|
||||
final class WatchSnapshotModel {
|
||||
private(set) var result: SnapshotReadResult = .neverWritten
|
||||
private(set) var calendars: [SnapshotCalendar] = []
|
||||
private(set) var hiddenKeys: Set<String> = WatchFilterStore.load()
|
||||
|
||||
private let store = SnapshotStore()
|
||||
private var changeToken: SnapshotChangeNotifier.ObservationToken?
|
||||
|
||||
init() {
|
||||
reload()
|
||||
// The receiver writes through SnapshotStore, which broadcasts. Listening
|
||||
// here means the UI does not have to be notified separately.
|
||||
changeToken = SnapshotChangeNotifier.observe { [weak self] in
|
||||
Task { @MainActor in self?.reload() }
|
||||
}
|
||||
}
|
||||
|
||||
func reload() {
|
||||
result = store.read()
|
||||
calendars = store.readCalendars()
|
||||
}
|
||||
|
||||
/// The snapshot with this watch's filter applied, or nil when there is
|
||||
/// nothing usable to show.
|
||||
var visible: CalendarrSnapshot? {
|
||||
result.snapshot?.filtered(excludingCalendarKeys: hiddenKeys)
|
||||
}
|
||||
|
||||
var language: String { result.snapshot?.language ?? "system" }
|
||||
|
||||
func isHidden(_ key: String) -> Bool { hiddenKeys.contains(key) }
|
||||
|
||||
/// The display name for an event's calendar. Events carry only a
|
||||
/// `calendarKey`; the names travel separately, in the calendar list.
|
||||
func calendarName(for key: String) -> String? {
|
||||
calendars.first { $0.id == key }?.name
|
||||
}
|
||||
|
||||
func setHidden(_ key: String, _ hidden: Bool) {
|
||||
if hidden { hiddenKeys.insert(key) } else { hiddenKeys.remove(key) }
|
||||
WatchFilterStore.save(hiddenKeys)
|
||||
WidgetCenter.shared.reloadAllTimelines()
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,81 @@
|
||||
import Foundation
|
||||
import WatchConnectivity
|
||||
import WidgetKit
|
||||
@_spi(Writer) import CalendarrCore
|
||||
|
||||
/// Takes what the phone sends and writes it into this watch's own App Group
|
||||
/// container, using the same `SnapshotStore` the phone writes with. One wire
|
||||
/// format, one reader, no drift.
|
||||
///
|
||||
/// Only the app runs a `WCSession`; a widget extension cannot. The complications
|
||||
/// always read what this wrote.
|
||||
@MainActor
|
||||
final class WatchSnapshotReceiver: NSObject {
|
||||
static let shared = WatchSnapshotReceiver()
|
||||
|
||||
private let store = SnapshotStore()
|
||||
private let sessions = SharedSessionStore()
|
||||
|
||||
/// Shown in the agenda footer, because a silent failure here looks exactly
|
||||
/// like an empty calendar.
|
||||
private(set) var lastError: String?
|
||||
|
||||
func activate() {
|
||||
guard WCSession.isSupported() else { return }
|
||||
WCSession.default.delegate = self
|
||||
WCSession.default.activate()
|
||||
}
|
||||
|
||||
/// Ask the phone for the current state. Covers "the app is open in front of
|
||||
/// me and looks stale"; the other two paths are push-driven.
|
||||
func requestRefresh() {
|
||||
let session = WCSession.default
|
||||
guard session.activationState == .activated, session.isReachable else { return }
|
||||
|
||||
session.sendMessage(["request": "snapshot"],
|
||||
replyHandler: { reply in
|
||||
Task { @MainActor in self.apply(reply) }
|
||||
},
|
||||
errorHandler: { error in
|
||||
Task { @MainActor in self.lastError = error.localizedDescription }
|
||||
})
|
||||
}
|
||||
|
||||
func apply(_ dict: [String: Any]) {
|
||||
guard !dict.isEmpty else { return }
|
||||
do {
|
||||
let payload = try WatchTransportPayload.decode(dict)
|
||||
// This write posts SnapshotChangeNotifier, which is what the read
|
||||
// model listens for — so the UI updates without being told twice.
|
||||
try store.write(payload.snapshot)
|
||||
try store.writeCalendars(payload.calendars)
|
||||
if let session = payload.session { try sessions.write(session) }
|
||||
WidgetCenter.shared.reloadAllTimelines()
|
||||
lastError = nil
|
||||
} catch {
|
||||
lastError = error.localizedDescription
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
extension WatchSnapshotReceiver: WCSessionDelegate {
|
||||
nonisolated func session(_ session: WCSession,
|
||||
activationDidCompleteWith state: WCSessionActivationState,
|
||||
error: Error?) {
|
||||
if let error {
|
||||
Task { @MainActor in self.lastError = error.localizedDescription }
|
||||
}
|
||||
}
|
||||
|
||||
/// The main path: latest state only, delivered when this app launches or wakes.
|
||||
nonisolated func session(_ session: WCSession,
|
||||
didReceiveApplicationContext applicationContext: [String: Any]) {
|
||||
Task { @MainActor in self.apply(applicationContext) }
|
||||
}
|
||||
|
||||
/// The complication push, which is the only path that wakes this app while
|
||||
/// it is not running.
|
||||
nonisolated func session(_ session: WCSession, didReceiveUserInfo userInfo: [String: Any]) {
|
||||
Task { @MainActor in self.apply(userInfo) }
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
{
|
||||
"colors" : [
|
||||
{
|
||||
"idiom" : "universal"
|
||||
}
|
||||
],
|
||||
"info" : {
|
||||
"author" : "xcode",
|
||||
"version" : 1
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
{
|
||||
"images" : [
|
||||
{
|
||||
"idiom" : "universal",
|
||||
"platform" : "watchos",
|
||||
"size" : "1024x1024"
|
||||
}
|
||||
],
|
||||
"info" : {
|
||||
"author" : "xcode",
|
||||
"version" : 1
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
{
|
||||
"info" : {
|
||||
"author" : "xcode",
|
||||
"version" : 1
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
{
|
||||
"colors" : [
|
||||
{
|
||||
"idiom" : "universal"
|
||||
}
|
||||
],
|
||||
"info" : {
|
||||
"author" : "xcode",
|
||||
"version" : 1
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||
<plist version="1.0">
|
||||
<dict>
|
||||
<key>com.apple.security.application-groups</key>
|
||||
<array>
|
||||
<string>group.com.scarriffleservices.calendarr</string>
|
||||
</array>
|
||||
</dict>
|
||||
</plist>
|
||||
@@ -0,0 +1,161 @@
|
||||
import WidgetKit
|
||||
import SwiftUI
|
||||
|
||||
@main
|
||||
struct CalendarrWatchComplicationBundle: WidgetBundle {
|
||||
var body: some Widget {
|
||||
WatchNextEventWidget()
|
||||
WatchCornerCountdownWidget()
|
||||
WatchCornerTitleWidget()
|
||||
WatchCornerTimeWidget()
|
||||
WatchCountdownWidget()
|
||||
WatchDateWidget()
|
||||
WatchTodayCountWidget()
|
||||
WatchNextTwoWidget()
|
||||
WatchDatePlusEventWidget()
|
||||
}
|
||||
}
|
||||
|
||||
private extension View {
|
||||
/// Accessory families paint their own background; ours must stay out of
|
||||
/// the way so the watch face's tint comes through.
|
||||
func complicationChrome(_ language: String) -> some View {
|
||||
containerBackground(for: .widget) { Color.clear }
|
||||
.environment(\.locale, WatchL10n.locale(language))
|
||||
}
|
||||
}
|
||||
|
||||
struct WatchNextEventWidget: Widget {
|
||||
let kind = "WatchNextEvent"
|
||||
|
||||
var body: some WidgetConfiguration {
|
||||
AppIntentConfiguration(kind: kind,
|
||||
intent: WatchCalendarSelectionIntent.self,
|
||||
provider: WatchComplicationProvider()) { entry in
|
||||
NextEventComplicationView(entry: entry).complicationChrome(entry.language)
|
||||
}
|
||||
.configurationDisplayName("Nächster Termin")
|
||||
.description("Zeit, Titel und Ort des nächsten Termins.")
|
||||
.supportedFamilies([.accessoryRectangular, .accessoryInline])
|
||||
}
|
||||
}
|
||||
|
||||
struct WatchCornerCountdownWidget: Widget {
|
||||
let kind = "WatchCornerCountdown"
|
||||
|
||||
var body: some WidgetConfiguration {
|
||||
AppIntentConfiguration(kind: kind,
|
||||
intent: WatchCalendarSelectionIntent.self,
|
||||
provider: WatchComplicationProvider()) { entry in
|
||||
CornerCountdownComplicationView(entry: entry).complicationChrome(entry.language)
|
||||
}
|
||||
.configurationDisplayName("Restzeit (Ecke)")
|
||||
.description("Restzeit bis zum nächsten Termin, Name am Rand.")
|
||||
.supportedFamilies([.accessoryCorner])
|
||||
}
|
||||
}
|
||||
|
||||
struct WatchCornerTitleWidget: Widget {
|
||||
let kind = "WatchCornerTitle"
|
||||
|
||||
var body: some WidgetConfiguration {
|
||||
AppIntentConfiguration(kind: kind,
|
||||
intent: WatchCalendarSelectionIntent.self,
|
||||
provider: WatchComplicationProvider()) { entry in
|
||||
CornerTitleComplicationView(entry: entry).complicationChrome(entry.language)
|
||||
}
|
||||
.configurationDisplayName("Terminname (Ecke)")
|
||||
.description("Name des nächsten Termins, Uhrzeit klein am Rand.")
|
||||
.supportedFamilies([.accessoryCorner])
|
||||
}
|
||||
}
|
||||
|
||||
struct WatchCornerTimeWidget: Widget {
|
||||
let kind = "WatchCornerTime"
|
||||
|
||||
var body: some WidgetConfiguration {
|
||||
AppIntentConfiguration(kind: kind,
|
||||
intent: WatchCalendarSelectionIntent.self,
|
||||
provider: WatchComplicationProvider()) { entry in
|
||||
CornerTimeComplicationView(entry: entry).complicationChrome(entry.language)
|
||||
}
|
||||
.configurationDisplayName("Uhrzeit (Ecke)")
|
||||
.description("Uhrzeit des nächsten Termins, Name am Rand.")
|
||||
.supportedFamilies([.accessoryCorner])
|
||||
}
|
||||
}
|
||||
|
||||
struct WatchCountdownWidget: Widget {
|
||||
let kind = "WatchCountdown"
|
||||
|
||||
var body: some WidgetConfiguration {
|
||||
AppIntentConfiguration(kind: kind,
|
||||
intent: WatchCalendarSelectionIntent.self,
|
||||
provider: WatchComplicationProvider()) { entry in
|
||||
CountdownComplicationView(entry: entry).complicationChrome(entry.language)
|
||||
}
|
||||
.configurationDisplayName("Countdown")
|
||||
.description("Ring, der bis zum nächsten Termin leerläuft.")
|
||||
.supportedFamilies([.accessoryCircular, .accessoryInline])
|
||||
}
|
||||
}
|
||||
|
||||
struct WatchDateWidget: Widget {
|
||||
let kind = "WatchDate"
|
||||
|
||||
var body: some WidgetConfiguration {
|
||||
AppIntentConfiguration(kind: kind,
|
||||
intent: WatchCalendarSelectionIntent.self,
|
||||
provider: WatchComplicationProvider()) { entry in
|
||||
DateComplicationView(entry: entry).complicationChrome(entry.language)
|
||||
}
|
||||
.configurationDisplayName("Datum")
|
||||
.description("Tag und Monat.")
|
||||
.supportedFamilies([.accessoryCircular, .accessoryInline])
|
||||
}
|
||||
}
|
||||
|
||||
struct WatchTodayCountWidget: Widget {
|
||||
let kind = "WatchTodayCount"
|
||||
|
||||
var body: some WidgetConfiguration {
|
||||
AppIntentConfiguration(kind: kind,
|
||||
intent: WatchCalendarSelectionIntent.self,
|
||||
provider: WatchComplicationProvider()) { entry in
|
||||
TodayCountComplicationView(entry: entry).complicationChrome(entry.language)
|
||||
}
|
||||
.configurationDisplayName("Termine heute")
|
||||
.description("Anzahl der heutigen Termine.")
|
||||
.supportedFamilies([.accessoryCircular, .accessoryInline])
|
||||
}
|
||||
}
|
||||
|
||||
struct WatchNextTwoWidget: Widget {
|
||||
let kind = "WatchNextTwo"
|
||||
|
||||
var body: some WidgetConfiguration {
|
||||
AppIntentConfiguration(kind: kind,
|
||||
intent: WatchCalendarSelectionIntent.self,
|
||||
provider: WatchComplicationProvider()) { entry in
|
||||
NextTwoComplicationView(entry: entry).complicationChrome(entry.language)
|
||||
}
|
||||
.configurationDisplayName("Die nächsten zwei")
|
||||
.description("Die beiden nächsten Termine.")
|
||||
.supportedFamilies([.accessoryRectangular])
|
||||
}
|
||||
}
|
||||
|
||||
struct WatchDatePlusEventWidget: Widget {
|
||||
let kind = "WatchDatePlusEvent"
|
||||
|
||||
var body: some WidgetConfiguration {
|
||||
AppIntentConfiguration(kind: kind,
|
||||
intent: WatchCalendarSelectionIntent.self,
|
||||
provider: WatchComplicationProvider()) { entry in
|
||||
DatePlusEventComplicationView(entry: entry).complicationChrome(entry.language)
|
||||
}
|
||||
.configurationDisplayName("Datum + Termin")
|
||||
.description("Datum und nächster Termin in einem Slot.")
|
||||
.supportedFamilies([.accessoryRectangular])
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,90 @@
|
||||
import SwiftUI
|
||||
import WidgetKit
|
||||
import CalendarrCore
|
||||
|
||||
// A corner holds **one** value plus the curved bezel label. A second inner line
|
||||
// runs into the curved text — do not add one. `.widgetLabel` is what makes
|
||||
// WidgetKit lay the text along the edge; there is no arc to draw by hand.
|
||||
|
||||
/// Value: live countdown. Label: time and title.
|
||||
struct CornerCountdownComplicationView: View {
|
||||
let entry: WatchComplicationEntry
|
||||
|
||||
var body: some View {
|
||||
let next = WatchComplicationSupport.nextEvent(in: entry.snapshot, at: entry.date)
|
||||
Group {
|
||||
if let next {
|
||||
Text(WatchComplicationSupport.compactCountdown(
|
||||
to: WatchComplicationSupport.countdownTarget(next, at: entry.date),
|
||||
at: entry.date,
|
||||
language: entry.language))
|
||||
.lineLimit(1)
|
||||
.minimumScaleFactor(0.6)
|
||||
.widgetAccentable()
|
||||
} else {
|
||||
Image(systemName: "calendar")
|
||||
}
|
||||
}
|
||||
.widgetLabel {
|
||||
Text(WatchComplicationSupport.eventLabel(next, at: entry.date, language: entry.language))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Value: the appointment's clock time. Label: the title.
|
||||
struct CornerTimeComplicationView: View {
|
||||
let entry: WatchComplicationEntry
|
||||
|
||||
var body: some View {
|
||||
let next = WatchComplicationSupport.nextEvent(in: entry.snapshot, at: entry.date)
|
||||
Group {
|
||||
if let next {
|
||||
Text(next.isAllDay
|
||||
? WatchL10n.t("watch.allday", entry.language)
|
||||
: WatchComplicationSupport.time(next.start, language: entry.language))
|
||||
.lineLimit(1)
|
||||
.minimumScaleFactor(0.6)
|
||||
.widgetAccentable()
|
||||
} else {
|
||||
Image(systemName: "calendar")
|
||||
}
|
||||
}
|
||||
.widgetLabel {
|
||||
Text(next?.title ?? WatchL10n.t("watch.no_events", entry.language))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Value: the appointment's name. Label: the time, small and curved.
|
||||
///
|
||||
/// This is the inverse of how the slot is shaped — the curved bezel is where a
|
||||
/// corner has room for long text, and the inner area holds three or four
|
||||
/// characters. The title therefore shrinks and truncates here. Built this way
|
||||
/// on request; `CornerTimeComplicationView` is the same information with the
|
||||
/// roles the other way round, so both can be compared on the face.
|
||||
struct CornerTitleComplicationView: View {
|
||||
let entry: WatchComplicationEntry
|
||||
|
||||
var body: some View {
|
||||
let next = WatchComplicationSupport.nextEvent(in: entry.snapshot, at: entry.date)
|
||||
Group {
|
||||
if let next {
|
||||
Text(next.title)
|
||||
.font(.system(size: 13, weight: .medium))
|
||||
.lineLimit(2)
|
||||
.minimumScaleFactor(0.5)
|
||||
.multilineTextAlignment(.center)
|
||||
.widgetAccentable()
|
||||
} else {
|
||||
Image(systemName: "calendar")
|
||||
}
|
||||
}
|
||||
.widgetLabel {
|
||||
Text(next.map {
|
||||
$0.isAllDay
|
||||
? WatchL10n.t("watch.allday", entry.language)
|
||||
: WatchComplicationSupport.time($0.start, language: entry.language)
|
||||
} ?? WatchL10n.t("watch.no_events", entry.language))
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||
<plist version="1.0">
|
||||
<dict>
|
||||
<key>NSExtension</key>
|
||||
<dict>
|
||||
<key>NSExtensionPointIdentifier</key>
|
||||
<string>com.apple.widgetkit-extension</string>
|
||||
</dict>
|
||||
</dict>
|
||||
</plist>
|
||||
@@ -0,0 +1,113 @@
|
||||
import SwiftUI
|
||||
import WidgetKit
|
||||
import CalendarrCore
|
||||
|
||||
/// Time, title, location. The variant that answers the actual question.
|
||||
struct NextEventComplicationView: View {
|
||||
let entry: WatchComplicationEntry
|
||||
@Environment(\.widgetFamily) private var family
|
||||
|
||||
var body: some View {
|
||||
let next = WatchComplicationSupport.nextEvent(in: entry.snapshot, at: entry.date)
|
||||
switch family {
|
||||
case .accessoryInline:
|
||||
inline(next)
|
||||
default:
|
||||
rectangular(next)
|
||||
}
|
||||
}
|
||||
|
||||
@ViewBuilder
|
||||
private func rectangular(_ next: SnapshotEvent?) -> some View {
|
||||
VStack(alignment: .leading, spacing: 1) {
|
||||
if let next {
|
||||
Text(next.isAllDay
|
||||
? WatchL10n.t("watch.allday", entry.language)
|
||||
: WatchComplicationSupport.time(next.start, language: entry.language))
|
||||
.font(.system(size: 12, weight: .semibold))
|
||||
.widgetAccentable()
|
||||
Text(next.title)
|
||||
.font(.system(size: 15))
|
||||
.lineLimit(1)
|
||||
if !next.location.isEmpty {
|
||||
Text(next.location)
|
||||
.font(.system(size: 11))
|
||||
.lineLimit(1)
|
||||
}
|
||||
} else {
|
||||
Image(systemName: "calendar").font(.system(size: 13)).widgetAccentable()
|
||||
Text(WatchL10n.t("watch.no_events", entry.language)).font(.system(size: 13))
|
||||
}
|
||||
}
|
||||
.frame(maxWidth: .infinity, maxHeight: .infinity, alignment: .topLeading)
|
||||
}
|
||||
|
||||
private func inline(_ next: SnapshotEvent?) -> some View {
|
||||
let text: String = {
|
||||
guard let next else { return WatchL10n.t("watch.no_events", entry.language) }
|
||||
if next.isAllDay { return next.title }
|
||||
return "\(WatchComplicationSupport.time(next.start, language: entry.language)) \(next.title)"
|
||||
}()
|
||||
return Label(text, systemImage: "calendar")
|
||||
}
|
||||
}
|
||||
|
||||
/// The next two, so back-to-back appointments are visible at once.
|
||||
struct NextTwoComplicationView: View {
|
||||
let entry: WatchComplicationEntry
|
||||
|
||||
var body: some View {
|
||||
let events = WatchComplicationSupport.upcoming(in: entry.snapshot, at: entry.date, limit: 2)
|
||||
VStack(alignment: .leading, spacing: 3) {
|
||||
if events.isEmpty {
|
||||
Image(systemName: "calendar").font(.system(size: 13)).widgetAccentable()
|
||||
Text(WatchL10n.t("watch.no_events", entry.language)).font(.system(size: 13))
|
||||
} else {
|
||||
ForEach(events) { event in
|
||||
HStack(spacing: 5) {
|
||||
Text(event.isAllDay
|
||||
? WatchL10n.t("watch.allday", entry.language)
|
||||
: WatchComplicationSupport.time(event.start, language: entry.language))
|
||||
.font(.system(size: 12, weight: .semibold))
|
||||
.widgetAccentable()
|
||||
Text(event.title)
|
||||
.font(.system(size: 13))
|
||||
.lineLimit(1)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
.frame(maxWidth: .infinity, maxHeight: .infinity, alignment: .topLeading)
|
||||
}
|
||||
}
|
||||
|
||||
/// Weekday and date plus the next appointment, so one slot does both jobs.
|
||||
struct DatePlusEventComplicationView: View {
|
||||
let entry: WatchComplicationEntry
|
||||
|
||||
var body: some View {
|
||||
let next = WatchComplicationSupport.nextEvent(in: entry.snapshot, at: entry.date)
|
||||
VStack(alignment: .leading, spacing: 2) {
|
||||
Text(dateLine)
|
||||
.font(.system(size: 11, weight: .semibold))
|
||||
.widgetAccentable()
|
||||
if let next {
|
||||
Text(next.isAllDay
|
||||
? next.title
|
||||
: "\(WatchComplicationSupport.time(next.start, language: entry.language)) \(next.title)")
|
||||
.font(.system(size: 14))
|
||||
.lineLimit(1)
|
||||
} else {
|
||||
Text(WatchL10n.t("watch.no_events", entry.language)).font(.system(size: 14))
|
||||
}
|
||||
}
|
||||
.frame(maxWidth: .infinity, maxHeight: .infinity, alignment: .topLeading)
|
||||
}
|
||||
|
||||
private var dateLine: String {
|
||||
let formatter = DateFormatter()
|
||||
formatter.locale = WatchL10n.locale(entry.language)
|
||||
formatter.setLocalizedDateFormatFromTemplate("EEE d MMMM")
|
||||
return formatter.string(from: entry.date).uppercased()
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,156 @@
|
||||
import SwiftUI
|
||||
import WidgetKit
|
||||
import CalendarrCore
|
||||
|
||||
/// Attaches the curved bezel label that a face with a bezel slot — Infograph —
|
||||
/// renders across the top, where the tick marks otherwise are. Faces without
|
||||
/// that slot ignore it, so it costs nothing to always provide.
|
||||
private extension View {
|
||||
func eventBezelLabel(_ entry: WatchComplicationEntry) -> some View {
|
||||
let next = WatchComplicationSupport.nextEvent(in: entry.snapshot, at: entry.date)
|
||||
return widgetLabel {
|
||||
Text(WatchComplicationSupport.eventLabel(next, at: entry.date, language: entry.language))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// A draining ring plus the remaining time.
|
||||
struct CountdownComplicationView: View {
|
||||
let entry: WatchComplicationEntry
|
||||
@Environment(\.widgetFamily) private var family
|
||||
|
||||
var body: some View {
|
||||
let next = WatchComplicationSupport.nextEvent(in: entry.snapshot, at: entry.date)
|
||||
switch family {
|
||||
case .accessoryInline:
|
||||
inline(next)
|
||||
default:
|
||||
circular(next)
|
||||
}
|
||||
}
|
||||
|
||||
@ViewBuilder
|
||||
private func circular(_ next: SnapshotEvent?) -> some View {
|
||||
if let next {
|
||||
let target = WatchComplicationSupport.countdownTarget(next, at: entry.date)
|
||||
Gauge(value: WatchComplicationSupport.gaugeFraction(to: target, at: entry.date)) {
|
||||
Image(systemName: "calendar")
|
||||
} currentValueLabel: {
|
||||
Text(WatchComplicationSupport.compactCountdown(to: target, at: entry.date,
|
||||
language: entry.language))
|
||||
.font(.system(size: 13))
|
||||
.lineLimit(1)
|
||||
.minimumScaleFactor(0.6)
|
||||
}
|
||||
.gaugeStyle(.accessoryCircularCapacity)
|
||||
.widgetAccentable()
|
||||
.eventBezelLabel(entry)
|
||||
} else {
|
||||
ZStack {
|
||||
AccessoryWidgetBackground()
|
||||
Image(systemName: "calendar").font(.system(size: 18))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ViewBuilder
|
||||
private func inline(_ next: SnapshotEvent?) -> some View {
|
||||
if let next {
|
||||
Label {
|
||||
Text(WatchComplicationSupport.countdownTarget(next, at: entry.date), style: .timer)
|
||||
} icon: {
|
||||
Image(systemName: "timer")
|
||||
}
|
||||
} else {
|
||||
Label(WatchL10n.t("watch.no_events", entry.language), systemImage: "calendar")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Weekday and day number, the way a date is actually read. The ring appears
|
||||
/// only while an appointment is running, and then shows how far through it we
|
||||
/// are — an empty track around an idle day is noise, so there is no Gauge at
|
||||
/// all in that case.
|
||||
struct DateComplicationView: View {
|
||||
let entry: WatchComplicationEntry
|
||||
@Environment(\.widgetFamily) private var family
|
||||
|
||||
private var day: Int {
|
||||
Calendar(identifier: .gregorian).component(.day, from: entry.date)
|
||||
}
|
||||
|
||||
private var weekday: String {
|
||||
WatchComplicationSupport.weekdayAbbreviation(entry.date, language: entry.language)
|
||||
}
|
||||
|
||||
var body: some View {
|
||||
switch family {
|
||||
case .accessoryInline:
|
||||
Label("\(weekday) \(day)", systemImage: "calendar")
|
||||
default:
|
||||
circular
|
||||
}
|
||||
}
|
||||
|
||||
@ViewBuilder
|
||||
private var circular: some View {
|
||||
if let running = WatchComplicationSupport.runningEvent(in: entry.snapshot, at: entry.date) {
|
||||
Gauge(value: WatchComplicationSupport.progress(of: running, at: entry.date)) {
|
||||
Text(weekday)
|
||||
} currentValueLabel: {
|
||||
dateStack
|
||||
}
|
||||
.gaugeStyle(.accessoryCircularCapacity)
|
||||
.eventBezelLabel(entry)
|
||||
} else {
|
||||
ZStack {
|
||||
AccessoryWidgetBackground()
|
||||
dateStack
|
||||
}
|
||||
.eventBezelLabel(entry)
|
||||
}
|
||||
}
|
||||
|
||||
private var dateStack: some View {
|
||||
VStack(spacing: -2) {
|
||||
Text(weekday)
|
||||
.font(.system(size: 11, weight: .semibold))
|
||||
.widgetAccentable()
|
||||
Text("\(day)")
|
||||
.font(.system(size: 23, weight: .semibold))
|
||||
.lineLimit(1)
|
||||
.minimumScaleFactor(0.7)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// How many appointments today. Says "busy", not "what".
|
||||
struct TodayCountComplicationView: View {
|
||||
let entry: WatchComplicationEntry
|
||||
@Environment(\.widgetFamily) private var family
|
||||
|
||||
private var count: Int {
|
||||
WatchComplicationSupport.todayCount(in: entry.snapshot, at: entry.date)
|
||||
}
|
||||
|
||||
var body: some View {
|
||||
switch family {
|
||||
case .accessoryInline:
|
||||
Label("\(count) \(WatchL10n.t("watch.events_today", entry.language))",
|
||||
systemImage: "calendar")
|
||||
default:
|
||||
Gauge(value: WatchComplicationSupport.todayRemainingFraction(in: entry.snapshot,
|
||||
at: entry.date)) {
|
||||
Image(systemName: "calendar")
|
||||
} currentValueLabel: {
|
||||
Text("\(count)")
|
||||
.font(.system(size: 20, weight: .semibold))
|
||||
.lineLimit(1)
|
||||
.minimumScaleFactor(0.6)
|
||||
}
|
||||
.gaugeStyle(.accessoryCircularCapacity)
|
||||
.widgetAccentable()
|
||||
.eventBezelLabel(entry)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,44 @@
|
||||
import AppIntents
|
||||
import WidgetKit
|
||||
import CalendarrCore
|
||||
|
||||
/// One calendar option in a complication's configuration.
|
||||
struct WatchCalendarEntity: AppEntity, Identifiable {
|
||||
let id: String
|
||||
let name: String
|
||||
|
||||
static var typeDisplayRepresentation: TypeDisplayRepresentation = "Kalender"
|
||||
static var defaultQuery = WatchCalendarEntityQuery()
|
||||
|
||||
var displayRepresentation: DisplayRepresentation {
|
||||
DisplayRepresentation(title: LocalizedStringResource(stringLiteral: name))
|
||||
}
|
||||
}
|
||||
|
||||
/// Reads the calendars from this watch's App Group container, so configuration
|
||||
/// needs no network call and no phone.
|
||||
struct WatchCalendarEntityQuery: EntityQuery {
|
||||
func entities(for identifiers: [String]) async throws -> [WatchCalendarEntity] {
|
||||
let wanted = Set(identifiers)
|
||||
return SnapshotStore().readCalendars()
|
||||
.filter { wanted.contains($0.id) }
|
||||
.map { WatchCalendarEntity(id: $0.id, name: $0.name) }
|
||||
}
|
||||
|
||||
func suggestedEntities() async throws -> [WatchCalendarEntity] {
|
||||
SnapshotStore().readCalendars().map { WatchCalendarEntity(id: $0.id, name: $0.name) }
|
||||
}
|
||||
}
|
||||
|
||||
/// Per-complication override of the calendar selection. The in-app toggles are
|
||||
/// the primary path — intent configuration on a watch face is awkward to
|
||||
/// operate — so an empty selection means "whatever the app is set to".
|
||||
struct WatchCalendarSelectionIntent: WidgetConfigurationIntent {
|
||||
static var title: LocalizedStringResource = "Kalender auswählen"
|
||||
static var description = IntentDescription("Leer = die Auswahl aus der Watch-App.")
|
||||
|
||||
// Must be optional: WidgetConfigurationIntent requires optional parameter
|
||||
// types, and the watchOS SDK enforces it where iOS lets a bare array pass.
|
||||
@Parameter(title: "Kalender")
|
||||
var selectedCalendars: [WatchCalendarEntity]?
|
||||
}
|
||||
@@ -0,0 +1,62 @@
|
||||
import WidgetKit
|
||||
import AppIntents
|
||||
import SwiftUI
|
||||
import CalendarrCore
|
||||
|
||||
struct WatchComplicationEntry: TimelineEntry {
|
||||
let date: Date
|
||||
let snapshot: CalendarrSnapshot?
|
||||
|
||||
var language: String { snapshot?.language ?? "system" }
|
||||
}
|
||||
|
||||
/// Builds every Calendarr complication's timeline.
|
||||
///
|
||||
/// Entries land on event boundaries rather than on a fixed schedule, so the
|
||||
/// complication flips in the exact minute the next appointment changes without
|
||||
/// a network call and without spending refresh budget. New *data* arrives by
|
||||
/// push from the phone, which is why the refresh policy is `.atEnd`: when the
|
||||
/// timeline simply runs out, regenerating from the same snapshot is cheap and
|
||||
/// correct.
|
||||
struct WatchComplicationProvider: AppIntentTimelineProvider {
|
||||
typealias Entry = WatchComplicationEntry
|
||||
typealias Intent = WatchCalendarSelectionIntent
|
||||
|
||||
func placeholder(in context: Context) -> WatchComplicationEntry {
|
||||
WatchComplicationEntry(date: .now, snapshot: nil)
|
||||
}
|
||||
|
||||
func snapshot(for configuration: WatchCalendarSelectionIntent,
|
||||
in context: Context) async -> WatchComplicationEntry {
|
||||
WatchComplicationEntry(date: .now, snapshot: resolved(configuration))
|
||||
}
|
||||
|
||||
func timeline(for configuration: WatchCalendarSelectionIntent,
|
||||
in context: Context) async -> Timeline<WatchComplicationEntry> {
|
||||
let snapshot = resolved(configuration)
|
||||
let now = Date()
|
||||
let dates = snapshot.map { ComplicationTimeline.entryDates(from: now, in: $0) } ?? [now]
|
||||
return Timeline(entries: dates.map { WatchComplicationEntry(date: $0, snapshot: snapshot) },
|
||||
policy: .atEnd)
|
||||
}
|
||||
|
||||
/// What the complication gallery offers before anyone configures anything.
|
||||
/// Required on watchOS — iOS has a default implementation, the watch does
|
||||
/// not, because the gallery has to be populated.
|
||||
///
|
||||
/// One entry, with nothing selected: that means "whatever the app is set
|
||||
/// to", which is the filter the user already chose there.
|
||||
func recommendations() -> [AppIntentRecommendation<WatchCalendarSelectionIntent>] {
|
||||
[AppIntentRecommendation(intent: WatchCalendarSelectionIntent(),
|
||||
description: Text("Calendarr"))]
|
||||
}
|
||||
|
||||
/// The app-level filter applies first; a per-complication selection narrows
|
||||
/// further. An empty selection means "whatever the app is set to".
|
||||
private func resolved(_ configuration: WatchCalendarSelectionIntent) -> CalendarrSnapshot? {
|
||||
guard case .ok(let snapshot) = SnapshotStore().read() else { return nil }
|
||||
return snapshot
|
||||
.filtered(excludingCalendarKeys: WatchFilterStore.load())
|
||||
.filtered(toCalendarKeys: Set((configuration.selectedCalendars ?? []).map(\.id)))
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,131 @@
|
||||
import Foundation
|
||||
import CalendarrCore
|
||||
|
||||
enum WatchComplicationSupport {
|
||||
/// The first event that has not ended yet — including one running now,
|
||||
/// which is what "what is next" means when you are already in a meeting.
|
||||
/// Matches `WatchPushPolicy.nextEventIdentity`, deliberately.
|
||||
static func nextEvent(in snapshot: CalendarrSnapshot?, at now: Date) -> SnapshotEvent? {
|
||||
snapshot?.events.filter { $0.end > now }.min { $0.start < $1.start }
|
||||
}
|
||||
|
||||
static func upcoming(in snapshot: CalendarrSnapshot?, at now: Date, limit: Int) -> [SnapshotEvent] {
|
||||
guard let snapshot else { return [] }
|
||||
return Array(snapshot.events.filter { $0.end > now }.sorted { $0.start < $1.start }.prefix(limit))
|
||||
}
|
||||
|
||||
static func todayCount(in snapshot: CalendarrSnapshot?, at now: Date,
|
||||
calendar: Calendar = Calendar(identifier: .gregorian)) -> Int {
|
||||
guard let snapshot else { return 0 }
|
||||
let dayStart = calendar.startOfDay(for: now)
|
||||
guard let dayEnd = calendar.date(byAdding: .day, value: 1, to: dayStart) else { return 0 }
|
||||
return snapshot.events.filter { $0.start < dayEnd && $0.end > dayStart }.count
|
||||
}
|
||||
|
||||
/// The appointment happening right now, if there is one. All-day events are
|
||||
/// excluded on purpose: one of those is "running" for twenty-four hours and
|
||||
/// would hold the ring at some arbitrary fraction all day, which says
|
||||
/// nothing.
|
||||
static func runningEvent(in snapshot: CalendarrSnapshot?, at now: Date) -> SnapshotEvent? {
|
||||
snapshot?.events
|
||||
.filter { !$0.isAllDay && $0.start <= now && $0.end > now }
|
||||
.min { $0.end < $1.end }
|
||||
}
|
||||
|
||||
/// How far through an appointment we are, 0 at its start and 1 at its end.
|
||||
static func progress(of event: SnapshotEvent, at now: Date) -> Double {
|
||||
let total = event.end.timeIntervalSince(event.start)
|
||||
guard total > 0 else { return 0 }
|
||||
return min(1, max(0, now.timeIntervalSince(event.start) / total))
|
||||
}
|
||||
|
||||
static func weekdayAbbreviation(_ date: Date, language: String) -> String {
|
||||
let formatter = DateFormatter()
|
||||
formatter.locale = WatchL10n.locale(language)
|
||||
formatter.dateFormat = "EEE"
|
||||
return formatter.string(from: date)
|
||||
.replacingOccurrences(of: ".", with: "")
|
||||
.uppercased()
|
||||
}
|
||||
|
||||
/// What a countdown counts to: the start if it has not begun, otherwise the
|
||||
/// end. Counting up from a start time that has passed reads as broken.
|
||||
static func countdownTarget(_ event: SnapshotEvent, at now: Date) -> Date {
|
||||
event.start > now ? event.start : event.end
|
||||
}
|
||||
|
||||
/// A countdown narrow enough for a corner or a circular gauge: at most
|
||||
/// three characters. `Text(style: .timer)` would be live and free of
|
||||
/// timeline entries, but renders `1:23:45` and overlaps whatever sits
|
||||
/// beside it — so the timeline carries minute-granular entries instead and
|
||||
/// this is recomputed per entry.
|
||||
static func compactCountdown(to target: Date, at now: Date, language: String) -> String {
|
||||
let seconds = target.timeIntervalSince(now)
|
||||
guard seconds > 0 else { return WatchL10n.t("watch.now", language) }
|
||||
let minutes = Int(seconds / 60)
|
||||
if minutes < 1 { return WatchL10n.t("watch.now", language) }
|
||||
if minutes < 60 { return "\(minutes)m" }
|
||||
let hours = minutes / 60
|
||||
if hours < 24 { return "\(hours)h" }
|
||||
return "\(hours / 24)d"
|
||||
}
|
||||
|
||||
/// How full a draining ring should be, over a two-hour lead window.
|
||||
static func gaugeFraction(to target: Date, at now: Date) -> Double {
|
||||
let window: TimeInterval = 2 * 3600
|
||||
return min(1, max(0, target.timeIntervalSince(now) / window))
|
||||
}
|
||||
|
||||
/// How much of today is still ahead. Drains through the day, matching the
|
||||
/// countdown ring rather than inventing a second direction.
|
||||
static func dayRemainingFraction(at now: Date,
|
||||
calendar: Calendar = Calendar(identifier: .gregorian)) -> Double {
|
||||
let dayStart = calendar.startOfDay(for: now)
|
||||
guard let dayEnd = calendar.date(byAdding: .day, value: 1, to: dayStart) else { return 0 }
|
||||
let total = dayEnd.timeIntervalSince(dayStart)
|
||||
guard total > 0 else { return 0 }
|
||||
return min(1, max(0, dayEnd.timeIntervalSince(now) / total))
|
||||
}
|
||||
|
||||
/// The share of today's appointments still ahead. Zero when there are none,
|
||||
/// which leaves the ring empty rather than full — an empty day should not
|
||||
/// look like an untouched one.
|
||||
static func todayRemainingFraction(in snapshot: CalendarrSnapshot?, at now: Date,
|
||||
calendar: Calendar = Calendar(identifier: .gregorian)) -> Double {
|
||||
guard let snapshot else { return 0 }
|
||||
let dayStart = calendar.startOfDay(for: now)
|
||||
guard let dayEnd = calendar.date(byAdding: .day, value: 1, to: dayStart) else { return 0 }
|
||||
let today = snapshot.events.filter { $0.start < dayEnd && $0.end > dayStart }
|
||||
guard !today.isEmpty else { return 0 }
|
||||
let ahead = today.filter { $0.end > now }.count
|
||||
return min(1, max(0, Double(ahead) / Double(today.count)))
|
||||
}
|
||||
|
||||
static func time(_ date: Date, language: String) -> String {
|
||||
let formatter = DateFormatter()
|
||||
formatter.locale = WatchL10n.locale(language)
|
||||
formatter.dateFormat = "HH:mm"
|
||||
return formatter.string(from: date)
|
||||
}
|
||||
|
||||
static func monthAbbreviation(_ date: Date, language: String) -> String {
|
||||
let formatter = DateFormatter()
|
||||
formatter.locale = WatchL10n.locale(language)
|
||||
formatter.dateFormat = "LLL"
|
||||
return formatter.string(from: date).uppercased()
|
||||
}
|
||||
|
||||
/// The curved label that accompanies a circular or corner complication:
|
||||
/// time plus title, or a plain "no events".
|
||||
///
|
||||
/// On a corner this follows the screen edge. On a face with a bezel slot —
|
||||
/// Infograph — the same label arcs across the top, where the tick marks
|
||||
/// otherwise are. That is the position ClockKit called `graphicBezel`, and
|
||||
/// in WidgetKit it is reached by attaching `.widgetLabel` to an
|
||||
/// `accessoryCircular` widget.
|
||||
static func eventLabel(_ event: SnapshotEvent?, at now: Date, language: String) -> String {
|
||||
guard let event else { return WatchL10n.t("watch.no_events", language) }
|
||||
if event.isAllDay { return event.title }
|
||||
return "\(time(event.start, language: language)) \(event.title)"
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
import Foundation
|
||||
import CalendarrCore
|
||||
|
||||
/// Which calendars this watch hides, on top of what the phone already filtered.
|
||||
///
|
||||
/// The snapshot arrives with hidden and banished calendars already removed, so
|
||||
/// this narrows further and can never contradict the phone. It lives in the App
|
||||
/// Group so one toggle in the app affects every complication at once.
|
||||
enum WatchFilterStore {
|
||||
private static let key = "watchHiddenCalendarKeys"
|
||||
|
||||
private static var defaults: UserDefaults? {
|
||||
UserDefaults(suiteName: CalendarrAppGroup.current)
|
||||
}
|
||||
|
||||
static func load() -> Set<String> {
|
||||
Set(defaults?.stringArray(forKey: key) ?? [])
|
||||
}
|
||||
|
||||
static func save(_ keys: Set<String>) {
|
||||
defaults?.set(keys.sorted(), forKey: key)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,57 @@
|
||||
import Foundation
|
||||
|
||||
/// German and English strings for the watch app and its complications.
|
||||
///
|
||||
/// A local copy rather than a shared one: `CalendarrCore` is deliberately
|
||||
/// Foundation-only and holds no presentation concerns, and the iOS `WidgetL10n`
|
||||
/// belongs to a target the watch cannot link against.
|
||||
enum WatchL10n {
|
||||
static func t(_ key: String, _ stored: String) -> String {
|
||||
strings[resolve(stored)]?[key] ?? strings["en"]?[key] ?? key
|
||||
}
|
||||
|
||||
static func locale(_ stored: String) -> Locale {
|
||||
Locale(identifier: resolve(stored) == "de" ? "de_DE" : "en_US")
|
||||
}
|
||||
|
||||
private static func resolve(_ stored: String) -> String {
|
||||
if stored == "de" || stored == "en" { return stored }
|
||||
let preferred = Locale.preferredLanguages.first ?? "en"
|
||||
return preferred.lowercased().hasPrefix("de") ? "de" : "en"
|
||||
}
|
||||
|
||||
private static let strings: [String: [String: String]] = [
|
||||
"de": [
|
||||
"watch.title": "Calendarr",
|
||||
"watch.calendars": "Kalender",
|
||||
"watch.allday": "Ganztägig",
|
||||
"watch.no_events": "Keine Termine",
|
||||
"watch.now": "jetzt",
|
||||
"watch.today": "Heute",
|
||||
"watch.tomorrow": "Morgen",
|
||||
"watch.events_today": "Termine",
|
||||
"watch.sign_in": "Auf dem iPhone anmelden",
|
||||
"watch.update": "Watch-App aktualisieren",
|
||||
"watch.waiting": "Warte auf Daten vom iPhone",
|
||||
"watch.unreadable": "Daten nicht lesbar",
|
||||
"watch.as_of": "Stand",
|
||||
"watch.no_information": "Keine Daten nach",
|
||||
],
|
||||
"en": [
|
||||
"watch.title": "Calendarr",
|
||||
"watch.calendars": "Calendars",
|
||||
"watch.allday": "All-day",
|
||||
"watch.no_events": "No events",
|
||||
"watch.now": "now",
|
||||
"watch.today": "Today",
|
||||
"watch.tomorrow": "Tomorrow",
|
||||
"watch.events_today": "events",
|
||||
"watch.sign_in": "Sign in on your iPhone",
|
||||
"watch.update": "Update the watch app",
|
||||
"watch.waiting": "Waiting for data from your iPhone",
|
||||
"watch.unreadable": "Data unreadable",
|
||||
"watch.as_of": "As of",
|
||||
"watch.no_information": "No data after",
|
||||
],
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
//
|
||||
// CalendarrWatch_Watch_AppTests.swift
|
||||
// CalendarrWatch Watch AppTests
|
||||
//
|
||||
// Created by Guido Schmit on 11.09.2026.
|
||||
//
|
||||
|
||||
import Testing
|
||||
@testable import CalendarrWatch_Watch_App
|
||||
|
||||
struct CalendarrWatch_Watch_AppTests {
|
||||
|
||||
@Test func example() async throws {
|
||||
// Write your test here and use APIs like `#expect(...)` to check expected conditions.
|
||||
// Swift Testing Documentation
|
||||
// https://developer.apple.com/documentation/testing
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,43 @@
|
||||
//
|
||||
// CalendarrWatch_Watch_AppUITests.swift
|
||||
// CalendarrWatch Watch AppUITests
|
||||
//
|
||||
// Created by Guido Schmit on 11.09.2026.
|
||||
//
|
||||
|
||||
import XCTest
|
||||
|
||||
final class CalendarrWatch_Watch_AppUITests: XCTestCase {
|
||||
|
||||
override func setUpWithError() throws {
|
||||
// Put setup code here. This method is called before the invocation of each test method in the class.
|
||||
|
||||
// In UI tests it is usually best to stop immediately when a failure occurs.
|
||||
continueAfterFailure = false
|
||||
|
||||
// In UI tests it’s important to set the initial state - such as interface orientation - required for your tests before they run. The setUp method is a good place to do this.
|
||||
}
|
||||
|
||||
override func tearDownWithError() throws {
|
||||
// Put teardown code here. This method is called after the invocation of each test method in the class.
|
||||
}
|
||||
|
||||
@MainActor
|
||||
func testExample() throws {
|
||||
// UI tests must launch the application that they test.
|
||||
let app = XCUIApplication()
|
||||
app.launch()
|
||||
|
||||
// Use XCTAssert and related functions to verify your tests produce the correct results.
|
||||
// XCUIAutomation Documentation
|
||||
// https://developer.apple.com/documentation/xcuiautomation
|
||||
}
|
||||
|
||||
@MainActor
|
||||
func testLaunchPerformance() throws {
|
||||
// This measures how long it takes to launch your application.
|
||||
measure(metrics: [XCTApplicationLaunchMetric()]) {
|
||||
XCUIApplication().launch()
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -2,6 +2,8 @@
|
||||
|
||||
> **For agentic workers:** REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (`- [ ]`) syntax for tracking.
|
||||
|
||||
**Status:** All 17 tasks implemented and build-verified as of 2026-09-11. On-device behaviour is still unverified — see the handoff list in Task 17.
|
||||
|
||||
**Goal:** Ship a read-only Apple Watch app plus eight watch-face complications that show the next Calendarr appointment, fed from the iPhone over WatchConnectivity.
|
||||
|
||||
**Architecture:** The watch is a read-only consumer of the phone's snapshot. Every piece of logic that can be tested lives in `CalendarrCore` (testable with `swift test` on the dev machine); the watch targets hold only thin SwiftUI and WCSession glue, which can only be build-verified here. The phone trims its 42-day snapshot to a 14-day watch payload and sends it; the watch writes it into its *own* App Group container through the same `SnapshotStore`, so there is one wire format and one reader.
|
||||
@@ -26,6 +28,12 @@ Xcode 26.6 ships the watchOS 26.5 SDK. The target device runs watchOS 27 beta, s
|
||||
|
||||
## Working rules for this repo
|
||||
|
||||
- **Never pass `-sdk` to xcodebuild.** It overrides SDKROOT for every target in
|
||||
the dependency graph, so the watch app gets built against the iOS SDK: the
|
||||
build still succeeds, but `WKApplication` is missing from the result and
|
||||
`DTSDKName` reads `iphonesimulator`. Select the platform with `-destination`
|
||||
alone and let each target keep its own SDK.
|
||||
|
||||
- Never run or start the server, and never run the apps. Apps are **build-verified only**; behaviour, layout and complication refresh are verified by the user on the device.
|
||||
- Commit messages in English. Commit after every task.
|
||||
- `CalendarrKit` is a **separate git repo** at `../CalendarrKit` with **no remote** — commit there, do not push. The iOS repo (`.`) pushes to `origin main`.
|
||||
@@ -509,7 +517,7 @@ import CalendarrCore
|
||||
- [ ] **Step 6: Build the iOS app to verify the adoption compiles**
|
||||
|
||||
```bash
|
||||
xcodebuild -project "Calendarr iOS.xcodeproj" -scheme "Calendarr iOS" -sdk iphonesimulator -destination 'generic/platform=iOS Simulator' CODE_SIGNING_ALLOWED=NO build
|
||||
xcodebuild -project "Calendarr iOS.xcodeproj" -scheme "Calendarr iOS" -destination 'generic/platform=iOS Simulator' CODE_SIGNING_ALLOWED=NO build
|
||||
```
|
||||
|
||||
Expected: `** BUILD SUCCEEDED **`.
|
||||
@@ -1529,7 +1537,7 @@ In `Calendarr iOS/CalendarrApp.swift`, in `AppState.logout()`, after the existin
|
||||
- [ ] **Step 5: Build the iOS app**
|
||||
|
||||
```bash
|
||||
xcodebuild -project "Calendarr iOS.xcodeproj" -scheme "Calendarr iOS" -sdk iphonesimulator -destination 'generic/platform=iOS Simulator' CODE_SIGNING_ALLOWED=NO build
|
||||
xcodebuild -project "Calendarr iOS.xcodeproj" -scheme "Calendarr iOS" -destination 'generic/platform=iOS Simulator' CODE_SIGNING_ALLOWED=NO build
|
||||
```
|
||||
|
||||
Expected: `** BUILD SUCCEEDED **`.
|
||||
@@ -1615,7 +1623,7 @@ For both new bundle IDs, enable the **App Groups** capability and select `group.
|
||||
- [ ] **Step 6: Verify both targets build**
|
||||
|
||||
```bash
|
||||
xcodebuild -project "Calendarr iOS.xcodeproj" -scheme CalendarrWatch -sdk watchsimulator -destination 'generic/platform=watchOS Simulator' CODE_SIGNING_ALLOWED=NO build
|
||||
xcodebuild -project "Calendarr iOS.xcodeproj" -scheme "Calendarr iOS" -destination 'generic/platform=iOS Simulator' CODE_SIGNING_ALLOWED=NO build
|
||||
```
|
||||
|
||||
Expected: `** BUILD SUCCEEDED **` (an empty app at this point).
|
||||
@@ -2349,7 +2357,7 @@ struct WatchCalendarFilterView: View {
|
||||
- [ ] **Step 2: Build the watch app**
|
||||
|
||||
```bash
|
||||
xcodebuild -project "Calendarr iOS.xcodeproj" -scheme CalendarrWatch -sdk watchsimulator -destination 'generic/platform=watchOS Simulator' CODE_SIGNING_ALLOWED=NO build
|
||||
xcodebuild -project "Calendarr iOS.xcodeproj" -scheme "Calendarr iOS" -destination 'generic/platform=iOS Simulator' CODE_SIGNING_ALLOWED=NO build
|
||||
```
|
||||
|
||||
Expected: `** BUILD SUCCEEDED **`.
|
||||
@@ -3012,7 +3020,7 @@ struct WatchDatePlusEventWidget: Widget {
|
||||
- [ ] **Step 5: Build the complications target**
|
||||
|
||||
```bash
|
||||
xcodebuild -project "Calendarr iOS.xcodeproj" -scheme CalendarrWatch -sdk watchsimulator -destination 'generic/platform=watchOS Simulator' CODE_SIGNING_ALLOWED=NO build
|
||||
xcodebuild -project "Calendarr iOS.xcodeproj" -scheme "Calendarr iOS" -destination 'generic/platform=iOS Simulator' CODE_SIGNING_ALLOWED=NO build
|
||||
```
|
||||
|
||||
Expected: `** BUILD SUCCEEDED **` — the scheme builds the embedded extension too.
|
||||
@@ -3046,8 +3054,8 @@ Expected: build succeeds against the native macOS SDK, every test passes.
|
||||
|
||||
```bash
|
||||
cd "../Calendarr iOS"
|
||||
xcodebuild -project "Calendarr iOS.xcodeproj" -scheme "Calendarr iOS" -sdk iphonesimulator -destination 'generic/platform=iOS Simulator' CODE_SIGNING_ALLOWED=NO build
|
||||
xcodebuild -project "Calendarr iOS.xcodeproj" -scheme CalendarrWatch -sdk watchsimulator -destination 'generic/platform=watchOS Simulator' CODE_SIGNING_ALLOWED=NO build
|
||||
xcodebuild -project "Calendarr iOS.xcodeproj" -scheme "Calendarr iOS" -destination 'generic/platform=iOS Simulator' CODE_SIGNING_ALLOWED=NO build
|
||||
xcodebuild -project "Calendarr iOS.xcodeproj" -scheme "Calendarr iOS" -destination 'generic/platform=iOS Simulator' CODE_SIGNING_ALLOWED=NO build
|
||||
```
|
||||
|
||||
Expected: `** BUILD SUCCEEDED **` twice.
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
# Apple Watch app and watch-face complications
|
||||
|
||||
Date: 2026-09-11
|
||||
Status: approved — ready for implementation planning
|
||||
Status: implemented — pending on-device verification
|
||||
|
||||
## Goal
|
||||
|
||||
|
||||
Reference in New Issue
Block a user