Replace the Opal-style custom presentation with the bare iOS design language, keeping the backend, flows, and accessibility identifiers: - Home: NavigationStack + List; rules now presented as list rows with kind icon, block summary, and trailing live status; '+' in the toolbar - New Rule: plain list of rule types and presets; editor still pushed via navigationDestination - Editor: native Form (DatePicker rows, day circles with summary in the section header, toggle rows with footers, stepper rows); create commits with a prominent 'Add Rule' button replacing Hold to Commit; edit uses toolbar Done plus red Disable/Delete rows - Detail: sheet with inline title + status caption, LabeledContent rows; Edit Rule pushes the editor natively - Default color scheme: drop forced dark mode and custom tint; system appearance and accent throughout - Delete Theme, HoldToCommitButton, RuleCardView and custom chrome - Use concrete Color.primary/secondary in button row labels (the hierarchical styles resolve against the button tint) - Tests: 93 passing; only delta is holdToCommitButton press → commitRuleButton tap in 5 call sites - Spec: add §6 mapping the original presentation to the native one
60 lines
1.8 KiB
Swift
60 lines
1.8 KiB
Swift
//
|
|
// SeveredApp.swift
|
|
// Severed
|
|
//
|
|
// Created by Brendan Chen on 2025.08.09.
|
|
//
|
|
|
|
import SwiftData
|
|
import SwiftUI
|
|
|
|
@main
|
|
struct SeveredApp: App {
|
|
private let container: ModelContainer
|
|
@State private var authorization: ScreenTimeAuthorization
|
|
@State private var enforcer: RuleEnforcer
|
|
|
|
init() {
|
|
let config = LaunchConfiguration.current
|
|
|
|
if let onboardingCompleted = config.onboardingCompleted {
|
|
UserDefaults.standard.set(onboardingCompleted, forKey: "hasCompletedOnboarding")
|
|
}
|
|
|
|
let schema = Schema([BlockingRule.self])
|
|
let modelConfiguration = ModelConfiguration(
|
|
schema: schema, isStoredInMemoryOnly: config.isUITesting
|
|
)
|
|
do {
|
|
container = try ModelContainer(for: schema, configurations: [modelConfiguration])
|
|
} catch {
|
|
fatalError("Could not create ModelContainer: \(error)")
|
|
}
|
|
|
|
if let scenario = config.seedScenario {
|
|
SampleRules.seed(scenario, into: container.mainContext)
|
|
}
|
|
|
|
let authProvider: AuthorizationProviding =
|
|
config.isUITesting
|
|
? MockAuthorizationProvider(
|
|
status: config.onboardingCompleted == false ? .notDetermined : .approved
|
|
)
|
|
: FamilyControlsAuthorizationProvider()
|
|
_authorization = State(initialValue: ScreenTimeAuthorization(provider: authProvider))
|
|
|
|
let shields: ShieldApplying =
|
|
config.isUITesting ? MockShieldController() : ManagedSettingsShieldController()
|
|
_enforcer = State(initialValue: RuleEnforcer(shields: shields))
|
|
}
|
|
|
|
var body: some Scene {
|
|
WindowGroup {
|
|
RootView()
|
|
.environment(authorization)
|
|
.environment(enforcer)
|
|
}
|
|
.modelContainer(container)
|
|
}
|
|
}
|