- RuleUsage + UsageLedger: per-rule, per-day minutes/opens in app-group defaults (monotonic minutes, incrementing opens, midnight rollover by day-keying); MockUsageLedger for tests and seeded UI scenarios - usage-aware status: a limit rule whose daily budget is spent on an enabled day is active (blocking) until next midnight; Hard Mode gating and app-list locking honor it; soft unblock pauses until midnight - RuleEnforcer shields spent limit rules (always Block mode) while the app runs - new Usage section under Blocked Apps: '18m of 45m used today · 27m left', '2 of 5 opens today · 3 opens left', 'Blocked until tomorrow' - new 'limits' seed scenario + UI tests
65 lines
2.1 KiB
Swift
65 lines
2.1 KiB
Swift
//
|
|
// OpenAppLockApp.swift
|
|
// OpenAppLock
|
|
//
|
|
// Created by Brendan Chen on 2025.08.09.
|
|
//
|
|
|
|
import SwiftData
|
|
import SwiftUI
|
|
|
|
@main
|
|
struct OpenAppLockApp: 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, AppList.self])
|
|
let modelConfiguration = ModelConfiguration(
|
|
schema: schema, isStoredInMemoryOnly: config.isUITesting
|
|
)
|
|
do {
|
|
container = try ModelContainer(for: schema, configurations: [modelConfiguration])
|
|
} catch {
|
|
fatalError("Could not create ModelContainer: \(error)")
|
|
}
|
|
|
|
let usageLedger: UsageReading = config.isUITesting ? MockUsageLedger() : UsageLedger()
|
|
|
|
if let scenario = config.seedScenario {
|
|
SampleRules.seed(
|
|
scenario, into: container.mainContext, usage: usageLedger as? MockUsageLedger
|
|
)
|
|
}
|
|
AppListMigration.run(in: 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, usage: usageLedger))
|
|
}
|
|
|
|
var body: some Scene {
|
|
WindowGroup {
|
|
RootView()
|
|
.environment(authorization)
|
|
.environment(enforcer)
|
|
}
|
|
.modelContainer(container)
|
|
}
|
|
}
|