109 lines
5.0 KiB
Markdown
109 lines
5.0 KiB
Markdown
|
|
# Project Structure
|
||
|
|
|
||
|
|
KisaniCal (display name **Wenza**) is a personal productivity + fitness iOS app,
|
||
|
|
built with SwiftUI and generated from `project.yml` via [XcodeGen]. It ships an
|
||
|
|
iOS app, a WidgetKit extension, and an optional watchOS companion.
|
||
|
|
|
||
|
|
## Build targets
|
||
|
|
|
||
|
|
Defined in [`project.yml`](project.yml):
|
||
|
|
|
||
|
|
| Target | Type | Platform | Bundle ID |
|
||
|
|
|---|---|---|---|
|
||
|
|
| `KisaniCal` | application | iOS 16+ | `com.kutesir.KisaniCal` |
|
||
|
|
| `KisaniCalWidgets` | app-extension | iOS 18+ | `com.kutesir.KisaniCal.KisaniCalWidgets` |
|
||
|
|
| `WenzaWatch` | application | watchOS 10+ | `com.kutesir.KisaniCal.watchkitapp` |
|
||
|
|
| `KisaniCalTests` | unit-test | iOS 16+ | — |
|
||
|
|
|
||
|
|
`WenzaWatch` is built/embedded only when the watchOS platform is installed
|
||
|
|
(see the commented `embed` note in `project.yml`).
|
||
|
|
|
||
|
|
## Directory layout
|
||
|
|
|
||
|
|
```
|
||
|
|
KisaniCal/ repo root (Gitea: kutesir/KisaniCal)
|
||
|
|
│
|
||
|
|
├── project.yml XcodeGen spec — source of truth for the .xcodeproj
|
||
|
|
├── KisaniCal.xcodeproj generated (do not hand-edit)
|
||
|
|
│
|
||
|
|
├── KisaniCal/ 📱 main iOS app target
|
||
|
|
│ ├── KisaniCalApp.swift @main entry point
|
||
|
|
│ ├── ContentView.swift root view / tab shell
|
||
|
|
│ ├── KisaniCal.entitlements
|
||
|
|
│ ├── LaunchScreen.storyboard
|
||
|
|
│ ├── Views/ SwiftUI screens (13)
|
||
|
|
│ │ ├── TodayView, CalendarView, MatrixView tasks
|
||
|
|
│ │ ├── WorkoutView, AddExerciseSheet fitness
|
||
|
|
│ │ ├── AuthView, OnboardingView, ProfileSetupView, SplashView
|
||
|
|
│ │ ├── SettingsView, TutorialView
|
||
|
|
│ │ └── TaskContextMenu, TodayMenuFeatures
|
||
|
|
│ ├── Managers/ stateful service singletons (10)
|
||
|
|
│ │ ├── AuthManager, CloudSyncManager auth + iCloud KVS
|
||
|
|
│ │ ├── HealthKitManager, SpeechRecognizer system frameworks
|
||
|
|
│ │ ├── NotificationManager, LiveActivityManager, ShortcutHandler
|
||
|
|
│ │ ├── TutorialManager
|
||
|
|
│ │ └── AppGroup, TaskActivityAttributes shared with widgets
|
||
|
|
│ ├── Models/ TaskItem, ExerciseModels, CalendarGrid
|
||
|
|
│ ├── Components/ SharedComponents, FloatingTabState
|
||
|
|
│ └── Theme/ DesignTokens
|
||
|
|
│
|
||
|
|
├── KisaniCalWidgets/ 🧩 WidgetKit extension (iOS 18+)
|
||
|
|
│ ├── KisaniCalWidgets.swift, MyTasksWidget, EventCountdownWidget
|
||
|
|
│ ├── TaskLiveActivity, WidgetViews, WidgetData
|
||
|
|
│ └── Info.plist, .entitlements
|
||
|
|
│
|
||
|
|
├── WenzaWatch/ ⌚️ watchOS companion (build-optional)
|
||
|
|
│ └── WenzaWatchApp.swift, Info.plist, .entitlements
|
||
|
|
│
|
||
|
|
├── KisaniCalTests/ 🧪 unit tests (the CI test target)
|
||
|
|
│ └── CalendarGridTests, RecurrenceTests, NLRecurrenceParseTests
|
||
|
|
│
|
||
|
|
├── .gitea/workflows/ 🔧 CI/CD (Gitea Actions)
|
||
|
|
│ ├── ci.yml build + test on PRs into main/develop
|
||
|
|
│ └── release.yml archive + export IPA on push to main
|
||
|
|
│
|
||
|
|
├── scripts/gitea-setup.sh Gitea branch-protection / default-branch setup
|
||
|
|
├── ExportOptions.plist IPA export template
|
||
|
|
│
|
||
|
|
├── CONTRIBUTING.md branch workflow + PR gate rules
|
||
|
|
├── PRODUCT.md product brief, users, design principles
|
||
|
|
├── SERVICES.md backing services / infra notes
|
||
|
|
├── ISSUES.md running issue log (KC-## ids)
|
||
|
|
└── Sanctum-Auto-FailOver-Uptime-Kuma.md
|
||
|
|
```
|
||
|
|
|
||
|
|
## Architecture
|
||
|
|
|
||
|
|
A flat SwiftUI **MVVM-lite** layout, organized by role rather than by feature:
|
||
|
|
|
||
|
|
- **Views** — SwiftUI screens, one file per screen.
|
||
|
|
- **Managers** — `ObservableObject` service singletons holding cross-cutting
|
||
|
|
state (auth, iCloud sync, HealthKit, notifications, speech, Live Activities).
|
||
|
|
- **Models** — plain data types (`TaskItem`, exercise/workout models, calendar
|
||
|
|
grid math).
|
||
|
|
- **Components / Theme** — shared UI and design tokens.
|
||
|
|
|
||
|
|
Three product domains coexist in one app:
|
||
|
|
|
||
|
|
1. **Tasks** — Today list, Calendar, Eisenhower Matrix; natural-language quick
|
||
|
|
add (with voice via `SpeechRecognizer`).
|
||
|
|
2. **Fitness** — workout logging with weighted/bodyweight sets; reads/writes
|
||
|
|
Health via `HealthKitManager`.
|
||
|
|
3. **Calendar / events** — surfaced on the dashboard, in **widgets**, and as
|
||
|
|
**Live Activities**.
|
||
|
|
|
||
|
|
### Cross-target code sharing
|
||
|
|
|
||
|
|
`KisaniCal/Managers/AppGroup.swift` and `TaskActivityAttributes.swift` are
|
||
|
|
compiled into **both** the app and the widget extension (see the widget target's
|
||
|
|
`sources` in `project.yml`), so task/activity data is shared through the App
|
||
|
|
Group container.
|
||
|
|
|
||
|
|
### Persistence & sync
|
||
|
|
|
||
|
|
State is persisted locally and mirrored to **iCloud key-value store** via
|
||
|
|
`CloudSyncManager`. Restore-on-launch rehydrates data after reinstall/update
|
||
|
|
(see ISSUES.md KC-39 for the save/restore symmetry the sync layer must keep).
|
||
|
|
|
||
|
|
[XcodeGen]: https://github.com/yonaskolb/XcodeGen
|