| Dashboard (Initial) | Dashboard (After Log) |
|---|---|
| History | Edit Form |
|---|---|
NappyChangeTracker is a SwiftUI iOS project for recording and reviewing baby nappy changes.
The app currently supports:
- Recording nappy changes with date/time, size, caregiver role, and notes
- Backdating entries (recording historical changes)
- Editing and deleting existing entries
- Viewing a dashboard and full history list (newest first)
This repository is intended as a foundation for open-source contributors and future App Store release work.
- Keep the codebase simple, explicit, and safe under Swift 6 concurrency
- Make features easy to extend without rewriting core flow
- Provide a predictable architecture that contributors can follow
This project intentionally favours long-term maintainability over short-term convenience:
- Scene-owned navigation keeps routes in one place per flow, so feature changes do not scatter navigation logic across views.
- View/ViewModel separation keeps rendering and business behaviour independent, which improves readability and change safety.
- Protocol-backed services make persistence replaceable without rewriting screens or view models.
- Actor-backed mutable state provides a clear concurrency boundary for data mutations.
- AsyncStream-driven updates keeps UI state in sync with service state while preserving asynchronous, testable APIs.
The app follows a lightweight layered approach:
Models/: domain data structures (NappyChange,NappyChangeDraft, etc.)Services/: data access and persistence abstractions (NappyChangeServiceProtocol) and implementations (InMemoryNappyChangeService)ViewModels/: state orchestration for each screenViews/: UI components and formsScenes/: navigation owners (NavigationStack+ destinations)ContentView: single appTabViewroot
- Single navigation ownership per flow: each
*Scenecontrols its own destinations, reducing hidden routing logic. - Views stay UI-focused: user interactions call ViewModel methods; ViewModels coordinate service calls.
- Actor-backed service layer: mutable data lives in an actor for concurrency safety.
- Protocol-backed dependencies: service contracts are defined via protocols so storage can be swapped later.
- Async stream updates: UI stays reactive to data changes through
AsyncStream<[NappyChange]>.
- App entry:
NappyChangeTrackerApp - Root tabs:
ContentViewDashboardScene->DashboardViewHistoryScene->NappyChangeHistoryView
- Editor route:
NappyChangeEditorMode(create/edit(UUID))
- A View initialises its ViewModel.
- The ViewModel receives a service dependency (
NappyChangeServiceProtocol). - ViewModels observe
stream()for live updates. - Create/edit/delete operations call async service methods.
- Service publishes updated snapshots back through the stream.
This project intentionally follows strict conventions:
- Swift 6 code only
- One
TabViewin the app root - Scene-based navigation ownership
- Explicit access control
- No business logic inside Views
- Form editing UX includes save/reset/delete behaviour
- Service dependencies passed through protocols
When adding a new feature, follow this order:
- Add or update domain model in
Models/. - Extend service protocol with required operations.
- Implement service behaviour in actor-backed service.
- Add a ViewModel focused on state + actions.
- Add Views for UI only.
- Register navigation in the owning
*Scene.
- Add a
Statisticsmodel inModels/ - Add
fetchStatistics()toNappyChangeServiceProtocol - Implement it in
InMemoryNappyChangeService(and future persistent service) - Add
StatisticsViewModel - Present data in a new View from
DashboardScene
Current storage is in-memory for rapid iteration.
Recommended next step:
- Add a persistent service implementation (for example file-backed JSON or database-backed storage)
- Keep
NappyChangeServiceProtocolstable and switchAppServiceContainerwiring
This keeps UI and ViewModels unchanged while persistence evolves.
- Open
NappyChangeTracker.xcodeprojin Xcode - Select scheme:
NappyChangeTracker - Use simulator:
iPhone 17 Pro - Build and run
CLI build:
xcodebuild -project NappyChangeTracker.xcodeproj \
-scheme NappyChangeTracker \
-destination 'platform=iOS Simulator,name=iPhone 17 Pro' buildIf SwiftUI previews fail with JIT object-file permission errors:
- Reset custom Xcode build locations to defaults
- Ensure DerivedData/Intermediates are under:
~/Library/Developer/Xcode/DerivedData
- Clean build folder and restart Xcode
Contributions are welcome. Please keep PRs focused and architecture-consistent:
- Keep one responsibility per file/type
- Avoid introducing alternative navigation patterns in the same flow
- Prefer extending existing service and ViewModel layers over view-level shortcuts
- Preserve concurrency safety (actors, async APIs, cancellation checks)
- Persistent storage implementation
- Export/import support
- Richer dashboard statistics
- Basic test suite for services and ViewModels
- Accessibility and localisation pass
- Privacy policy + release prep for App Store distribution