Covers every feature with how-it-works, selling-point framing, and clickable source links — targeted at both developers and business partners. Includes an in-depth section on the guided reflection flow (Socratic templating, cognitive-distortion routing, evidence step). Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Reflect
A private, on-device iOS mood tracker that turns a five-second daily check-in into durable self-knowledge. Users rate their day on a 5-point scale, and the app turns that signal into calendars, trends, Apple-Intelligence-powered insights, guided CBT reflections, watch complications, widgets, Live Activities, and shareable summaries.
Platform: iOS 16+ · iPadOS · watchOS · macOS (Catalyst) Language: Swift / SwiftUI Data: SwiftData + CloudKit (private database) AI: Apple Foundation Models (on-device, iOS 26+) Monetization: 30-day free trial → monthly / yearly subscription (StoreKit 2)
Table of Contents
- Elevator pitch
- Feature map
- Core features in depth
- Daily mood logging
- Day / Month / Year views
- Insights (AI)
- Weekly Digest (AI)
- Auto-tagging (AI)
- AI Reports + PDF export
- Guided Reflection (CBT / ACT / BA)
- Widgets
- Live Activities
- Watch app + complications
- Control Center, Siri, App Intents, Shortcuts
- HealthKit State of Mind
- WeatherKit correlation
- Photos on entries
- Face ID / Touch ID lock
- Sharing templates
- Customization
- Notifications + personality packs
- Export / Import
- Subscriptions
- Guided Reflection — deep dive
- Architecture
- Data model
- Directory map
- Build, run, test
- Localization
- Privacy & security
- Configuration reference
Elevator pitch
Most mood trackers collect data. Reflect converts it into insight. The five-second daily tap is only the entry point — behind it sits a full behavioral-health stack:
- On-device Apple Intelligence (Foundation Models) writes your weekly digest, surfaces patterns, and runs a cognitive-behavioral-therapy-aligned reflection flow. Nothing leaves the phone.
- CloudKit sync keeps iPhone and Apple Watch in step without an account.
- HealthKit State of Mind writes your rating back into Apple Health and reads activity / sleep / HRV so the app can correlate mood with the rest of your life.
- A CBT-aligned Guided Reflection detects cognitive distortions in your automatic thoughts and rewrites the follow-up question to match the specific distortion — the actual mechanism of change in evidence-based CBT.
- Four surfaces to log from: app, home/lock-screen widget, Control Center widget, Apple Watch complication. One tap, anywhere.
The thesis: a mood tracker is only useful if the patterns it surfaces can drive behavior change. Reflect is designed from the top down around the CBT/ACT evidence base, not screen time.
Feature map
| Area | What it is | Why it sells | Source |
|---|---|---|---|
| 5-point mood log | Horrible → Great daily rating | The lowest-friction signal that still captures variance | Shared/Models/Mood.swift |
| Day view | Chronological list with notes, photos, weather | Diary + heatmap in one scroll | Shared/Views/DayView/ |
| Month view | Calendar heatmap | "What kind of month was this?" in one glance | Shared/Views/MonthView/ |
| Year view | 12-month heatmap + filters | See multi-month seasonality | Shared/Views/YearView/ |
| AI Insights | Patterns, predictions, advice | Turns data into behavior change | Shared/Views/InsightsView/ · FoundationModelsInsightService.swift |
| Weekly Digest | AI-written summary of the last 7 days | Sunday-night ritual, builds habit | FoundationModelsDigestService.swift |
| Auto-tagging | AI extracts themes (work, sleep, family…) | Enables mood↔theme correlation | FoundationModelsTagService.swift |
| AI Reports + PDF | Clinical multi-week reports | Shareable with therapist | ReportPDFGenerator.swift |
| Guided Reflection | CBT Thought Record / ACT Defusion / Behavioral Activation | Evidence-based, not a mood journal | GuidedReflectionView.swift · GuidedReflection.swift |
| Cognitive distortion detector | Routes reframes to specific distortion | The mechanism of change in CBT | CognitiveDistortionDetector.swift |
| Widgets | 6 widget families | Logging from the home screen | ReflectWidget/ |
| Live Activities | Lock Screen + Dynamic Island streak | Keeps the habit present | ReflectLiveActivity.swift · MoodStreakActivity.swift |
| Watch app | Log and view on wrist | Frictionless logging | Reflect Watch App/ |
| Complications | Streak / last mood on watch face | Ambient nudge to log | ReflectComplication.swift |
| Control Center | iOS 18 Control Center widget | One-tap log from anywhere | ReflectMoodControlWidget.swift |
| Siri / Shortcuts | App Intents for "Log mood great" | Hands-free logging | AppShortcuts.swift · SharedMoodIntent.swift |
| HealthKit | State of Mind write + activity read | Deep Apple Health integration | HealthKitManager.swift · HealthService.swift |
| WeatherKit | Attach weather to each entry | Mood↔weather correlation in Insights | WeatherManager.swift |
| Photos | Attach a photo per day | Visual journal | PhotoManager.swift |
| Biometric lock | Face ID / Touch ID / Optic ID | Private data stays private | BiometricAuthManager.swift |
| Sharing templates | Mood-style social cards | Organic growth channel | Shared/Views/Sharing/ · Shared/Views/SharingTemplates/ |
| Custom icon widget | User-designed widget layout | Personalization as retention | Shared/Views/CustomWidget/ |
| Theming | Color sets, icon packs, shapes, personality | Make it feel like yours | Shared/Views/CustomizeView/ |
| Export | CSV / JSON / PDF | No lock-in — builds trust | ExportService.swift |
| Subscription | Monthly / yearly + 30-day trial | StoreKit 2, offer codes | IAPManager.swift |
Core features in depth
1. Daily mood logging
- What: A 5-point ordinal scale — Horrible, Bad, Average, Good, Great — defined in
Shared/Models/Mood.swift. - How: Every mood entry — from the app, a widget, the watch, Siri, the lock-screen Live Activity, a push-notification action, or an imported CSV row — funnels through a single choke point:
MoodLogger.shared.logMood(...). That one method runs the write to SwiftData, the HealthKit sync, streak recomputation, widget-timeline reload, Watch Connectivity nudge, TipKit parameter update, Live Activity refresh, and analytics event. - Why 5 points: Enough to capture variance, few enough to fit on a widget. The ordinal scale is what lets us run
MoodMetrics.swiftover it without arguing about arithmetic on emoji. - Source-of-truth rule: never insert into
modelContextdirectly — always go throughMoodLogger, or side effects silently break. This invariant is enforced inCLAUDE.md.
2. Day / Month / Year views
Three zoom levels over the same data, selected via the tab bar in MainTabView.swift.
- Day — Chronological list of entries with notes, photo, weather card, and the "open reflection" CTA. Backed by
DayViewViewModel. - Month — Classic calendar grid, each cell tinted by its mood. Tap to drill into that date. Shape is user-configurable (circle / square / rounded / diamond).
- Year — 12 mini calendars stacked for seasonality. Users can filter by weekday (e.g. "only show Mondays") to isolate work-week patterns.
3. Insights (AI)
- What: An on-device AI pass over your mood history that returns a short, human-readable list of patterns, predictions, advice, and achievements, tinted to the dominant mood.
- How:
FoundationModelsInsightService.swiftuses Apple Foundation Models (LanguageModelSessionwith a@Generableschema defined inAIInsight.swift). The prompt is built byMoodDataSummarizer.swift, which precomputes mood distribution, day-of-week patterns, streaks, weather correlations, and HealthKit correlations so the model only has to narrate facts we already have. - Caching: 1-hour result cache — regenerating is one tap.
- Selling point: Zero network, zero account, zero data leaves the device.
4. Weekly Digest (AI)
- What: Every week the app writes you a one-paragraph summary with a headline, a highlight ("your best day was Thursday — walk after work"), and an intention for next week.
- How:
FoundationModelsDigestService.swiftgenerates against theAIWeeklyDigestschema. Scheduled byBGTask.runWeeklyDigestTaskso it's ready on Sunday night without the user opening the app. Delivered via a local notification; rendered inWeeklyDigestCardView.swift. - Why it sells: A weekly "here's what happened" ritual is how habits become durable.
5. Auto-tagging (AI)
- What: Themes like work / sleep / family / exercise / stress are extracted automatically from each entry's notes and reflection.
- How:
FoundationModelsTagService.swiftreturns anAIEntryTags. Insights cross-reference the tag with the mood to answer questions like "what happens to your mood on days you write about sleep?". - Why it sells: Users don't have to tag manually — the signal is free.
6. AI Reports + PDF export
- What: A multi-week clinical-style report with overview stats, week-by-week breakdowns, and visualizations, exportable as a PDF to share with a therapist or doctor.
- How:
ReportPDFGenerator.swiftrenders an HTML template through WKWebView and rasterizes to PDF. Data shape isAIReport; UI lives inShared/Views/InsightsView/ReportsView.swift. - Why it sells: Unlocks clinical use cases — a real moat vs. tap-to-log competitors.
7. Guided Reflection — deep dive
See the dedicated Guided Reflection section below.
8. Widgets
All six live in ReflectWidget/ and are bundled by WidgetBundle.swift.
| Widget | File | Purpose |
|---|---|---|
| Vote | ReflectVoteWidget.swift |
Interactive 5-button logger — small/medium, no app launch |
| Timeline | ReflectTimelineWidget.swift |
Recent-days strip; pre-vote shows buttons, post-vote shows stats |
| Graphic | ReflectGraphicWidget.swift |
Large mood art widget |
| Icon | ReflectIconWidget.swift |
User-customized widget (see CustomWidget) |
| Mood Control | ReflectMoodControlWidget.swift |
Control Center widget — iOS 18 |
| Live Activity | ReflectLiveActivity.swift |
Lock Screen + Dynamic Island |
Widgets can't reach CloudKit or HealthKit from their process, so they read through ExtensionDataProvider.swift, which talks to the App Group container directly. When the widget writes a vote via an AppIntent, the main app picks it up and runs the deferred MoodLogger side effects next launch.
9. Live Activities
- What: A streak card on the Lock Screen and in the Dynamic Island, updating in real time when you log or when the voting window opens.
- How:
MoodStreakActivity.swiftstarts theActivityKitactivity;ReflectLiveActivity.swiftis the view.LiveActivitySchedulerdecides when the activity should be on-screen (typically after the daily reminder fires until the day is logged). - Why it sells: Continuous visible nudge without being a notification — drives logging consistency.
10. Watch app + complications
Reflect Watch App/ContentView.swift— Full voting UI on the wrist. Writes go through the shared CloudKit database; UI refresh for the phone happens overWatchConnectivityManager.ReflectComplication.swift— Circular / rectangular / corner complications showing streak, last mood, or graphic.- Selling point: the watch is the lowest-friction surface — users raise their wrist, tap, done.
11. Control Center, Siri, App Intents, Shortcuts
AppShortcuts.swift— Exposes "Log a mood" and "Open Reflect" as App Intents so Siri and Shortcuts can call them.SharedMoodIntent.swift— The intent shared across widget buttons and voice actions.ReflectMoodControlWidget.swift— iOS 18 Control Center widget that opens the app pre-scrolled to the log screen.
12. HealthKit State of Mind
HealthKitManager.swift— Writes each mood into Apple Health's State of Mind store (valence mapped from the 5-point scale).HealthService.swift— Reads activity, exercise time, heart rate, HRV, and sleep and folds them into the Insights prompt for mood↔body correlation.- Why it sells: Reflect becomes the primary State-of-Mind logger for users already bought into Apple Health.
13. WeatherKit correlation
WeatherManager.swift— Pulls current conditions from WeatherKit using a one-shot location fromLocationManager.swift(10-min cache, 15 s timeout) and attaches aWeatherDatasnapshot to the entry.- Retry: failures queue for
BGTask.runWeatherRetryTaskso weather fills in overnight.
14. Photos on entries
PhotoManager.swift— JPEG at 0.8, stored in the App Group container, 200×200 thumbnail cached for list rendering.- Picker:
PhotoPickerView.swift/ImagePickerGridView.swift.
15. Face ID / Touch ID lock
BiometricAuthManager.swift— Detects Face ID / Touch ID / Optic ID, gracefully falls back to the device passcode, and gates the whole app behind a biometric unlock if the user opts in.
16. Sharing templates
Shared/Views/Sharing/— The sharing flow.Shared/Views/SharingTemplates/— Visual templates (monthly recap, streak brag, mood pie).SharingScreenshotExporter.swift— Debug tool that renders every template for marketing.- Why it sells: Shareable cards are a zero-cost organic-growth channel — each user who shares is a free ad.
17. Customization
Shared/Views/CustomizeView/ lets users pick:
| Axis | Protocol | Options |
|---|---|---|
| Color set | MoodTintable |
Default, Pastel, Neon, Ocean, Forest, Sunset |
| Icon / emoji | MoodImagable |
FontAwesome, Emoji, Hand Emoji |
| Shape | Shapes.swift |
Circle, Square, Diamond, Rounded |
| Theme | Theme.swift |
System / Light / Dark / iFeel gradient |
| Personality | PersonalityPackable |
Default, Coach, Zen, Best Friend, Data Analyst |
| App icon | Shared/Views/CustomIcon/ |
Multiple home-screen icons |
18. Notifications + personality packs
LocalNotification.swift— Daily reminder with inline mood buttons; notification copy is written by the selectedPersonalityPackableso the "Nice" pack asks gently and the "Rude" pack heckles you.AppDelegate.swift— Handles the action buttons on the notification to route the vote throughMoodLogger.
19. Export / Import
ExportService.swift— CSV, JSON, and PDF export of the full entry history with notes, weather, reflections, photos, and entry source preserved.- Why it sells: No lock-in. Users know their data can leave, which makes them trust it going in.
20. Subscriptions
IAPManager.swift— StoreKit 2, monthly (com.88oakapps.reflect.IAP.subscriptions.monthly) and yearly, 30-day free trial counted fromfirstLaunchDate, paywall UI inReflectSubscriptionStoreView.swift.- Gated behind paywall: AI Insights, Weekly Digest, AI Reports, Guided Reflection AI feedback, HealthKit correlation insights, Apple Watch premium complications. Core logging + history are free forever.
Guided Reflection — deep dive
Source files: — Model:
Shared/Models/GuidedReflection.swift— Distortion detector:Shared/Services/CognitiveDistortionDetector.swift— AI feedback:Shared/Services/FoundationModelsReflectionService.swift— Feedback model:Shared/Models/AIReflectionFeedback.swift— UI:Shared/Views/GuidedReflectionView.swift— Feedback UI:Shared/Views/ReflectionFeedbackView.swift— Info sheet:Shared/Views/GuidedReflectionInfoView.swift— Plan doc:docs/guided-reflection-cbt-plan.md— Flowchart:docs/guided-reflection-flowchart.html
Why this exists
Most mood trackers stop at logging. Reflect's reflection flow is a short, mood-adaptive, CBT-aligned guided exercise. The goal is not to make the user journal more — it's to run a known therapeutic mechanism that has evidence for changing the relationship with a thought.
Three mood tiers → three different evidence-based frameworks:
| Mood | Framework | Mechanism | Question count |
|---|---|---|---|
| Great / Good | Behavioral Activation (BA) | Savor + plan to repeat | 3 |
| Average | ACT Cognitive Defusion | Notice thought, loosen its grip, re-orient to values | 4 |
| Bad / Horrible | CBT Thought Record | Identify situation, surface automatic thought, check for distortion, examine evidence, generate a balanced reframe | 5 |
These are routed from the mood value via MoodCategory(from:) and rendered as a stepped sheet from GuidedReflectionView.swift.
The Socratic template system
The defining feature of Socratic questioning in CBT is that each question builds on the user's previous answer. Static question lists are not Socratic. We implemented this with QuestionTemplate — each question has a text with an optional %@ placeholder and a placeholderRef: Int? pointing at which earlier question's answer to inject.
Example (negative path):
Q1: "What happened today that affected your mood?"
→ user: "My boss criticized my presentation in front of the team"
Q2: "What thought kept coming back about 'My boss criticized my presentation…'?"
→ user: "I'm not cut out for this job"
Q3 (distortion-specific, see below): "Is 'I'm not cut out for this job' something you are, or something you did?"
Q4 (evidence): "What evidence supports 'I'm not cut out for this job', and what challenges it?"
Q5 (reframe): "Looking at 'I'm not cut out for this job' again — what's a more balanced way to see it?"
Injections are truncated to a sentence boundary or 60 characters by GuidedReflection.truncatedForInjection so long answers never break grammar. Templates are fully localized — each language controls its own %@ position so grammar stays natural.
Cognitive distortion detection
CognitiveDistortionDetector.swift classifies the Q2 answer into one of seven types using localized keyword lists (deterministic, offline, privacy-preserving — not ML). This was a deliberate product choice: rule-based is inspectable, predictable, and works with zero latency.
| Distortion | Example phrasing | Q3 reframe prompt |
|---|---|---|
overgeneralization |
"always", "never", "everyone" | "Can you think of one counter-example to '%@'?" |
shouldStatement |
"should", "must", "have to" | "Where did the rule 'I should …' come from? Is it still serving you?" |
labeling |
"I am [trait]" | "Is '%@' something you are, or something you did?" |
personalization |
"my fault", "because of me" | "What other factors, besides you, contributed to this?" |
catastrophizing |
"ruined", "can't recover" | "What's the worst case? What's the most likely case?" |
mindReading |
"thinks I'm", "hates me" | "What evidence do you have for that interpretation?" |
unknown |
(no match) | Falls back to the generic perspective check |
Keywords live in Localizable.xcstrings under distortion_*_keywords keys so each locale tunes its own detection rules. Priority order is specific → general so that "I always ruin everything" classifies as catastrophizing first, overgeneralization second.
Evidence examination
The negative path explicitly inserts a dedicated evidence step (Q4) — supporting and contradicting evidence both. This is the single most load-bearing step in a real CBT Thought Record and was previously missing.
Intensity tracking
GuidedReflection carries optional preIntensity and postIntensity (0–10). CBT literature emphasizes measuring the emotion's intensity before and after the thought work — the delta is the efficacy signal. The AI feedback stage references the shift ("you moved from an 8 to a 5") when it narrates the reflection.
AI feedback stage
On completion FoundationModelsReflectionService generates an AIReflectionFeedback with three slots — affirmation, observation, takeaway — rendered by ReflectionFeedbackView.swift. Tone is driven by the user's selected PersonalityPackable (Coach / Zen / Best Friend / Data Analyst / Default). This is gated behind the subscription + iOS 26 Apple Intelligence — when unavailable, the reflection still saves normally and the feedback view degrades gracefully.
Back-compat
Old reflections saved with 4 negative-path responses (pre-evidence step) still decode and still count as complete. GuidedReflection.isComplete detects the legacy shape so users never lose completed work across updates.
Full flow diagram
Open docs/guided-reflection-flowchart.html in a browser for the visual flow. The plan doc in docs/guided-reflection-cbt-plan.md walks through the phased rationale — phase 1 (templates + intensity), phase 2 (distortion detection + evidence), phase 3 (AI-generated final question).
Architecture
Pattern: MVVM + SwiftUI, singletons for cross-cutting concerns.
Entry point: Shared/ReflectApp.swift
Layers
┌─────────────────────────────────────────────────────────────────┐
│ Views (SwiftUI) Shared/Views/ │
│ ViewModels (@MainActor) colocated with views │
├─────────────────────────────────────────────────────────────────┤
│ Services (singletons) Shared/Services/ │
│ MoodLogger AnalyticsManager IAPManager │
│ HealthKitManager WeatherManager LocationManager │
│ FoundationModels{Insight,Digest,Reflection,Tag}Service │
│ CognitiveDistortionDetector │
├─────────────────────────────────────────────────────────────────┤
│ Persistence Shared/Persisence/ │
│ DataController (GET/ADD/UPDATE/DELETE) │
│ SharedModelContainer — SwiftData + CloudKit + App Group │
│ ExtensionDataProvider — widget/watch read path │
├─────────────────────────────────────────────────────────────────┤
│ Models Shared/Models/ │
│ MoodEntryModel (@Model) Mood GuidedReflection │
│ AIInsight AIWeeklyDigest AIReflectionFeedback AIReport │
└─────────────────────────────────────────────────────────────────┘
Data flow for a single mood log
tap → DayViewViewModel.add()
└─► MoodLogger.shared.logMood() ← ALL entry points converge here
├─► DataController.shared.add() — SwiftData insert + save
├─► HealthKitManager.write() — State of Mind
├─► streak recompute
├─► WidgetCenter.reloadAllTimelines()
├─► WatchConnectivityManager.nudge()
├─► LiveActivityScheduler.update()
├─► TipKit parameter update
└─► AnalyticsManager.shared.track()
Widgets and the watch bypass MoodLogger when the main app isn't running and write through ExtensionDataProvider; MoodLogger.applySideEffects() catches up on next launch. Detailed rules: CLAUDE.md.
Data model
Primary entity: MoodEntryModel — a SwiftData @Model with:
| Field | Type | Notes |
|---|---|---|
moodValue |
Int | 0–4 (Horrible → Great), 5 = missing, 6 = placeholder |
forDate |
Date | The logical day being rated |
timestamp |
Date | When the entry was written |
weekDay |
Int | 1–7, denormalized for fast filtering |
entryType |
Int | listView / widget / watch / shortcut / filledInMissing / notification / header / siri / controlCenter / liveActivity |
notes |
String? | Free text |
photoID |
String? | Path into App Group container |
weatherJSON |
String? | WeatherData |
reflectionJSON |
String? | GuidedReflection |
tagsJSON |
String? | AIEntryTags |
All fields have defaults — required by CloudKit. Sync is automatic via SwiftData's built-in CloudKit integration.
Directory map
| Path | Contents |
|---|---|
Shared/ |
All cross-platform code |
Shared/Models/ |
Domain types, SwiftData models, AI schemas |
Shared/Views/ |
SwiftUI views, grouped by feature |
Shared/Services/ |
Singletons for AI, HealthKit, Weather, Export, etc. |
Shared/Persisence/ |
SwiftData layer (note: directory name has a typo — intentional, for historic reasons) |
Shared/Onboarding/ |
First-launch flow |
Shared/Utilities/ |
Small helpers |
ReflectWidget/ |
Widget + Control Center + Live Activity extension |
Reflect Watch App/ |
watchOS app + complications |
Reflect/ |
iOS app target assets, Info.plist, Localizable.xcstrings |
Tests iOS/ |
XCUITest suites — see docs/XCUITest-Authoring.md |
ReflectTests/ |
XCTest unit tests |
Tests macOS/ |
macOS target tests |
landing_page/ |
Marketing site |
docs/ |
Design docs, ASO, QA plan, CBT plan, competitors |
scripts/ |
Build/dev utilities |
ads/ |
Marketing creative |
Build, run, test
# Build
xcodebuild -project Reflect.xcodeproj \
-scheme "Reflect (iOS)" \
-destination 'platform=iOS Simulator,name=iPhone 17 Pro' build
# Run all tests
xcodebuild -project Reflect.xcodeproj \
-scheme "Reflect (iOS)" \
-destination 'platform=iOS Simulator,name=iPhone 17 Pro' test
# Run one UI suite
xcodebuild -project Reflect.xcodeproj \
-scheme "Reflect (iOS)" \
-destination 'platform=iOS Simulator,name=iPhone 17 Pro' \
-only-testing:"Tests iOS/Tests_iOS" test
UI test conventions are mandatory — see docs/XCUITest-Authoring.md, Tests iOS/Helpers/BaseUITestCase.swift, and Shared/AccessibilityIdentifiers.swift.
Localization
Format: String Catalog at Reflect/Localizable.xcstrings.
Shipping languages: English · German · Spanish · French · Japanese · Korean · Portuguese (Brazil).
All user-facing strings use String(localized:). "Reflect" is a brand name — untranslated. Distortion-detection keywords are per-locale (see Cognitive distortion detection).
Privacy & security
- No account, no backend, no third-party server ever sees mood data. Data lives in SwiftData, synced through the user's own private CloudKit database.
- AI is 100% on-device — Foundation Models runs locally; nothing is sent to Anthropic, OpenAI, Apple servers, or us.
- Analytics (PostHog via
AnalyticsManager) captures UI events, never mood content, and the user can opt out in settings. - Optional biometric lock (
BiometricAuthManager) gates the app behind Face ID / Touch ID / Optic ID. - HealthKit read/write is permission-gated and only triggered on explicit user opt-in.
Configuration reference
| Setting | Value |
|---|---|
| Bundle ID (iOS) | com.88oakapps.reflect |
| App Group (prod) | group.com.88oakapps.reflect |
| App Group (debug) | group.com.88oakapps.reflect.debug |
| CloudKit (prod) | iCloud.com.88oakapps.reflect |
| CloudKit (debug) | iCloud.com.88oakapps.reflect.debug |
| Subscription group | 21951685 |
| Monthly product ID | com.88oakapps.reflect.IAP.subscriptions.monthly |
| Yearly product ID | com.88oakapps.reflect.IAP.subscriptions.yearly |
| Free trial | 30 days from firstLaunchDate |
| URL scheme | reflect:// (e.g. reflect://subscribe) |
| BGTask — missing dates | com.88oakapps.reflect.dbUpdateMissing |
| BGTask — weather retry | com.88oakapps.reflect.weatherRetry |
| BGTask — weekly digest | com.88oakapps.reflect.weeklyDigest |
| Logger subsystem | com.88oakapps.reflect |
For deeper architectural rules, concurrency constraints, data-access invariants, and edge-case gotchas, see CLAUDE.md. For ASO strategy and screenshot plans see docs/.