Odak — Complete SwiftUI Rebuild Plan
Odak — Complete SwiftUI Rebuild Plan
Context
The Odak app is currently built with React Native/Expo (Expo 55, RN 0.83, Reanimated, Unistyles v3, Skia). The user wants a ground-up rewrite in pure SwiftUI — no React Native, no JavaScript. The existing app has a mature feature set: timer state machine, 25 achievements, dot grid visualizations, Live Activities, widgets, 9 accent colors, and rich animations. The existing Swift code for widgets and Live Activities can be reused directly.
Goal: A native SwiftUI app that is feature-complete with the current React Native version, sharing code between app, widget, and Live Activity targets via a local Swift package.
Skills Required
| Skill Area | What It Covers |
|---|---|
| SwiftUI (advanced) | Canvas, TimelineView, NavigationStack, TabView, sheet detents, GeometryReader, @Observable, @Environment, @AppStorage, preferredColorScheme |
| Swift Concurrency | async/await, Sendable types, MainActor isolation, Task for fire-and-forget side effects |
| Core Graphics / Canvas | Drawing 50–4000 dots per frame, 8 custom shapes (stars, hearts, hexagons, diamonds), hit testing |
| Gesture System | DragGesture for pan/hold-to-start, LongPressGesture, simultaneous gestures, haptic feedback |
| Animation | withAnimation springs, TimelineView for 60fps Canvas, breathing loops, staggered transitions, parallax scroll |
| GRDB.swift | SQLite wrapper for sessions, achievements, stats — generated columns, views, migrations, WAL concurrent access |
| UserDefaults + App Groups | Settings storage, widget data sharing, @AppStorage with suite name |
| ActivityKit | Live Activities, Dynamic Island (existing Swift code to adapt) |
| WidgetKit | Home screen widgets with AppIntent configuration (existing Swift code to adapt) |
| Swift Charts | Bar charts (7/30/90 day), heatmaps for session visualization |
| ImageRenderer | Share card snapshot capture (replaces ViewShot) |
| PhotosUI | PHPickerViewController for event image selection |
| iOS 26 Liquid Glass | .glassEffect(.regular) with #if compiler(>=6.2) + #available(iOS 26.0, *) fallback chain |
| SPM Local Packages | Shared OdakKit package between main app, widget, and Live Activity targets |
| Xcode Project Setup | Multi-target project with app, widget extension, Live Activity extension, shared package |
Architecture
Project Structure
Odak/
├── Odak.xcodeproj
├── OdakApp/ # Main app target
│ ├── App/
│ │ ├── OdakApp.swift # @main, WindowGroup
│ │ └── AppState.swift # Root @Observable container
│ ├── Navigation/
│ │ └── AppTabView.swift # 4-tab TabView + coordinators
│ ├── Features/
│ │ ├── Focus/ # Timer screen (Tab 1)
│ │ ├── Time/ # Time visualization views
│ │ ├── Dates/ # Countdown/milestone events (Tab 2)
│ │ ├── Stats/ # Charts & history (Tab 3)
│ │ ├── You/ # Profile, achievements (Tab 4)
│ │ └── Modals/ # Settings, Share, Welcome
│ ├── Components/ # Shared UI: AdaptiveCard, DotShapes, etc.
│ └── Utils/ # HapticManager, Capabilities
├── OdakKit/ # SPM local package (shared)
│ └── Sources/OdakKit/
│ ├── Domain/ # TimerEngine, Presets, AchievementDefs
│ ├── Models/ # All Codable structs
│ ├── Data/ # OdakDatabase (GRDB), AppGroupStorage, ImageStorage
│ └── Theme/ # AccentColor, BackgroundMode, DotShape enums
├── OdakWidget/ # Widget extension (port existing Swift)
├── OdakLiveActivity/ # Live Activity extension (port existing Swift)
└── OdakIntents/ # AppIntents for widget configuration
Key Decisions
| Decision | Choice | Why |
|---|---|---|
| Min deployment | iOS 17.0 | Required for @Observable, modern NavigationStack, Swift Charts, AppIntents |
| Persistence (structured) | GRDB.swift | Existing schema uses generated columns, complex views, ON CONFLICT upserts — SwiftData can't express these |
| Persistence (settings) | UserDefaults (App Group suite) | Widgets already read from this; @AppStorage provides SwiftUI reactivity |
| State management | @Observable + @Environment | Per-property tracking, no third-party deps |
| Dot grid rendering | Canvas + TimelineView | Single draw call for 50–4000 dots; individual views would tank performance |
| Charts | Swift Charts | Native, accessible, matches iOS design language |
| Code sharing | SPM local package (OdakKit) | Clean sharing between 3 targets without framework embedding issues |
| Image snapshots | ImageRenderer | Native SwiftUI replacement for ViewShot |
Implementation Phases
Phase 1: Foundation
Create Xcode project + OdakKit package with all domain logic and data layer.
Files to create:
OdakKit/Package.swift— local package with GRDB dependencyOdakKit/Sources/OdakKit/Models/— all domain types:TimerTypes.swift—TimerPhase,TimerState,TimerEvent,ActiveTimerState,HoldingState,TimerDisplayState,FocusSettings(port fromdomain/types.ts)Preset.swift—PresetId,Preset, static quick/standard/deep definitions (port fromdomain/models/Preset.ts)FocusSession.swift—FocusSessionwith computeddateKey/weekKey/monthKey(port fromdomain/models/Session.ts)Achievement.swift—AchievementDefinition,AchievementProgress,AchievementCategory,CriteriaType, all 25 static definitions (port fromdomain/models/Achievement.ts)Event.swift—AheadEvent,SinceEventCodable structsStats.swift—StreakData,DailyStats,ComputedStats
OdakKit/Sources/OdakKit/Domain/TimerEngine.swift— pure reducer function, 1:1 port ofdomain/TimerEngine.tsOdakKit/Sources/OdakKit/Domain/AchievementEngine.swift— processing pipeline with protocol-based criteria checkers (port fromdomain/achievements/AchievementEngine.ts)OdakKit/Sources/OdakKit/Data/OdakDatabase.swift— GRDB wrapper with exact schema fromdata/database/OdakDatabase.ts(tables, views, indexes, generated columns)OdakKit/Sources/OdakKit/Data/AppGroupStorage.swift— UserDefaults wrapper matching all MMKV keys fromutils/storage.tsOdakKit/Sources/OdakKit/Data/ImageStorage.swift— App Group container image save/load/deleteOdakKit/Sources/OdakKit/Theme/AccentColor.swift— 9 accent colors (blue, green, orange, yellow, pink, red, mint, purple, brown) mapping to system colorsOdakKit/Sources/OdakKit/Theme/OdakTheme.swift— spacing (4/8/16/24/32/48), borderRadius (8/16/24/32/9999), typography sizes, all color tokens fromtheme/unistyles.ts
Reference files to port from:
domain/TimerEngine.ts— state machine reducer (6 phases, 10 events)domain/types.ts— all type definitions and constantsdomain/models/Preset.ts— preset definitionsdomain/models/Achievement.ts— 25 achievement definitionsdomain/achievements/AchievementEngine.ts— achievement processing pipeline + SQL queriesdata/database/OdakDatabase.ts— complete SQLite DDL, views, indexesutils/storage.ts— all MMKV keys, types, defaultstheme/unistyles.ts— complete theme tokens
Phase 2: App Shell + Focus Timer (Core Experience)
Build the navigation skeleton and the most important screen — the focus timer.
Files to create:
OdakApp/App/OdakApp.swift— @main with WindowGroup, environment injection, theme initializationOdakApp/App/AppState.swift— root @Observable holding database, storage, theme manager, child VMsOdakApp/Navigation/AppTabView.swift— 4-tab TabView (Focus, Dates, Stats, You) with SF SymbolsOdakApp/Features/Focus/FocusScreen.swift— timer orchestrator with preset selector (idle), countdown (active), break overlayOdakApp/Features/Focus/FocusViewModel.swift— @Observable wrapping TimerEngine, driving ticks via Timer, managing Live ActivityOdakApp/Features/Focus/FocusDotGrid.swift— Canvas + TimelineView rendering 10x5 dot grid with decay animation on current dotOdakApp/Features/Focus/SwipeToFocusButton.swift— hold-to-start (1.5s idle / 2.0s focusing), breathing animation, DotRing integrationOdakApp/Features/Focus/DotRing.swift— 12 dots at 30deg intervals, Canvas-drawn, fills during hold, celebration spring at completionOdakApp/Features/Focus/PresetSelector.swift— 3 circular buttons (10/25/50), accent bg when selected, glass otherwiseOdakApp/Features/Focus/BreakOverlay.swift— full accent-colored background, single-row dot grid, skip buttonOdakApp/Components/AdaptiveCard.swift— iOS 26 glassEffect → .ultraThinMaterial → solid background fallbackOdakApp/Utils/HapticManager.swift— light/medium/heavy impact + notification success/warning wrappersOdakApp/Utils/Capabilities.swift—hasLiquidGlassSupportcheck
Reference files:
app/(tabs)/focus/index.tsx— focus screen layout, phase transitions, debug overlaycomponents/focus/DotGrid.tsx— dot rendering, decay animation, charging, pan gesturecomponents/focus/SwipeToFocus.tsx— hold gesture, breathing, DotRing, progress trackingcomponents/focus/DotRing.tsx— 12-dot ring layout, progress-driven fillcomponents/focus/SealButton.tsx— circular progress ring, break-the-sealcomponents/ui/AdaptiveCard.tsx— glass/blur/solid fallback pattern
Phase 3: Time Visualization Screen
Build the dot grid time visualization with all 7 view types and 8 dot shapes.
Files to create:
OdakApp/Features/Time/TimeScreen.swift— view type switching, header with context menu, share buttonOdakApp/Features/Time/TimeDotGrid.swift— Canvas rendering all view types (now/today/month/year/life/since/ahead) with pan gestureOdakApp/Features/Time/TimeHeader.swift— left pill (Menu for view type), right pill (time remaining / dot label), share pillOdakApp/Features/Time/DotGridConfig.swift— per-view-type configuration: total dots, columns, gap sizes, dot sizing formulaOdakApp/Components/DotShapes.swift— 8 shape renderers for Canvas (circle, square, diamond, star, heart, hexagon, x, hash)OdakApp/Components/AdaptivePillButton.swift— glass pill with text/icon content
Reference files:
app/(tabs)/index.tsx— all view type logic, dot sizing formula, column configs, pan gesture hit testing
Phase 4: Dates Screen (Events)
Build countdown/milestone events with image backgrounds and CRUD.
Files to create:
OdakApp/Features/Dates/DatesScreen.swift— segmented picker (Ahead/Since), grid/list toggle, sort options, toolbarOdakApp/Features/Dates/DatesViewModel.swift— @Observable managing events from AppGroupStorageOdakApp/Features/Dates/TimeCard.swift— ImageBackground with dark overlay, day count + title, grid (1:1) and list (170pt) modesOdakApp/Features/Dates/AddEventSheet.swift— PHPicker for image, title input (35 char max), DatePicker graphical styleOdakApp/Features/Dates/EventDetailScreen.swift— parallax scroll header (350pt), real-time countdown, progress bar, calendar section
Reference files:
app/(tabs)/dates/index.tsx— dates screen with segmented picker, sort, grid/listcomponents/TimeCard.tsx— card layout and sizingcomponents/EventDetailScreen.tsx— parallax scroll implementation, countdown display
Phase 5: Stats Screen (Bank)
Build session analytics with charts and history.
Files to create:
OdakApp/Features/Stats/StatsScreen.swift— today stats strip, chart cards, session heatmap, staggered animationsOdakApp/Features/Stats/StatsViewModel.swift— @Observable loading session data from GRDBOdakApp/Features/Stats/BarChartView.swift— Swift Charts BarMark for 7/30/90 day ranges, accent highlighting for current periodOdakApp/Features/Stats/SessionHeatmap.swift— row of 16pt colored circles for recent sessionsOdakApp/Features/Stats/SessionHistoryScreen.swift— SectionList grouped by day, session cards with completion badges
Reference files:
app/(tabs)/bank/index.tsx— stats layout, chart configurations, heatmapapp/(tabs)/bank/history.tsx— section list with deduplication
Phase 6: You Screen (Profile & Achievements)
Build profile with streaks, stats, and achievement system.
Files to create:
OdakApp/Features/You/YouScreen.swift— streak card, stats cards, daily goal dots, awards summaryOdakApp/Features/You/YouViewModel.swift— @Observable loading from GRDB + AppGroupStorageOdakApp/Features/You/StreakCard.swift— accent bg, flame SF Symbol (tier-based), scale-on-tap animationOdakApp/Features/You/DailyGoalDots.swift— filled/empty dot rowOdakApp/Features/You/AwardsGallery.swift— 3-col grid (5 on iPad), locked (gray 0.5 opacity) / unlocked (accent)OdakApp/Features/You/AwardDetail.swift— always black bg, ZoomIn entrance animation, progress bar or earned date
Reference files:
app/(tabs)/you/index.tsx— profile layout, streak calculation displayapp/(tabs)/you/awards.tsx— awards grid layoutapp/(tabs)/you/award/[id].tsx— detail with dark background
Phase 7: Modals (Settings, Share, Welcome)
Files to create:
OdakApp/Features/Modals/SettingsSheet.swift— Form-style: accent color picker (9 options), theme mode (3), focus settings (toggles + pickers), detents [60%, 100%]OdakApp/Features/Modals/ShareSheet.swift— card preview with theme/color/shape pickers, ImageRenderer for capture, tilt animation, UIActivityViewControllerOdakApp/Features/Modals/WelcomeSheet.swift— 5 feature cards in 2-column LazyVGrid, CTA button with haptic
Reference files:
app/settings.tsx— all setting rows, accent color options, theme optionsapp/share.tsx— card customization, ViewShot capture, share actionapp/welcome.tsx— 5 feature cards, layout
Phase 8: System Integration
Port existing Swift widget/Live Activity code to use shared OdakKit package.
Files to modify/create:
OdakWidget/— refactor existingtargets/widget/OdakWidget.swiftto import OdakKit for models and storage instead of duplicating codeOdakLiveActivity/— refactor existingios/LiveActivity/*.swiftfiles to import OdakKit, expose ActivityAttributes in shared packageOdakApp/Services/LiveActivityService.swift— portservices/liveActivity/LiveActivityService.tsto Swift ActivityKit callsOdakApp/Services/NotificationService.swift— UNUserNotificationCenter for session completion notifications
Reference files:
targets/widget/OdakWidget.swift— existing widget (22.6KB, already Swift)ios/LiveActivity/*.swift— existing Live Activity (already Swift)services/liveActivity/LiveActivityService.ts— activity lifecycle managementservices/liveActivity/configs.ts— visual configuration for focus/break activities
Phase 9: Polish & Migration
- iOS 26 Liquid Glass conditional modifiers on all cards/headers
- iPad adaptive layouts (
@Environment(\.horizontalSizeClass)) - Accessibility: labels on all interactive elements,
AccessibilityCustomActionfor long-press gestures, reduced motion support - Data migration from RN version: read existing MMKV store + SQLite from app sandbox, copy to App Group container, run
AchievementEngine.recomputeAll()
Verification
- Build:
xcodebuild -scheme Odak -destination 'platform=iOS Simulator,name=iPhone 16 Pro' - All tabs render: Launch app, verify 4 tabs load without crashes
- Focus timer flow: Hold to start → dots count down → break → skip break → idle
- Time screen: Switch between all 7 view types, verify dot counts, pan gesture with haptics
- Dates: Add event with image, verify grid/list views, verify countdown in detail
- Stats: Complete a session, verify it appears in charts and history
- Achievements: Complete sessions to trigger achievements, verify unlock in awards gallery
- Settings: Change accent color and theme, verify immediate update across all screens
- Share: Generate share card, verify ImageRenderer produces correct snapshot
- Widget: Install widget, verify it shows event data from App Group
- Live Activity: Start focus session, verify Dynamic Island countdown appears
- iOS 26 glass: Run on iOS 26 simulator, verify glass effects render; run on iOS 17 simulator, verify blur/solid fallbacks