Context
What needed to change
PontoApp is a self-directed native app project for recording workday punch events from iPhone and Apple Watch. The public repository shows a focused time clock product surface: users can clock in, start a break, return from break, and clock out while keeping local history synchronized across devices. I analyzed the implementation from the GitHub repository, including the README, Xcode project, SwiftUI views, shared models, storage, and WatchConnectivity layer.
Constraints
What shaped the solution
- The app needed to work without an external server, so records had to be stored locally and moved between devices when connectivity was available.
- iPhone and Apple Watch had different interaction constraints, but both needed to use the same business rules for valid punch actions.
- Sync needed to handle deleted records, not only newly created records, otherwise old punches could reappear after a delayed device update.
- The app had to stay small enough for a first native build while still showing real product architecture.
- The current repository is an initial implementation, so the case study should not claim App Store release, production analytics, or commercial usage metrics.
Goals and non-goals
What success looked like
- Goal: create a useful time clock app that covers the complete daily flow: entry, break, return, and exit.
- Goal: keep iPhone and Apple Watch behavior consistent through shared models and store logic.
- Goal: make the UI contextual so users only see actions that are valid for the current shift state.
- Goal: preserve a local history with daily summaries and device source information.
- Goal: document the project clearly enough that another developer can inspect the architecture from GitHub.
- Non-goal: build payroll, team management, approvals, reporting dashboards, or backend accounts.
- Non-goal: present the app as a commercial product before validation, testing, and distribution work exist.
Approach
How the work was structured
The app is organized around a shared time-clock domain layer used by both targets. TimeClockStore owns the punch records, current shift status, daily summary, deletion markers, persistence, and sync calls. The iPhone target provides the fuller dashboard and history view, while the Watch target keeps the same actions compact enough for quick wrist-based use. This keeps product logic centralized while allowing each device interface to respect its own usage context.
Key decisions
Trade-offs that shaped the outcome
Shared domain model
- Option A
- Duplicate punch logic separately inside the iPhone and Watch screens.
- Option B
- Put punch records, shift state, summaries, persistence, and sync in shared Swift files.
- Chosen path
- Shared Swift domain layer.
- Reason
- The same time clock rules need to stay consistent across both devices, especially when sync can happen from either side.
Consequence
The app has less UI duplication and a clearer place to extend future rules like overtime, notes, or export.
Contextual punch actions
- Option A
- Always show every action and let the user choose any punch type.
- Option B
- Derive the next valid actions from the current shift status.
- Chosen path
- Status-driven action availability.
- Reason
- A time clock interface should prevent invalid sequences instead of asking users to correct them later.
Consequence
The UI only exposes meaningful actions: clock in before work starts, break or clock out while working, and return while on break.
Local-first persistence
- Option A
- Require a backend before the app can save time entries.
- Option B
- Persist records locally with UserDefaults and JSON encoding.
- Chosen path
- Local JSON persistence.
- Reason
- The first version needs to prove the device experience without adding account, server, or network complexity.
Consequence
The app remains usable offline and keeps the implementation small enough to inspect.
Deletion-safe sync
- Option A
- Sync only the current list of records between devices.
- Option B
- Sync records together with deleted record IDs.
- Chosen path
- Archive sync with deletion markers.
- Reason
- If one device deletes a record, delayed sync from the other device should not restore stale data.
Consequence
The merge logic can reconcile both new records and removals across iPhone and Apple Watch.
Implementation highlights
How the system was built
- Created separate SwiftUI entry points for iPhone and Apple Watch while sharing the same store and domain files.
- Modeled punch types as
clockIn,breakStart,breakEnd, andclockOut, each with labels, short labels, SF Symbols, and tint behavior. - Derived the current shift status from ordered records, then used that status to control the next valid UI actions.
- Calculated daily worked time, break time, first clock-in, and last clock-out from the same record sequence.
- Persisted a
PunchArchivelocally with ISO-8601 encoded dates and sorted records. - Implemented WatchConnectivity sync with application context, user info fallback, reachable-device message delivery, and deletion markers.
- Built iPhone views for dashboard, today timeline, daily summary, full history, and destructive clear actions.
- Built a compact Apple Watch dashboard focused on quick status checks, punch actions, and recent records.
Results
What the work achieved
- The project is now a public GitHub repository with a documented SwiftUI app structure and bilingual README files.
- The app contains 22 Swift files across shared logic, iPhone UI, and Apple Watch UI.
- The codebase demonstrates native state modeling, cross-device synchronization, local persistence, and device-specific interface composition.
- The time clock flow is structured around product rules rather than freeform buttons, which makes the interface harder to misuse.
- The current build is a strong first native app artifact, with future validation still needed for tests, signing, App Store distribution, and real-user feedback.
Gallery
Artifacts and checkpoints
Artifact
iPhone dashboard flow
The phone surface combines status, contextual punch actions, today's summary, today's timeline, sync status, and access to full history.
Evidence: PhoneDashboardView, DailySummaryView, PunchTimelineView.
Artifact
Apple Watch companion
The watch surface uses the same store but compresses the interaction into quick status, valid actions, and recent records.
Evidence: WatchDashboardView and shared views.
Artifact
Shared time-clock model
Punch records, shift status, daily summaries, formatters, storage, and sync contracts live outside the device-specific screens.
Evidence: Shared/Models, Shared/Stores, Shared/Connectivity.
Artifact
Deletion-aware sync
Records are synchronized with deleted IDs so one device cannot accidentally restore punches removed on another device.
Evidence: PunchArchive.deletedRecordIDs and TimeClockStore.merge.
Learnings
What I would carry into the next iteration
- Even a small native app benefits from separating product rules from screens early.
- Cross-device features need deletion and conflict behavior, not only a happy path for new data.
- Contextual actions make the product feel simpler because the state model removes invalid choices before they reach the user.
- The next iteration should add automated tests for summary calculation and merge behavior before expanding features.