Add launchd-based system scheduling and UI support for system-scheduled timers - #3
Conversation
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 3dce67bafe
ℹ️ 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 a real per-user LaunchAgents location
With AutoRun.entitlements enabling the App Sandbox, FileManager.default.urls(for: .libraryDirectory, in: .userDomainMask) resolves inside the app container rather than the user's ~/Library. That means these plists are written under the container's Library/LaunchAgents, so after a logout/reboot launchd will not auto-load them from the standard per-user LaunchAgents directory, while reconcileSystemScheduleState() can still mark the timer active just because the container plist exists; users then see an active system-scheduled timer that never fires.
Useful? React with 👍 / 👎.
| isSystemScheduleEnabled = true | ||
| nextFireDate = nextSystemFireDate() | ||
| isActive = true | ||
| return true |
There was a problem hiding this comment.
Clear stale in-app timers after launchd install
When a timer previously used the in-app path (for example a one-shot timer or a launchd fallback), stopTimer() only invalidates the Timer and leaves the non-nil object in timer. This success path returns without clearing it, but isSystemScheduled requires timer == nil, so the UI will not show the launchd state and the clock handler will not advance nextFireDate even though the LaunchAgent was installed.
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