refactor: model rule options as a per-kind sum type
Replace the wide BlockingRule/RuleDraft "god struct" — where every option existed for every kind — with a RuleConfiguration sum type that carries only the options each kind actually has: .schedule(ScheduleConfig: window + selectionMode + blockAdultContent) .timeLimit(TimeLimitConfig: dailyLimitMinutes) .openLimit(OpenLimitConfig: maxOpens) Name, days, Hard Mode, app list, and pause stay common to all kinds. This makes illegal states unrepresentable: Block / Allow Only and Block Adult Content are now structurally Schedule-only. User-visible behavior change: the Time Limit and Open Limit editors no longer offer a Block Adult Content toggle, and their detail sheets drop the "Adult websites" row — those never made sense for a usage budget. Limit rules are always Block and never engage the web-content filter. BlockingRule keeps flat columns as raw persistence behind a computed `configuration` bridge (lowest SwiftData risk; the cross-process RuleSnapshot wire format and the Screen Time extensions are untouched). Logic, the editors, and the detail sheet all switch on the sum type. Spec (§1, §3.5/§3.6, §5.2) updated first; tests reworked to the new API with new structural-guarantee unit tests and a UI test asserting the Time Limit editor/detail omit adult content. Full suite green (180 tests). Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
@@ -13,6 +13,20 @@
|
||||
/* End PBXBuildFile section */
|
||||
|
||||
/* Begin PBXContainerItemProxy section */
|
||||
20A7EDDE2E47B7CF0097608D /* PBXContainerItemProxy */ = {
|
||||
isa = PBXContainerItemProxy;
|
||||
containerPortal = 20A7EDC62E47B7CD0097608D /* Project object */;
|
||||
proxyType = 1;
|
||||
remoteGlobalIDString = 20A7EDCD2E47B7CD0097608D;
|
||||
remoteInfo = OpenAppLock;
|
||||
};
|
||||
20A7EDE82E47B7CF0097608D /* PBXContainerItemProxy */ = {
|
||||
isa = PBXContainerItemProxy;
|
||||
containerPortal = 20A7EDC62E47B7CD0097608D /* Project object */;
|
||||
proxyType = 1;
|
||||
remoteGlobalIDString = 20A7EDCD2E47B7CD0097608D;
|
||||
remoteInfo = OpenAppLock;
|
||||
};
|
||||
E20000000000000000000001 /* PBXContainerItemProxy */ = {
|
||||
isa = PBXContainerItemProxy;
|
||||
containerPortal = 20A7EDC62E47B7CD0097608D /* Project object */;
|
||||
@@ -34,22 +48,24 @@
|
||||
remoteGlobalIDString = C10000000000000000000003;
|
||||
remoteInfo = OpenAppLockShieldAction;
|
||||
};
|
||||
20A7EDDE2E47B7CF0097608D /* PBXContainerItemProxy */ = {
|
||||
isa = PBXContainerItemProxy;
|
||||
containerPortal = 20A7EDC62E47B7CD0097608D /* Project object */;
|
||||
proxyType = 1;
|
||||
remoteGlobalIDString = 20A7EDCD2E47B7CD0097608D;
|
||||
remoteInfo = OpenAppLock;
|
||||
};
|
||||
20A7EDE82E47B7CF0097608D /* PBXContainerItemProxy */ = {
|
||||
isa = PBXContainerItemProxy;
|
||||
containerPortal = 20A7EDC62E47B7CD0097608D /* Project object */;
|
||||
proxyType = 1;
|
||||
remoteGlobalIDString = 20A7EDCD2E47B7CD0097608D;
|
||||
remoteInfo = OpenAppLock;
|
||||
};
|
||||
/* End PBXContainerItemProxy section */
|
||||
|
||||
/* Begin PBXCopyFilesBuildPhase section */
|
||||
E10000000000000000000001 /* Embed Foundation Extensions */ = {
|
||||
isa = PBXCopyFilesBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
dstPath = "";
|
||||
dstSubfolderSpec = 13;
|
||||
files = (
|
||||
F10000000000000000000001 /* OpenAppLockMonitor.appex in Embed Foundation Extensions */,
|
||||
F10000000000000000000002 /* OpenAppLockShieldConfig.appex in Embed Foundation Extensions */,
|
||||
F10000000000000000000003 /* OpenAppLockShieldAction.appex in Embed Foundation Extensions */,
|
||||
);
|
||||
name = "Embed Foundation Extensions";
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
/* End PBXCopyFilesBuildPhase section */
|
||||
|
||||
/* Begin PBXFileReference section */
|
||||
20A7EDCE2E47B7CD0097608D /* OpenAppLock.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = OpenAppLock.app; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||
20A7EDDD2E47B7CF0097608D /* OpenAppLockTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = OpenAppLockTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||
@@ -59,6 +75,30 @@
|
||||
B10000000000000000000003 /* OpenAppLockShieldAction.appex */ = {isa = PBXFileReference; explicitFileType = "wrapper.app-extension"; includeInIndex = 0; path = OpenAppLockShieldAction.appex; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||
/* End PBXFileReference section */
|
||||
|
||||
/* Begin PBXFileSystemSynchronizedBuildFileExceptionSet section */
|
||||
AB00000000000000000000B1 /* Exceptions for "OpenAppLockMonitor" folder in "OpenAppLockMonitor" target */ = {
|
||||
isa = PBXFileSystemSynchronizedBuildFileExceptionSet;
|
||||
membershipExceptions = (
|
||||
Info.plist,
|
||||
);
|
||||
target = C10000000000000000000001 /* OpenAppLockMonitor */;
|
||||
};
|
||||
AB00000000000000000000B2 /* Exceptions for "OpenAppLockShieldConfig" folder in "OpenAppLockShieldConfig" target */ = {
|
||||
isa = PBXFileSystemSynchronizedBuildFileExceptionSet;
|
||||
membershipExceptions = (
|
||||
Info.plist,
|
||||
);
|
||||
target = C10000000000000000000002 /* OpenAppLockShieldConfig */;
|
||||
};
|
||||
AB00000000000000000000B3 /* Exceptions for "OpenAppLockShieldAction" folder in "OpenAppLockShieldAction" target */ = {
|
||||
isa = PBXFileSystemSynchronizedBuildFileExceptionSet;
|
||||
membershipExceptions = (
|
||||
Info.plist,
|
||||
);
|
||||
target = C10000000000000000000003 /* OpenAppLockShieldAction */;
|
||||
};
|
||||
/* End PBXFileSystemSynchronizedBuildFileExceptionSet section */
|
||||
|
||||
/* Begin PBXFileSystemSynchronizedRootGroup section */
|
||||
20A7EDD02E47B7CD0097608D /* OpenAppLock */ = {
|
||||
isa = PBXFileSystemSynchronizedRootGroup;
|
||||
@@ -106,46 +146,6 @@
|
||||
};
|
||||
/* End PBXFileSystemSynchronizedRootGroup section */
|
||||
|
||||
/* Begin PBXFileSystemSynchronizedBuildFileExceptionSet section */
|
||||
AB00000000000000000000B1 /* Exceptions for "OpenAppLockMonitor" folder in "OpenAppLockMonitor" target */ = {
|
||||
isa = PBXFileSystemSynchronizedBuildFileExceptionSet;
|
||||
membershipExceptions = (
|
||||
Info.plist,
|
||||
);
|
||||
target = C10000000000000000000001 /* OpenAppLockMonitor */;
|
||||
};
|
||||
AB00000000000000000000B2 /* Exceptions for "OpenAppLockShieldConfig" folder in "OpenAppLockShieldConfig" target */ = {
|
||||
isa = PBXFileSystemSynchronizedBuildFileExceptionSet;
|
||||
membershipExceptions = (
|
||||
Info.plist,
|
||||
);
|
||||
target = C10000000000000000000002 /* OpenAppLockShieldConfig */;
|
||||
};
|
||||
AB00000000000000000000B3 /* Exceptions for "OpenAppLockShieldAction" folder in "OpenAppLockShieldAction" target */ = {
|
||||
isa = PBXFileSystemSynchronizedBuildFileExceptionSet;
|
||||
membershipExceptions = (
|
||||
Info.plist,
|
||||
);
|
||||
target = C10000000000000000000003 /* OpenAppLockShieldAction */;
|
||||
};
|
||||
/* End PBXFileSystemSynchronizedBuildFileExceptionSet section */
|
||||
|
||||
/* Begin PBXCopyFilesBuildPhase section */
|
||||
E10000000000000000000001 /* Embed Foundation Extensions */ = {
|
||||
isa = PBXCopyFilesBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
dstPath = "";
|
||||
dstSubfolderSpec = 13;
|
||||
files = (
|
||||
F10000000000000000000001 /* OpenAppLockMonitor.appex in Embed Foundation Extensions */,
|
||||
F10000000000000000000002 /* OpenAppLockShieldConfig.appex in Embed Foundation Extensions */,
|
||||
F10000000000000000000003 /* OpenAppLockShieldAction.appex in Embed Foundation Extensions */,
|
||||
);
|
||||
name = "Embed Foundation Extensions";
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
/* End PBXCopyFilesBuildPhase section */
|
||||
|
||||
/* Begin PBXFrameworksBuildPhase section */
|
||||
20A7EDCB2E47B7CD0097608D /* Frameworks */ = {
|
||||
isa = PBXFrameworksBuildPhase;
|
||||
@@ -308,8 +308,8 @@
|
||||
dependencies = (
|
||||
);
|
||||
fileSystemSynchronizedGroups = (
|
||||
AA00000000000000000000A2 /* OpenAppLockMonitor */,
|
||||
AA00000000000000000000A1 /* Shared */,
|
||||
AA00000000000000000000A2 /* OpenAppLockMonitor */,
|
||||
);
|
||||
name = OpenAppLockMonitor;
|
||||
packageProductDependencies = (
|
||||
@@ -331,8 +331,8 @@
|
||||
dependencies = (
|
||||
);
|
||||
fileSystemSynchronizedGroups = (
|
||||
AA00000000000000000000A3 /* OpenAppLockShieldConfig */,
|
||||
AA00000000000000000000A1 /* Shared */,
|
||||
AA00000000000000000000A3 /* OpenAppLockShieldConfig */,
|
||||
);
|
||||
name = OpenAppLockShieldConfig;
|
||||
packageProductDependencies = (
|
||||
@@ -354,8 +354,8 @@
|
||||
dependencies = (
|
||||
);
|
||||
fileSystemSynchronizedGroups = (
|
||||
AA00000000000000000000A4 /* OpenAppLockShieldAction */,
|
||||
AA00000000000000000000A1 /* Shared */,
|
||||
AA00000000000000000000A4 /* OpenAppLockShieldAction */,
|
||||
);
|
||||
name = OpenAppLockShieldAction;
|
||||
packageProductDependencies = (
|
||||
|
||||
Reference in New Issue
Block a user