Native Mobile · Plan 1 of 2 · iOS

MOS Field — Native iOS App Plan

A comprehensive development plan for the native iOS companion to the MOS Operations Platform, covering every current and future platform feature (Phases 1–5). Governed end-to-end by the iOS App Development Constitution (July 1, 2026 — Final): Swift + SwiftUI, strict MVVM, SOLID, Apple-native frameworks only, zero third-party dependencies.

Target · iOS 17.0+ Stack · Swift / SwiftUI / SwiftData Architecture · MVVM + SOLID Dependencies · Zero third-party Backend · FastAPI + Supabase Status · Planning
01

Purpose & product shape

MOS Field is the native iOS companion to the MOS Operations Platform. It is not a wrapper around the web app — it is a first-class client of the same FastAPI + Supabase backend, built for the people who spend their day away from a desk. The web app remains the primary surface for office staff and heavy PM workflows; the iOS app starts field-first and grows to cover every role as platform phases ship.

Field Crew Primary

Today's jobs, navigation to site, photo capture, notes tied to the field book, daily reports, clock in/out — designed for one-handed use, gloves, and weak rural signal.

Project Manager Secondary

Job status at a glance, crew locations, approving field reports, pushing documents to a crew mid-job.

Company Owner Phase C

KPI dashboard, profitability and AR snapshots, historical search — the executive view in a pocket.

Office Staff Phase C

Scheduling adjustments and customer lookups when away from the desk; the web app remains their main tool.

One backend, three clients. Web (Next.js), iOS (this plan), and Android (companion plan, next) all speak to the same FastAPI API with the same Supabase-issued JWT and the same server-side RBAC. Nothing in this plan forks the domain model.
02

Constitution compliance map

Every pillar of the constitution, mapped to the concrete decision in this plan. One deviation exists, and it is explicitly authorized by the product owner (constitution §preamble: “Do not deviate unless explicitly instructed by the user”).

Constitutional pillarDecision in this planStatus
Stack — Swift, SwiftUI, iOS 17+, XcodeSwift (latest stable), SwiftUI-only UI, min deployment iOS 17.0. UIKit touched only where SwiftUI cannot do the job (haptics via UIImpactFeedbackGenerator, as the constitution itself prescribes).Compliant
Zero third-party dependenciesNo Supabase Swift SDK, no Alamofire, no image libraries. Supabase Auth is consumed as plain HTTPS REST (GoTrue endpoints) via URLSession. Maps = MapKit. Charts = Swift Charts. Camera = AVFoundation/PhotosUI. Speech = Speech framework.Compliant
MVVM — strict layer separationOne @MainActor ViewModel per screen; Models are UI-free Codable structs; Views are declarative and logic-free; all I/O behind Services/Repositories (§03).Compliant
SOLIDProtocol-first services (AuthServiceProtocol, NetworkServiceProtocol, JobRepositoryProtocol…), initializer injection everywhere, small role-scoped protocols, no concrete dependencies in ViewModels (§03).Compliant
Authentication — Sign in with Apple primaryDeviation (owner-authorized): primary auth is email/password against the existing Supabase employee accounts, because roles are admin-assigned to company identities and must match the web app. Face ID/Touch ID re-entry via LocalAuthentication is kept exactly as mandated. Sign in with Apple can be layered on later as a linked provider without architectural change.Authorized deviation
Keychain-only secretsAccess + refresh tokens live in Keychain via a typed KeychainManager with kSecAttrAccessibleWhenUnlockedThisDeviceOnly. Nothing sensitive in @AppStorage/UserDefaults.Compliant
Networking — URLSession, HTTPS, typedSingle protocol-driven layer, async throws only, typed Endpoint/AppError, certificate pinning on the production API host, explicit CodingKeys, ATS fully on (§05).Compliant
Persistence — SwiftData + KeychainSwiftData @Model entities behind repositories; offline write-queue for field data; Keychain for secrets; @AppStorage only for benign flags (§06).Compliant
HIG — native, accessibleNavigationStack + role-aware TabView, SF Pro type scale, semantic colors with brand Color Sets (light/dark), SF Symbols, Dynamic Type, VoiceOver labels/hints, 44pt targets (§07).Compliant
Concurrency — async/await, @MainActorAll async paths use Swift Concurrency; no DispatchQueue.main.async, no completion handlers in new code; task cancellation on view disappear.Compliant
Testing — XCTest, >80% logic coverageProtocol mocks for every injected dependency; unit tests per ViewModel/Service; UI tests for sign-in, jobs, photo upload (§10).Compliant
The single deviation, on the record. Constitution §6 names Sign in with Apple as the primary auth method. For an internal operations tool where the office assigns roles to company accounts, Apple-ID-first would break identity parity with the web platform. The product owner has explicitly directed email/password (Supabase) as primary. Everything else in §6 — Keychain, biometrics, ATS, pinning — applies unchanged.
03

Architecture & project layout

Strict MVVM with a Services/Repositories core, instantiated with MOS feature names. The folder structure below is the constitution's mandated skeleton filled in for this product — Phase A folders are created at kickoff; later-phase folders are created only when their phase begins (no speculative scaffolding, per constitution §4).

/App
  MOSFieldApp.swift            ← @main entry; injects root environment
  AppCoordinator.swift         ← session state → Login vs role-aware TabView
/Features
  /Auth        /Views /ViewModels /Models
  /Jobs        /Views /ViewModels /Models     ← list, detail, status updates
  /Notes       /Views /ViewModels /Models
  /Documents   /Views /ViewModels /Models
  /Photos      /Views /ViewModels /Models     ← Phase A
  /FieldReports /Views /ViewModels /Models    ← Phase A
  /TimeTracking /Views /ViewModels /Models    ← Phase B
  /Mileage     /Views /ViewModels /Models     ← Phase B
  /Equipment   /Views /ViewModels /Models     ← Phase B
  /Dashboard   /Views /ViewModels /Models     ← Phase C (owner/PM)
  /Search      /Views /ViewModels /Models     ← Phase C
  /VoiceReports /Views /ViewModels /Models    ← Phase D
/Core
  /Services      AuthService.swift · NetworkService.swift · SyncService.swift
                 PushRegistrationService.swift · LocationService.swift
  /Repositories  JobRepository.swift · NoteRepository.swift · DocumentRepository.swift
                 TimeEntryRepository.swift · UploadQueueRepository.swift
  /Persistence   PersistenceController.swift  ← SwiftData ModelContainer
  /Security      KeychainManager.swift · BiometricAuthManager.swift · CertificatePinningDelegate.swift
/Shared
  /Components    PrimaryButton.swift · InputField.swift · StatusPill.swift · RoleBadge.swift · DataStateView.swift
  /Extensions    View+Extensions.swift · String+Extensions.swift
  /Constants     AppConstants.swift · APIConstants.swift
  /Utilities     Logger.swift · Validator.swift
/Resources
  Assets.xcassets              ← MOS brand Color Sets (light/dark variants)
  Localizable.strings
Design-language parity with the web app. StatusPill, RoleBadge, and DataStateView mirror the primitives already shipped in apps/web (Day 3), and brand colors come from the approved design-system tokens as Asset Catalog Color Sets with light/dark variants — semantic colors only in views, hex lives in the catalog, exactly as the constitution requires.

Layer contract

LayerRules applied
ModelPlain Codable structs mirroring API schemas (Job, JobNote, UserProfile, RoleKey enum). No import SwiftUI. Domain logic (e.g., allowed status transitions Assigned → In Progress → Waiting → Completed) lives here as pure functions.
ViewModelOne per screen, final class, @MainActor, ObservableObject, @Published state + errorMessage: String?. Depends only on protocols, injected via initializer. Never touches URLSession or SwiftData directly.
ViewPure SwiftUI; owns its ViewModel with @StateObject; calls async methods inside Task { }; renders loading/empty/error/populated through DataStateView; zero business logic.
Service / RepositoryServices own one domain each (auth, network, sync, push, location). Repositories mediate SwiftData + API per aggregate. Protocols are split by role when consumers need less (JobReadable vs JobWritable) — interface segregation.
// The DI pattern used by every ViewModel in the app
@MainActor final class JobListViewModel: ObservableObject {
    @Published private(set) var jobs: [Job] = []
    @Published var errorMessage: String?

    private let jobRepository: any JobReadable

    init(jobRepository: any JobReadable) {
        self.jobRepository = jobRepository
    }

    func loadAssignedJobs() async {
        do { jobs = try await jobRepository.assignedJobs() }
        catch let error as AppError { errorMessage = error.errorDescription }
        catch { errorMessage = AppError.unknown.errorDescription }
    }
}
04

Authentication & roles

Because the no-dependency rule forbids the Supabase Swift SDK, the app consumes Supabase Auth (GoTrue) as what it really is: a plain HTTPS REST API. This keeps the entire auth stack inside URLSession + Keychain + LocalAuthentication — all Apple-native.

Token lifecycle

StepMechanism
1 · Sign inPOST /auth/v1/token?grant_type=password on the Supabase project URL with the publishable (anon) key header. Response: ES256 access_token (~1h) + refresh_token. Decoded into a typed SessionTokens model.
2 · StoreBoth tokens → KeychainManager (kSecAttrAccessibleWhenUnlockedThisDeviceOnly). The anon key is a publishable client identifier (safe in the binary); real secrets never ship in source.
3 · Call the APIEvery FastAPI request carries Authorization: Bearer <access_token>. The backend already verifies signature via JWKS (ES256) — no shared secret anywhere, including the app.
4 · RefreshAuthService refreshes proactively before expiry and reactively on a 401 (grant_type=refresh_token), serialized through an actor so concurrent requests trigger exactly one refresh.
5 · Re-entryOn foreground/app-launch with a stored session: Face ID / Touch ID via BiometricAuthManager (LocalAuthentication) gates access before tokens are read. Passcode fallback; sign-out wipes Keychain.
6 · RolesAfter sign-in, GET /api/me + GET /api/me/roles (already live on the backend) hydrate UserProfile and [RoleKey]. Roles gate navigation and actions in the UI — but authorization is always re-enforced server-side; the client never trusts itself.

Role-aware navigation

Future Sign in with Apple. Supabase supports Apple as an OAuth provider, so restoring full §6 compliance later is additive: a second method on AuthServiceProtocol and an account-linking step — no changes to token storage, refresh, or RBAC.
05

Networking layer

One protocol-driven layer, exactly per constitution §7 — the snippets below are the constitution's own contracts extended with the MOS endpoint catalog.

protocol NetworkServiceProtocol {
    func request<T: Decodable>(_ endpoint: Endpoint) async throws -> T
}

struct Endpoint {
    let path: String
    let method: HTTPMethod          // .get / .post / .put / .delete
    let headers: [String: String]
    let body: Encodable?
}

enum AppError: LocalizedError {
    case networkUnavailable, unauthorized, forbidden, notFound
    case decodingFailed, serverError(Int)
    case keychainFailure(OSStatus), biometricUnavailable
    case validationFailed(String), syncConflict, unknown
}
06

Persistence & offline-first

Survey crews work where signal doesn't. The app treats the local SwiftData store as the read source of truth for the UI and queues writes when offline — the single most important product decision in this plan.

SwiftData entities (repositories only — never touched from Views)

@ModelPurposeSync behavior
JobEntityAssigned jobs cache: number, client, site, status, due, crewPull-refresh + on-launch delta fetch; server wins on conflict
JobNoteEntityNotes authored on deviceQueued write; marked pending → synced
PhotoUploadEntityCaptured photos + job link + GPS + timestampBackground URLSession upload queue, survives app termination
DailyReportEntityField report draftsDraft locally; explicit submit; queued if offline
TimeEntryEntityClock in/out pairs (Phase B)Timestamped locally at tap-time; reconciled on sync
MileageEntityTrip logs (Phase B)Queued write
07

Feature roadmap — iOS Phases A–D

Every platform feature from the product roadmap (Phases 1–5), mapped to iOS delivery phases. Each feature names its MVVM units and the Apple-native framework that satisfies the zero-dependency rule. Later phases are planned here but scaffolded only when they begin.

iOS Phase A — Field MVP

maps platform Phase 1 + Phase 2 · ships first

A field crew member can run their whole day from the phone: see jobs, get to site, capture evidence, file the daily report.

FeatureMVVM unitsApple frameworks
Sign in + biometric re-entryLoginView/ViewModel, BiometricGateView, AuthService, KeychainManagerAuthenticationServices-ready, LocalAuthentication, Security
My Jobs list + detailJobListView/ViewModel, JobDetailView/ViewModel, JobRepositorySwiftUI, SwiftData
Status updates (Assigned → In Progress → Waiting → Completed)Transition rules in Job model; JobDetailViewModel.updateStatus()— (pure domain logic)
Job notes — built Jul 14JobDetailView/ViewModel, JobNoteRepository, JobNoteEntitySwiftUI, SwiftData
Document viewingDocumentListView/ViewModel, DocumentRepositoryQuickLook
Photo capture + upload (GPS + timestamp stamped) — built Jul 14PhotoCaptureView, JobDetailViewModel, PhotoEvidenceStamp, PhotoLocationProvider, DocumentRepositoryUIKit camera bridge, CoreLocation, SwiftUI
Site maps + navigation hand-offJobMapView inside job detailMapKit, CoreLocation
Daily field reports — built Jul 14JobDetailView/ViewModel, FieldReportRepository, FieldReportEntitySwiftUI, SwiftData
Push notifications (assignment, status, due-date) — built Jul 14PushRegistrationService, AppDelegate; deep-link into job detailUserNotifications, APNs

iOS Phase B — Time & assets

maps platform Phase 3

The phone becomes the timesheet and the equipment ledger.

FeatureMVVM unitsApple frameworks
Clock in/out with GPS evidence and job-site arrival/leave reminders — built Jul 14TimeClockView/ViewModel, TimeEntryRepository, JobGeofenceServiceCoreLocation, UserNotifications, SwiftData
Time Off request form, status history, and offline read cache — built Jul 14TimeOffView/ViewModel, TimeOffRepository, TimeOffEntitySwiftUI, SwiftData
Mileage tracking with start/end odometer validation and calculated trip miles — built Jul 14TimeClockView/ViewModel, TimeEntryRepositorySwiftUI, CoreLocation
Equipment checkout/return with assignment and double-checkout protection — built Jul 14EquipmentListView/ViewModel, EquipmentRepositorySwiftUI
Payroll-ready summaries (read-only current-week hours and completed entries) — built Jul 14TimeClockView/ViewModelSwiftUI

iOS Phase C — Management & insight

maps platform Phase 4

Owner and office roles get real value beyond the web parity baseline.

FeatureMVVM unitsApple frameworks
Executive dashboard (active jobs, revenue, margin, AR) — built Jul 14InsightsView/ViewModelSwift Charts
Reports (server-rendered daily-report PDFs viewed natively) — built Jul 14ReportLibraryView/ViewModelQuickLook
Historical search across jobs/clients/docs/notes — built Jul 14InsightsView/ViewModel, debounced query to backendSwiftUI
Office scheduling adjustments — built Jul 6DispatchListView/ViewModelSwiftUI
Home-screen at-a-glance — built Jul 8MOSFieldWidget (jobs due / status)WidgetKit

iOS Phase D — Intelligence

maps platform Phase 5 · AI features surfaced natively

Heavy AI runs on the backend; the app contributes what only a phone can — voice, camera, and context.

FeatureMVVM unitsApple frameworks
Voice-to-report — on-device dictation followed by backend OpenAI structuring — built Jul 15SpeechTranscriptionService, AIRepository, JobDetailViewModelSpeech, AVFoundation
Document summaries — private PDF/text extraction and professional survey summary — built Jul 15DocumentListViewModel, AIRepositorySwiftUI; backend-computed
Intelligent search — natural-language queries interpreted by the backend before cross-entity search — built Jul 15InsightsViewModel, AIRepositorySwiftUI
Smart capture — document scanning with perspective correction for field paperwork — built Jul 15DocumentScannerViewVisionKit (VNDocumentCameraViewController)
08

Backend work the app depends on

The iOS app consumes the platform API — these are the FastAPI additions each iOS phase needs. They are platform-side work items, not iOS work, and most are already on the platform roadmap (the web app needs them too).

Needed byBackend additionNotes
Phase AJobs CRUD + status transition endpoints (/api/jobs…)Already Day-5 scope for the web vertical slice; shared as-is
Phase ANotes + documents endpoints; presigned upload/download URLs (Supabase Storage brokered by FastAPI)App never holds storage credentials — it receives short-lived signed URLs
Phase ADevice-token registration (POST /api/devices) + APNs push serviceServer-side APNs auth key; push on assignment/status/due events
Phase BTime-entry, mileage, and equipment endpointsSame tables feed payroll export on the web
Phase CDashboard aggregates + search endpointServer-computed; app only renders
Phase DAI endpoints: report structuring from transcript, doc summaries, NL searchPlatform Phase 5 scope
Contract-first. Every endpoint lands in the FastAPI OpenAPI schema first; iOS Model structs are written against that schema (mirroring the typed-client approach planned for the web). One source of truth, three clients.
09

Security checklist — constitution §6 applied

10

Testing, delivery & milestones

Testing standards (constitution §12)

M1 Job Notes validation: immutable note creation, job-scoped network refresh, SwiftData caching, offline reads, composer behavior, error recovery, and field-crew authorization are covered by repository and ViewModel tests. The full scheme passed 83 tests with zero failures or skips on iPhone 17 / iOS 26 simulator on July 14, 2026; the Debug app also installed and launched cleanly.
M2 Field Photos validation: captured images receive a permanent UTC timestamp and six-decimal GPS evidence banner when location is available, then upload as photo/field documents scoped to the active job. Missing location is labeled explicitly rather than fabricated. The full scheme passed 89 tests with zero failures or skips on iPhone 17 / iOS 26 simulator on July 14, 2026, followed by a clean Debug build.
M2 Daily Field Reports validation: reports capture weather, crew, completed work, issues, and next steps against the active job, with SwiftData offline reads and a server uniqueness gate per author/job/date. The API suite passed 62 tests and the complete iOS scheme passed 91 tests with zero failures or skips on July 14, 2026.

Distribution

Milestones

M0 · Kickoff — ~1 wk
Project foundation
Xcode project, folder skeleton, brand Color Sets, KeychainManager, NetworkService + pinning, AuthService + biometric gate, AppCoordinator. Unit tests from the first file.
M1 · ~2–3 wks
Jobs core
Job list/detail/status/notes/documents against the Day-5 Jobs API; SwiftData cache + pull-to-refresh; DataStateView states everywhere.
M2 · ~2–3 wks
Field capture
Photo capture + background upload queue, site map + navigation hand-off, daily field reports, offline write-queue + SyncService.
M3 · TestFlight
Phase A in crews' hands
Push notifications live; real field feedback loop starts. Exit bar: constitution §15 checklist green on every screen.
M4 · ~2 wks
Phase B — time & assets
Clock in/out + geofencing, mileage, equipment checkout, payroll-ready week view.
M5 · ~2 wks
Phase C — management
Owner dashboard (Swift Charts), historical search, schedule view, WidgetKit at-a-glance.
M6 · scoped later
Phase D — intelligence
Voice-to-report, AI summaries, NL search, VisionKit capture — sequenced with platform Phase 5 backend delivery.
Definition of done, every screen: the constitution §15 checklist — MVVM separation, SOLID, zero third-party deps, Keychain-only secrets, HTTPS + async/await, typed errors, accessibility labels, Dark Mode + Dynamic Type, no force unwraps, ViewModel/Service tests, localized strings, no dead code.
11

Appendix — the Constitution (verbatim)

iOS App Development Constitution · July 1, 2026 · Status: Final. The governing document for all iOS work on this project, reproduced in full.

iOS SaaS App Development Constitution — click to expand

You are an expert iOS engineer specializing in SaaS application development. This document is your constitution — a set of non-negotiable principles and patterns you must follow precisely when building any iOS application in this project. Read every rule carefully. Apply every rule consistently. Do not deviate unless explicitly instructed by the user.

1. Core Technology Stack

  • IDE: Xcode (latest stable release)
  • Language: Swift (latest stable version)
  • UI Framework: SwiftUI (primary); UIKit only when SwiftUI cannot accomplish the requirement
  • Minimum Deployment Target: iOS 17.0
  • No external third-party libraries, SDKs, or package dependencies — use only Apple-native frameworks
  • No CocoaPods, SPM external packages, or Carthage dependencies

2. Architecture — MVVM (Non-Negotiable)

Every feature must follow strict Model-View-ViewModel separation. No exceptions.

  • Model: Plain Swift structs or classes representing data. No UI imports (import SwiftUI is forbidden in Model files). Codable conformance where persistence or networking is required. Business logic lives here, not in the View or ViewModel.
  • ViewModel: One ViewModel per View (or logical screen unit). Marked @MainActor for all UI-bound state. Conforms to ObservableObject; uses @Published for state properties. Handles all data transformation, validation, and state management. Never imports or references SwiftUI views directly. Calls into Services/Repositories — never performs networking or persistence inline.
  • View: Pure SwiftUI — declarative, stateless where possible. Receives its ViewModel via @StateObject (owner) or @ObservedObject (passed in). Contains zero business logic. No direct API calls, database calls, or heavy computation.

Folder structure: /App (AppEntry.swift, AppCoordinator.swift) · /Features/[FeatureName]/{Views, ViewModels, Models} · /Core/{Services, Repositories, Persistence, Security} · /Shared/{Components, Extensions, Constants, Utilities} · /Resources (Assets.xcassets, Localizable.strings)

3. SOLID Principles — Applied to Swift

  • S — Single Responsibility: Every class, struct, and protocol does one thing. A ViewModel manages state for one screen. A Service handles one domain (auth, networking, storage). If a file exceeds ~150 lines, question whether it has multiple responsibilities.
  • O — Open/Closed: Use protocols and extensions to extend behavior. Never modify existing working code to add new features — extend it. Prefer protocol-based abstractions over concrete type dependencies.
  • L — Liskov Substitution: Any conforming type must be fully substitutable for its protocol. Do not add fatalError or stub implementations in production conformances.
  • I — Interface Segregation: Define small, focused protocols. Never force a type to conform to methods it does not need. Example: split UserServiceProtocol into UserFetchable, UserUpdatable, UserDeletable.
  • D — Dependency Inversion: ViewModels depend on protocol abstractions, never concrete service implementations. Inject dependencies via initializer injection. This enables testability and clean separation.

4. Simplicity — No Bloat

  • Write only what is needed. Do not scaffold features that have not been requested.
  • No over-engineering. If a simple struct solves the problem, do not create a class hierarchy.
  • No premature abstraction. Create a protocol when you have two or more concrete implementations, not speculatively.
  • No redundant comments. Code should be self-documenting. Only comment on why, never what.
  • No dead code. Do not leave TODO stubs, unused variables, or commented-out blocks.
  • Prefer value types (struct, enum) over reference types (class) unless reference semantics are required.
  • Avoid massive files. Split large views into focused subcomponents.

5. Apple Human Interface Guidelines (HIG) Compliance

  • Layout & Navigation: NavigationStack for push navigation (not deprecated NavigationView). TabView for top-level navigation with 2–5 tabs. Respect safe area insets. Support Dynamic Type. Support Light and Dark Mode using semantic colors only.
  • Typography: Only SF Pro (system font) via .font(.title), .font(.body), etc. Never hardcode font sizes. Maintain clear visual hierarchy: Title → Headline → Body → Caption.
  • Color: Use Color(.systemBackground), Color(.label), Color(.secondaryLabel) etc. Define brand colors in Assets.xcassets as Color Sets with light/dark variants. Never use hardcoded hex values inline in views.
  • Controls & Interaction: Native SwiftUI controls. Minimum tap target 44×44 points. Haptic feedback for significant actions via UIImpactFeedbackGenerator. SF Symbols for all icons — no custom icon assets unless brand-required.
  • Accessibility: Every interactive element has .accessibilityLabel and .accessibilityHint. VoiceOver order via .accessibilitySortPriority. .accessibilityElement(children: .combine) for grouped content. Test with Accessibility Inspector before considering any screen complete.

6. Security — Best Practices (Apple-Native Only)

  • Authentication: Implement Sign in with Apple (AuthenticationServices) as the primary auth method. Support Face ID / Touch ID via LocalAuthentication for app re-entry. Never store passwords in UserDefaults — use Keychain exclusively.
  • Keychain: All sensitive data stored in Keychain via Security framework. kSecAttrAccessibleWhenUnlockedThisDeviceOnly as default accessibility. All Keychain operations wrapped in a dedicated KeychainManager with typed methods (save/retrieve/delete for KeychainKey).
  • Networking: URLSession with https:// only — reject HTTP. Certificate pinning for production API endpoints via URLSessionDelegate. Validate all server responses with type-safe decoding. JSONDecoder with explicit CodingKeys — never Any or untyped dictionaries.
  • Data Protection: NSFileProtectionComplete entitlement. Never log sensitive user data — custom Logger strips PII in production. #if DEBUG guards around diagnostic logging.
  • Input Validation: Validate and sanitize all user input before processing or transmitting. Dedicated Validator utility with typed rules. Reject over-length inputs before the ViewModel.
  • App Transport Security: NSAllowsArbitraryLoads must be false. No ATS exceptions unless absolutely required and documented.

7. Networking Layer

Only URLSession, structured through a typed, protocol-driven service layer (NetworkServiceProtocol.request<T: Decodable>, Endpoint, HTTPMethod). All calls async throws — Swift Concurrency only, no completion handlers. All HTTP error codes handled explicitly, mapped to typed AppError cases. Responses decoded into typed models immediately.

8. State Management

  • @StateObject — ViewModel owned by a View
  • @ObservedObject — ViewModel passed into a View
  • @EnvironmentObject — App-wide shared state (sparingly, truly global only)
  • @State — Local, ephemeral view state only
  • @AppStorage — Non-sensitive user preferences only
  • Never store sensitive data in @AppStorage or @State

9. Error Handling

Typed AppError enum conforming to LocalizedError (networkUnavailable, unauthorized, decodingFailed, keychainFailure(OSStatus), validationFailed(String)…). Every throwing function propagates typed errors — no empty catch. ViewModels expose errorMessage: String?. Errors shown via native .alert.

10. Swift Concurrency Rules

async/await for all asynchronous operations. ViewModels @MainActor. Task { } in .onAppear / button actions. TaskGroup for parallel operations. Never DispatchQueue.main.async. Cancel tasks appropriately on disappear.

11. Persistence

SwiftData (iOS 17+) as primary local persistence. Keychain for sensitive data. @AppStorage only for non-sensitive flags. Clear @Model classes. Never persist directly from a View — route through the Repository layer.

12. Testing Standards

Unit tests for every ViewModel and Service (XCTest). Protocol-based mocks for all injected dependencies. >80% coverage on business logic. UI tests for critical flows (login, onboarding, core feature). No production code ships with failing tests.

13. Code Style & Conventions

Swift API Design Guidelines naming. Default to private. final by default. No force unwrapping, no try!/as!. guard for early exit. Extensions for protocol conformance. Standard file header comment (filename, project, creation date) in every file.

14. What You Must Never Do

  • Import third-party libraries — violates no-external-dependency rule
  • Store tokens in UserDefaults — security vulnerability
  • Use HTTP endpoints — plaintext data transmission
  • Put business logic in Views — violates MVVM
  • Use DispatchQueue for UI updates — use @MainActor instead
  • Force unwrap optionals — runtime crash risk
  • Hardcode API keys in source — security vulnerability
  • Use Any or untyped dictionaries — type safety violation
  • Skip accessibility labels — HIG and accessibility violation
  • Leave TODO comments in delivered code — incomplete implementation

15. Checklist Before Delivering Any Feature

  • MVVM layers cleanly separated
  • All SOLID principles applied
  • No third-party dependencies introduced
  • Sensitive data stored in Keychain only
  • All network calls use HTTPS and async/await
  • Typed error handling implemented
  • Accessibility labels on all interactive elements
  • Dark Mode and Dynamic Type supported
  • No force unwraps in production code
  • Unit tests written for ViewModel and Service logic
  • No hardcoded strings — use Localizable.strings
  • No dead code, stubs, or TODO comments

This constitution governs all iOS development in this project. When in doubt, choose the simpler, more secure, more native solution.