Refactor StoreKit 2 subscription system and add interactive vote widget
## StoreKit 2 Refactor - Rewrote IAPManager with clean enum-based state model (SubscriptionState) - Added native SubscriptionStoreView for iOS 17+ purchase UI - Subscription status now checked on every app launch - Synced subscription status to UserDefaults for widget access - Simplified PurchaseButtonView and IAPWarningView - Removed unused StatusInfoView ## Interactive Vote Widget - New FeelsVoteWidget with App Intents for mood voting - Subscribers can vote directly from widget, shows stats after voting - Non-subscribers see "Tap to subscribe" which opens subscription store - Added feels:// URL scheme for deep linking ## Firebase Removal - Commented out Firebase imports and initialization - EventLogger now prints to console in DEBUG mode only ## Other Changes - Added fallback for Core Data when App Group unavailable - Added new localization strings for subscription UI - Updated entitlements and Info.plist 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
197
PROJECT_OVERVIEW.md
Normal file
197
PROJECT_OVERVIEW.md
Normal file
@@ -0,0 +1,197 @@
|
||||
# Feels - iOS Mood Tracking App
|
||||
|
||||
## Overview
|
||||
|
||||
**Feels** is a daily mood tracking iOS application that allows users to rate their emotional state each day on a 5-point scale (Horrible, Bad, Average, Good, Great) and visualize patterns over time through multiple view modes.
|
||||
|
||||
## Core Features
|
||||
|
||||
- **Daily Mood Tracking**: Rate your day on a 5-point scale
|
||||
- **Multiple View Modes**: Day (chronological list), Month (calendar grid), Year (aggregate stats)
|
||||
- **Customization**: Themes, color schemes, icon packs, notification personalities
|
||||
- **Widgets**: iOS home screen widgets showing recent moods
|
||||
- **Local Notifications**: Daily reminders with action buttons for quick mood entry
|
||||
- **Data Export/Import**: CSV format for data portability
|
||||
- **Subscription Model**: 30-day free trial, then monthly/yearly subscriptions
|
||||
- **Localization**: English and Spanish
|
||||
|
||||
---
|
||||
|
||||
## Architecture
|
||||
|
||||
**Pattern**: MVVM (Model-View-ViewModel) with SwiftUI
|
||||
|
||||
### Project Structure
|
||||
|
||||
```
|
||||
Feels/
|
||||
├── Shared/ # Core app code
|
||||
│ ├── Models/ # Data models (Mood, Theme, MoodTintable, etc.)
|
||||
│ ├── Views/ # SwiftUI views organized by feature
|
||||
│ │ ├── DayView/ # Chronological list view
|
||||
│ │ ├── MonthView/ # Calendar grid view
|
||||
│ │ ├── YearView/ # Yearly statistics view
|
||||
│ │ ├── CustomizeView/ # Settings and customization
|
||||
│ │ └── Components/ # Reusable UI components
|
||||
│ ├── Persisence/ # Core Data persistence layer
|
||||
│ ├── Onboarding/ # First-run experience
|
||||
│ └── Protocols/ # Protocol definitions
|
||||
├── FeelsWidget/ # iOS Widget Extension (3 widget types)
|
||||
├── en.lproj/ # English localization
|
||||
├── es.lproj/ # Spanish localization
|
||||
└── Tests iOS/ # Test targets
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Key Files
|
||||
|
||||
### App Entry
|
||||
| File | Purpose |
|
||||
|------|---------|
|
||||
| `FeelsApp.swift` | Main app entry, Core Data setup, IAP manager, tab navigation |
|
||||
|
||||
### Data Layer
|
||||
| File | Purpose |
|
||||
|------|---------|
|
||||
| `Feels.xcdatamodeld` | Core Data model with `MoodEntry` entity |
|
||||
| `Persistence.swift` | Core Data stack, App Group container |
|
||||
| `PersistenceGET.swift` | Fetch operations |
|
||||
| `PersistenceADD.swift` | Create/fill missing entries |
|
||||
| `PersistenceUPDATE.swift` | Update operations |
|
||||
| `PersistenceDELETE.swift` | Delete operations |
|
||||
|
||||
### Main Views
|
||||
| File | Purpose |
|
||||
|------|---------|
|
||||
| `MainTabView.swift` | Root navigation (Day, Month, Year, Customize tabs) |
|
||||
| `DayView.swift` | Chronological mood list with edit/delete |
|
||||
| `MonthView.swift` | Calendar grid with color-coded moods |
|
||||
| `YearView.swift` | Aggregate yearly statistics |
|
||||
| `CustomizeView.swift` | Theme, colors, icons, shape settings |
|
||||
|
||||
### Services
|
||||
| File | Purpose |
|
||||
|------|---------|
|
||||
| `IAPManager.swift` | StoreKit 2 subscriptions, 30-day trial |
|
||||
| `LocalNotification.swift` | Daily reminders with quick actions |
|
||||
| `BGTask.swift` | Background task for filling missing dates |
|
||||
| `EventLogger.swift` | Analytics event tracking |
|
||||
|
||||
---
|
||||
|
||||
## Data Models
|
||||
|
||||
### MoodEntry (Core Data Entity)
|
||||
```
|
||||
- moodValue: Int16 (0-4 for mood ratings)
|
||||
- forDate: Date (the day being rated)
|
||||
- timestamp: Date (when entry was created)
|
||||
- weekDay: Int16 (1-7)
|
||||
- canEdit, canDelete: Boolean
|
||||
- entryType: Int16 (header, list, filled-in-missing)
|
||||
```
|
||||
|
||||
### Mood Enum
|
||||
```swift
|
||||
enum Mood: Int {
|
||||
case horrible = 0
|
||||
case bad = 1
|
||||
case average = 2
|
||||
case good = 3
|
||||
case great = 4
|
||||
case missing = 5
|
||||
case placeholder = 6
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Customization System
|
||||
|
||||
### Themes (`Theme.swift`)
|
||||
- System, iFeel (gradient), Dark, Light
|
||||
- Protocol: `Themeable`
|
||||
|
||||
### Color Schemes (`MoodTintable.swift`)
|
||||
- Default, Neon, Pastel, Custom (user-defined)
|
||||
- Each mood has primary and secondary colors
|
||||
|
||||
### Icon Packs (`MoodImagable.swift`)
|
||||
- FontAwesome, Emoji, Hand Emoji
|
||||
- Protocol: `MoodImagable`
|
||||
|
||||
### Shapes
|
||||
- Circle, Square, Diamond for calendar visualization
|
||||
|
||||
### Notification Personalities (`PersonalityPackable.swift`)
|
||||
- "Nice": Friendly text
|
||||
- "Rude": Sarcastic/aggressive text
|
||||
|
||||
---
|
||||
|
||||
## Widget System
|
||||
|
||||
Three widget types in `FeelsWidget/`:
|
||||
1. **FeelsWidget**: Small/Medium/Large showing recent moods
|
||||
2. **FeelsGraphicWidget**: Small widget with mood graphic
|
||||
3. **FeelsIconWidget**: Custom icon widget
|
||||
|
||||
Data shared via App Groups: `group.com.88oakapps.ifeel`
|
||||
|
||||
---
|
||||
|
||||
## Dependencies
|
||||
|
||||
### Apple Frameworks
|
||||
- SwiftUI, CoreData, WidgetKit, StoreKit, UserNotifications, CloudKit, BackgroundTasks
|
||||
|
||||
### Third-Party
|
||||
- CloudKitSyncMonitor
|
||||
- Firebase (GoogleService-Info.plist)
|
||||
|
||||
---
|
||||
|
||||
## Configuration
|
||||
|
||||
### App Groups
|
||||
- Production: `group.com.88oakapps.ifeel`
|
||||
- Debug: `group.com.88oakapps.ifeelDebug`
|
||||
|
||||
### Background Tasks
|
||||
- Identifier: `com.88oak.Feels.dbUpdateMissing`
|
||||
|
||||
### StoreKit Products
|
||||
- Monthly subscription
|
||||
- Yearly subscription
|
||||
- 30-day free trial
|
||||
|
||||
---
|
||||
|
||||
## Data Flow
|
||||
|
||||
```
|
||||
User Input → Persistence (Core Data) → ViewModel Update → SwiftUI Re-render
|
||||
↓
|
||||
Widget Update (WidgetCenter.shared.reloadAllTimelines())
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Localization
|
||||
|
||||
| Language | File |
|
||||
|----------|------|
|
||||
| English | `en.lproj/Localizable.strings` |
|
||||
| Spanish | `es.lproj/Localizable.strings` |
|
||||
|
||||
Covers: Onboarding, mood names, UI labels, notifications, settings
|
||||
|
||||
---
|
||||
|
||||
## Notable Implementation Details
|
||||
|
||||
1. **Automatic Missing Date Fill**: Creates placeholder entries for days without ratings
|
||||
2. **Entry Types**: `header`, `listView`, `filledInMissing` - tracks how entries were created
|
||||
3. **Day Filtering**: Users can filter which weekdays appear in visualizations
|
||||
4. **CSV Export**: Full data portability with all metadata preserved
|
||||
Reference in New Issue
Block a user