Conversation
Introduce the SwiftUI stopwatch, SwiftData models, App Intents, Live Activity extension, tests, and a macOS GitHub Actions workflow that generates the Xcode project and an unsigned IPA. Co-authored-by: Abdur Raheem Imami <arahe-dev@users.noreply.github.com>
Co-authored-by: Abdur Raheem Imami <arahe-dev@users.noreply.github.com>
Co-authored-by: Abdur Raheem Imami <arahe-dev@users.noreply.github.com>
Co-authored-by: Abdur Raheem Imami <arahe-dev@users.noreply.github.com>
Co-authored-by: Abdur Raheem Imami <arahe-dev@users.noreply.github.com>
Co-authored-by: Abdur Raheem Imami <arahe-dev@users.noreply.github.com>
Co-authored-by: Abdur Raheem Imami <arahe-dev@users.noreply.github.com>
Co-authored-by: Abdur Raheem Imami <arahe-dev@users.noreply.github.com>
Co-authored-by: Abdur Raheem Imami <arahe-dev@users.noreply.github.com>
Co-authored-by: Abdur Raheem Imami <arahe-dev@users.noreply.github.com>
Co-authored-by: Abdur Raheem Imami <arahe-dev@users.noreply.github.com>
Co-authored-by: Abdur Raheem Imami <arahe-dev@users.noreply.github.com>
Co-authored-by: Abdur Raheem Imami <arahe-dev@users.noreply.github.com>
Co-authored-by: Abdur Raheem Imami <arahe-dev@users.noreply.github.com>
Co-authored-by: Abdur Raheem Imami <arahe-dev@users.noreply.github.com>
Co-authored-by: Abdur Raheem Imami <arahe-dev@users.noreply.github.com>
There was a problem hiding this comment.
Pull request overview
Introduces the ProductivityTracker native SwiftUI iOS app and the supporting tooling/CI needed to build, test, screenshot, and package an unsigned device IPA via GitHub Actions (macOS 26), with a Windows helper path for downloading and sideload signing.
Changes:
- Added the SwiftUI + SwiftData app implementation (timer engine/state machine, Spaces/tasks/history, App Intents, notifications, Live Activity widget).
- Added XCTest + XCUITest coverage and deterministic screenshot capture for CI artifacts.
- Added macOS CI workflow scripts to generate the Xcode project with XcodeGen, select a simulator, run tests, package an unsigned IPA, and publish artifacts (plus a Windows
gh-based artifact fetch script).
Reviewed changes
Copilot reviewed 74 out of 80 changed files in this pull request and generated 5 comments.
Show a summary per file
| File | Description |
|---|---|
| Scripts/windows/fetch-ipa.ps1 | Downloads latest successful CI artifact IPA on Windows and verifies SHA-256 when present. |
| Scripts/ci/select-simulator.sh | Selects a preferred iPhone simulator (newest runtime + preferred model) for CI. |
| Scripts/ci/package-ipa.sh | Packages an unsigned .ipa from a built .app and emits SHA256SUMS + sanity checks. |
| Scripts/ci/install-xcodegen.sh | Installs pinned XcodeGen version for CI project generation. |
| README.md | Replaces repo README with app-focused documentation and workflow overview. |
| project.yml | XcodeGen spec defining app, widget extension, and test targets/schemes. |
| ProductivityTrackerUITests/ProductivityTrackerUITests.swift | UI tests for navigation, paging isolation, controls, persistence, and screenshots. |
| ProductivityTrackerTests/TimerEngineTests.swift | Unit tests for timer engine state transitions and formatter. |
| ProductivityTrackerTests/TaskIntervalTests.swift | SwiftData-backed tests for session/task interval behavior. |
| ProductivityTrackerTests/SpacePersistenceTests.swift | SwiftData-backed tests for Spaces CRUD, ordering, deletion safeguards, recovery. |
| ProductivityTrackerTests/NotificationAndImportTests.swift | Tests for distraction scheduling, JSON import validation, launch config. |
| ProductivityTrackerLiveActivity/ProductivityTrackerLiveActivityBundle.swift | Widget bundle entry point for Live Activity/Dynamic Island UI. |
| ProductivityTrackerLiveActivity/LiveActivityView/LiveActivityView.swift | Lock Screen Live Activity UI with App Intent controls and timer text. |
| ProductivityTrackerLiveActivity/Intents/LiveActivityIntents.swift | App Intents for pause/resume/stop/lap from the Live Activity. |
| ProductivityTrackerLiveActivity/Info.plist | Widget extension Info.plist for WidgetKit/Live Activities. |
| ProductivityTrackerLiveActivity/DynamicIsland/DynamicIslandViews.swift | Dynamic Island compact/minimal/expanded rendering. |
| ProductivityTrackerLiveActivity/ActivityAttributes/SessionActivityAttributes.swift | ActivityKit attributes/state shared between app and widget. |
| ProductivityTracker/Views/Timer/TimerScreen.swift | Main timer screen layout: upper pager + lower task panel + settings sheet. |
| ProductivityTracker/Views/Timer/TaskPickerSheet.swift | Sheet UI for selecting a task (long-press Lap). |
| ProductivityTracker/Views/Timer/StopwatchDisplay.swift | Timeline-driven stopwatch display with accessibility values. |
| ProductivityTracker/Views/Timer/StopwatchCard.swift | Glass “card” for stopwatch + controls + page dots per Space. |
| ProductivityTracker/Views/Timer/StopwatchButtons.swift | Start/Stop/Lap button cluster and long-press task picker trigger. |
| ProductivityTracker/Views/Timer/PageDots.swift | Page indicator dots with accessibility label. |
| ProductivityTracker/Views/Tasks/TaskRow.swift | Row view for a task showing name + elapsed. |
| ProductivityTracker/Views/Tasks/TaskPanel.swift | Task list panel (tap-to-select while running). |
| ProductivityTracker/Views/Spaces/SpaceListView.swift | Space management list (reorder/delete/create). |
| ProductivityTracker/Views/Spaces/SpaceEditorView.swift | Space editor for name, tint, enabled tasks, add/delete tasks. |
| ProductivityTracker/Views/Settings/SettingsView.swift | Settings UI including Spaces/History navigation and distraction threshold. |
| ProductivityTracker/Views/RootView.swift | Root bootstrap creating SessionController and presenting settings. |
| ProductivityTracker/Views/History/HistoryView.swift | Session history list with per-task grouped durations. |
| ProductivityTracker/Timer/TimeSource.swift | Time source abstraction + controllable clock for tests. |
| ProductivityTracker/Timer/TimerSnapshot.swift | Snapshot model for timestamp-based elapsed calculation. |
| ProductivityTracker/Timer/TimerEngine.swift | Pure timer state machine with explicit transitions. |
| ProductivityTracker/Timer/SessionController.swift | Main app controller integrating SwiftData, timer engine, live activity, notifications. |
| ProductivityTracker/Timer/ElapsedFormatter.swift | Stopwatch/compact elapsed string formatting utilities. |
| ProductivityTracker/Services/SettingsStore.swift | UserDefaults-backed settings (selected space, distraction timeout, appearance). |
| ProductivityTracker/Services/LiveActivityManager.swift | ActivityKit integration for starting/updating/ending the live activity. |
| ProductivityTracker/Services/Haptics.swift | Centralized haptic feedback helpers. |
| ProductivityTracker/Resources/PrivacyInfo.xcprivacy | App privacy manifest (tracking off + accessed API reasons). |
| ProductivityTracker/Resources/Info.plist | App Info.plist including Live Activities and orientations. |
| ProductivityTracker/Resources/Assets.xcassets/Contents.json | Asset catalog metadata. |
| ProductivityTracker/Resources/Assets.xcassets/AppIcon.appiconset/Contents.json | App icon asset entry. |
| ProductivityTracker/Resources/Assets.xcassets/AccentColor.colorset/Contents.json | Accent color definition. |
| ProductivityTracker/Persistence/PersistenceController.swift | SwiftData container construction (memory vs disk). |
| ProductivityTracker/Persistence/DemoDataSeeder.swift | Demo seed data + stable IDs for tests/UI. |
| ProductivityTracker/Notifications/NotificationService.swift | Local notification scheduling + action handling + recording test double. |
| ProductivityTracker/Notifications/DistractionMonitor.swift | Distraction tracking state for scheduling/cancelling reminders. |
| ProductivityTracker/Models/TimerPhase.swift | Timer phase enum. |
| ProductivityTracker/Models/TaskItem.swift | SwiftData model for tasks. |
| ProductivityTracker/Models/TaskInterval.swift | SwiftData model for per-task intervals within a session. |
| ProductivityTracker/Models/SpaceTint.swift | Tint enum + parsing for imports. |
| ProductivityTracker/Models/Space.swift | SwiftData model for Spaces and task ordering helpers. |
| ProductivityTracker/Models/Session.swift | SwiftData model for sessions + elapsed calculation. |
| ProductivityTracker/Intents/SpaceImportPayload.swift | JSON parsing/validation for Space import intent. |
| ProductivityTracker/Intents/SessionIntents.swift | Core app intents: select/start/stop/pause/resume/lap. |
| ProductivityTracker/Intents/ImportAndDistractionIntents.swift | App intents for task selection, import, and distraction start/end. |
| ProductivityTracker/Intents/FocusFilterIntent.swift | Focus Filter intent to select a Space when a Focus activates. |
| ProductivityTracker/Intents/Entities.swift | AppEntity + queries for Spaces and Tasks. |
| ProductivityTracker/Intents/AppShortcuts.swift | Suggested Shortcuts phrases for key intents. |
| ProductivityTracker/DesignSystem/StopwatchButtonStyle.swift | Custom glass stopwatch button component. |
| ProductivityTracker/DesignSystem/GlassSurface.swift | Shared “glass surface” container styling. |
| ProductivityTracker/DesignSystem/AppTheme.swift | Shared accessibility IDs and layout metrics. |
| ProductivityTracker/App/ProductivityTrackerApp.swift | App entry point + model container + launch configuration handling. |
| ProductivityTracker/App/LaunchConfiguration.swift | Launch argument/environment parsing for tests and screenshots. |
| ProductivityTracker/App/AppRuntime.swift | Shared runtime container/controller for intents and services. |
| INSTALL_WINDOWS.md | Windows sideload installation guide (free-signing flow). |
| Design/screenshots/README.md | Documents CI-produced simulator screenshots. |
| Design/references/space-import.schema.json | Example JSON payload for Space import. |
| Design/references/SPACE_IMPORT.md | Human-readable schema documentation for Space import. |
| Design/references/README.md | Notes about missing original concept images and reference usage. |
| ARCHITECTURE.md | High-level architecture and rationale for timer/timestamps/CI pipeline. |
| .xcodegen-version | Pins XcodeGen version for CI install script. |
| .gitignore | iOS/Swift/Xcode ignores + artifacts/output directories. |
| .github/workflows/ios-ci.yml | CI workflow for generation, build, tests, screenshots, IPA packaging, artifact upload. |
| .cursor/rules/productivity-tracker.mdc | Project constraints and workflow rules for Cursor/Windows-first development. |
💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.
Comment on lines
+28
to
+31
| init(center: UNUserNotificationCenter = .current()) { | ||
| self.center = center | ||
| super.init() | ||
| } |
Comment on lines
+14
to
+16
| #if !targetEnvironment(simulator) || true | ||
| guard ActivityAuthorizationInfo().areActivitiesEnabled else { return } | ||
| let snapshot = controller.snapshot |
Comment on lines
+35
to
+38
| let continueAction = UNNotificationAction( | ||
| identifier: NotificationIdentifiers.continueAction, | ||
| title: "Continue" | ||
| ) |
Comment on lines
+30
to
+34
| .simultaneousGesture( | ||
| LongPressGesture(minimumDuration: 0.55).onEnded { _ in | ||
| onLongPressLap() | ||
| } | ||
| ) |
Comment on lines
+333
to
+337
| private func activeSession() -> Session? { | ||
| guard let id = engine.snapshot.sessionID else { return nil } | ||
| let all = (try? context.fetch(FetchDescriptor<Session>())) ?? [] | ||
| return all.first(where: { $0.id == id }) | ||
| } |
Stop now freezes a resumable stopwatch; Start resumes the same session; Reset archives history and dismisses the Live Activity immediately. Each Space owns its own timer. The main screen drops tinted glass cards, tasks are directly editable, and Live Activity presentation is timer-first with explicit controls only. Co-authored-by: Abdur Raheem Imami <arahe-dev@users.noreply.github.com>
Widget code now builds ContentState from primitive fields so the extension compiles without app timer types. Co-authored-by: Abdur Raheem Imami <arahe-dev@users.noreply.github.com>
Co-authored-by: Abdur Raheem Imami <arahe-dev@users.noreply.github.com>
Co-authored-by: Abdur Raheem Imami <arahe-dev@users.noreply.github.com>
Co-authored-by: Abdur Raheem Imami <arahe-dev@users.noreply.github.com>
Publish timer snapshots before Live Activity refresh, apply a new default task while idle, and query timer controls through descendants so disabled Lap remains visible to XCTest. Co-authored-by: Abdur Raheem Imami <arahe-dev@users.noreply.github.com>
Co-authored-by: Abdur Raheem Imami <arahe-dev@users.noreply.github.com>
Co-authored-by: Abdur Raheem Imami <arahe-dev@users.noreply.github.com>
Co-authored-by: Abdur Raheem Imami <arahe-dev@users.noreply.github.com>
Co-authored-by: Abdur Raheem Imami <arahe-dev@users.noreply.github.com>
Co-authored-by: Abdur Raheem Imami <arahe-dev@users.noreply.github.com>
…y tree. Co-authored-by: Abdur Raheem Imami <arahe-dev@users.noreply.github.com>
Co-authored-by: Abdur Raheem Imami <arahe-dev@users.noreply.github.com>
Co-authored-by: Abdur Raheem Imami <arahe-dev@users.noreply.github.com>
Co-authored-by: Abdur Raheem Imami <arahe-dev@users.noreply.github.com>
Co-authored-by: Abdur Raheem Imami <arahe-dev@users.noreply.github.com>
Co-authored-by: Abdur Raheem Imami <arahe-dev@users.noreply.github.com>
Co-authored-by: Abdur Raheem Imami <arahe-dev@users.noreply.github.com>
Swipe past the last Space to create one in place, with color, SF Symbols, emoji, or a monogram. Tasks can be added inline and completed with a double-tap. The whole Space page pages together, the lock screen uses a Clock-style hh:mm:ss timer in the Space color with a large icon, and the home-screen app is Conduit with a new icon and IPA name. Co-authored-by: Abdur Raheem Imami <arahe-dev@users.noreply.github.com>
SwiftUI double-tap arbitration delays the single tap, and TabView paging is not instantaneous with animations disabled. Co-authored-by: Abdur Raheem Imami <arahe-dev@users.noreply.github.com>
Shrink icons and add lock-screen padding. Pause, resume, and close wait for ActivityKit so they feel immediate; close archives even while running. Tap the Space name to edit. The canvas tint fills the status bar. Paging uses a committed swipe to reach New Space, and leaving that page dismisses the keyboard. Task rows select immediately. A squircle save control stores elapsed time without resetting, with filtered history. Distraction reminders are removed from the UI. Co-authored-by: Abdur Raheem Imami <arahe-dev@users.noreply.github.com>
Avoid SwiftUI’s double-tap delay so row selection stays immediate. Co-authored-by: Abdur Raheem Imami <arahe-dev@users.noreply.github.com>
Drop the full-screen tint washes, page Spaces with native paging, and add exponential resistance only when pulling into New Space. Push ActivityKit updates before SwiftData saves and reuse the running clock anchor on pause. Co-authored-by: Abdur Raheem Imami <arahe-dev@users.noreply.github.com>
Co-authored-by: Abdur Raheem Imami <arahe-dev@users.noreply.github.com>
Rubber-band the New Space peek so it moves less than the finger, and keep an in-pager New Space page under UITesting so the last swipe still lands reliably. Co-authored-by: Abdur Raheem Imami <arahe-dev@users.noreply.github.com>
This is the green CI build from 6b1d7df. Sign it with your own Apple account before installing. Co-authored-by: Abdur Raheem Imami <arahe-dev@users.noreply.github.com>
Pause/resume now flips the system timer on the Live Activity before SwiftData work, skip lock-screen auth, and keep SessionController ready as soon as the process launches. Co-authored-by: Abdur Raheem Imami <arahe-dev@users.noreply.github.com>
Co-authored-by: Abdur Raheem Imami <arahe-dev@users.noreply.github.com>
Co-authored-by: Abdur Raheem Imami <arahe-dev@users.noreply.github.com>
WidgetKit can pre-render running and stopped controls so the glyph flips on tap, while perform() only sets pauseTime on the system timer then persists in the background. Co-authored-by: Abdur Raheem Imami <arahe-dev@users.noreply.github.com>
Co-authored-by: Abdur Raheem Imami <arahe-dev@users.noreply.github.com>
The Toggle intent was defaulting to running=false, so Stop worked and Start was a no-op. Decide the target from the current Live Activity state, pre-render both Toggle values, and actually resume the system timer plus the session. Co-authored-by: Abdur Raheem Imami <arahe-dev@users.noreply.github.com>
Co-authored-by: Abdur Raheem Imami <arahe-dev@users.noreply.github.com>
Stop and Start are dedicated Live Activity buttons, not a Toggle. Resume publishes a new timer view identity so the system clock actually runs again instead of keeping pauseTime. Co-authored-by: Abdur Raheem Imami <arahe-dev@users.noreply.github.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Pause then play on the lock screen was leaving the digits frozen.
Cause
Toggle/SetValueIntent. That can send “not running” on every tap, so Start never resumes.Text(timerInterval:pauseTime:)does not start counting again if you only clearpauseTimeon the same view. The system timer stays paused.Fix
StopFromLiveActivityIntent; Start isResumeFromLiveActivityIntent.pauseTime. Stopped digits usepauseTimeon a different view identity (elapsedClockID+ newdisplayStarton resume).Install the IPA from the latest green
ios-cirun on this branch (sign with your Apple account). The zip currently inartifacts/is the previous build and does not include this fix.