Rebuild the app around recurring screen-time blocking rules modeled on Opal's My Apps tab: - Onboarding with FamilyControls Screen Time authorization - Apps home with Blocked Apps tiles and live rule cards - New Rule sheet: Schedule/Time Limit/Open Limit types + preset gallery - Rule editors: time windows (incl. overnight), day picker, app selection via FamilyActivityPicker (Block/Allow Only), rename, Hold to Commit - Hard Mode: active hard rules cannot be edited, disabled, deleted, or unblocked until their window ends; soft rules pause until next window - Shield enforcement through per-rule ManagedSettingsStore + RuleEnforcer - 73 unit tests (Swift Testing) + 16 UI tests (XCUITest) with launch-arg harness for in-memory storage, mocked authorization, seeded scenarios - docs/RULES_FEATURE_SPEC.md: spec derived from the reference recording Known gap: background window transitions and time/open-limit thresholds need a DeviceActivityMonitor extension; shields currently sync while the app is running. Co-Authored-By: Claude <noreply@anthropic.com>
46 lines
1.7 KiB
Swift
46 lines
1.7 KiB
Swift
//
|
|
// LaunchConfiguration.swift
|
|
// Severed
|
|
//
|
|
|
|
import Foundation
|
|
|
|
/// Launch-argument configuration used by UI tests: in-memory storage, mocked
|
|
/// Screen Time authorization, forced onboarding state, and seeded scenarios.
|
|
struct LaunchConfiguration: Equatable {
|
|
enum SeedScenario: String {
|
|
/// One actively blocking rule ("Work Time") and one upcoming rule ("Sleep").
|
|
case standard
|
|
/// An actively blocking Hard Mode rule ("Locked In") plus an upcoming rule.
|
|
case hardModeActive = "hard-mode-active"
|
|
}
|
|
|
|
var isUITesting = false
|
|
/// Forces the onboarding-completed flag at launch. Nil leaves stored state alone.
|
|
var onboardingCompleted: Bool?
|
|
var seedScenario: SeedScenario?
|
|
|
|
static let uiTestingFlag = "-ui-testing"
|
|
static let onboardingCompletedFlag = "-onboarding-completed"
|
|
static let onboardingRequiredFlag = "-onboarding-required"
|
|
static let seedScenarioPrefix = "-seed-scenario="
|
|
|
|
static func parse(arguments: [String]) -> LaunchConfiguration {
|
|
var config = LaunchConfiguration()
|
|
config.isUITesting = arguments.contains(uiTestingFlag)
|
|
if arguments.contains(onboardingCompletedFlag) {
|
|
config.onboardingCompleted = true
|
|
} else if arguments.contains(onboardingRequiredFlag) {
|
|
config.onboardingCompleted = false
|
|
}
|
|
if let seedArgument = arguments.first(where: { $0.hasPrefix(seedScenarioPrefix) }) {
|
|
config.seedScenario = SeedScenario(
|
|
rawValue: String(seedArgument.dropFirst(seedScenarioPrefix.count))
|
|
)
|
|
}
|
|
return config
|
|
}
|
|
|
|
static let current = parse(arguments: ProcessInfo.processInfo.arguments)
|
|
}
|