Add launchd-based system scheduling and UI support for system-scheduled timers - #2
Conversation
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 80e6b627ae
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
| guard let home = FileManager.default.urls(for: .libraryDirectory, in: .userDomainMask).first else { | ||
| throw SchedulerError.unsupportedHomeDirectory | ||
| } | ||
| return home.appendingPathComponent("LaunchAgents", isDirectory: true) |
There was a problem hiding this comment.
Use the real per-user LaunchAgents directory
In sandboxed builds (the app entitlement enables App Sandbox), this resolves the user's Library to the app container rather than /Users/.../Library, so the plist is written under Containers/.../Data/Library/LaunchAgents. launchctl bootstrap can load that path for the current GUI session, but after logout/reboot launchd will not auto-load it from the real ~/Library/LaunchAgents; reconcileSystemScheduleState() will still see the file and mark the timer active, so repeating timers silently stop until manually restarted. Use the actual home LaunchAgents directory or re-bootstrap on reconciliation.
Useful? React with 👍 / 👎.
| print("launchd scheduling failed; falling back to in-app timer: \(error)") | ||
| isSystemScheduleEnabled = false | ||
| systemScheduleStartDate = nil |
There was a problem hiding this comment.
Clean up partial LaunchAgent installs before fallback
If launchctl fails after the plist has been written (or after bootstrap succeeded but enable failed), this catch only clears the flags and falls through to the in-app timer. The partially installed plist/job is left behind, so it can fire at next login or immediately in parallel with the fallback, causing duplicate or unrequested launches. Call uninstall/bootout before falling back.
Useful? React with 👍 / 👎.
Motivation
launchdhandles recurring launches.Timerwhenlaunchdinstallation fails and support correct countdown visuals for system-scheduled timers.Description
LaunchAgentSchedulerutility withinstall,uninstall,isInstalled,label, and helper methods that write a plist into~/Library/LaunchAgentsand calllaunchctlto bootstrap/enable jobs.TimerItemwithisSystemScheduleEnabledandsystemScheduleStartDate, integrateLaunchAgentSchedulerinstartTimer/stopTimer, addreconcileSystemScheduleState,nextSystemFireDate, and updatecalcProgressto compute progress for system-scheduled timers without an in-appTimer.timer.reconcileSystemScheduleState()fromTimerItemList, show a "Scheduled by macOS launchd" caption and passrepeatsintoCountdownViewinTimerSummaryView, and add a clock ticker to advancenextFireDatefor system-scheduled timers; changeCountdownViewto acceptrepeatsand compute wrap-around remaining time viaremainingTime(until:from:).README.mddescribing thelaunchdbehavior and when the app falls back to in-app timers.Testing
Codex Task