docs(readme): update with MVP scope, both platforms, iOS port status
- Add Closer tagline + private-space positioning at top - Add explicit anti-scope (not social, not therapy, not productivity) - Add Platform Status table (Android active, iOS in progress, backend shared) - Add MVP scope split: Free / Premium / Out-of-Scope - Add iOS folder layout with each layer's purpose - Add iOS build commands (xcodegen + xcodebuild) - Expand privacy section (App Check, server-verified subscriptions, E2EE note) - Clear MVP status breakdown: complete / in-progress / release-prep - Link HISTORY.md and PROJECT.md for deeper context
This commit is contained in:
parent
bdd2bf27c0
commit
d5a17cc90d
275
README.md
275
README.md
|
|
@ -1,8 +1,14 @@
|
||||||
# Closer
|
# Closer
|
||||||
|
|
||||||
Closer is a private Android app for couples who want a steadier way to talk, reflect, and make time for each other. It turns relationship check-ins into small, intentional rituals: one daily question, curated conversation packs, private answers, partner reveal flows, reminders, and date-planning tools.
|
> *A private space for two people to talk, reflect, and stay close.*
|
||||||
|
|
||||||
The product is built as a native Kotlin app with Jetpack Compose and Firebase-backed sync. It is not a social network, not a therapy replacement, and not a productivity tracker. The core loop is simple: answer honestly, choose what to reveal, and keep a record of the conversations that mattered.
|
Closer is a **couples relationship app** built for partners who want a steadier way to talk, reflect, and make time for each other. It turns relationship check-ins into small, intentional rituals — one daily question, curated conversation packs, private answers, partner reveal flows, gentle reminders, and shared date planning.
|
||||||
|
|
||||||
|
The app is **not** a social network, **not** a therapy replacement, and **not** a productivity tracker. There are no public feeds, no likes, no followers, and no infinite scroll. The core loop is simple: **answer honestly, choose what to reveal, and keep a record of the conversations that mattered.**
|
||||||
|
|
||||||
|
Closer is built as native apps on **Android (Kotlin / Jetpack Compose)** and **iOS (SwiftUI)**, both backed by the same Firebase stack and shared data model.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
## Screenshots
|
## Screenshots
|
||||||
|
|
||||||
|
|
@ -24,87 +30,188 @@ The product is built as a native Kotlin app with Jetpack Compose and Firebase-ba
|
||||||
<img src="docs/screenshots/02-login.png" alt="Closer login screen" width="180" />
|
<img src="docs/screenshots/02-login.png" alt="Closer login screen" width="180" />
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
## What The App Does
|
---
|
||||||
|
|
||||||
Closer gives couples a shared space for guided connection:
|
## What Closer Does
|
||||||
|
|
||||||
- **Daily question**: one prompt at a time, with written, scale, choice, and this-or-that answer modes.
|
Closer gives couples a private, shared space for guided connection:
|
||||||
- **Private-first answers**: each person can answer privately before deciding whether to reveal or discuss.
|
|
||||||
- **Question packs**: 22 curated categories across 6,000+ bundled prompts, including communication, conflict, trust, money, stress, intimacy, marriage, parenting, and date night.
|
- **Daily question** — one prompt a day, with written, scale, multiple-choice, and this-or-that answer modes.
|
||||||
- **Answer history**: local answer storage, review flows, delete controls, and partner reveal support.
|
- **Private-first answers** — each partner answers independently before deciding whether to reveal or discuss.
|
||||||
- **Discussion threads**: question-specific conversation threads and reactions for follow-up.
|
- **Question packs** — 22+ curated categories spanning 6,000+ bundled prompts (communication, conflict, trust, intimacy, parenting, marriage, money, stress, date night, and more).
|
||||||
- **Partner pairing**: invite-code pairing, email invite flow, and partner-aware home states.
|
- **Answer history** — review past questions and answers, delete controls, and partner reveal support.
|
||||||
- **Spin wheel**: category-based random questions for date nights, long drives, and low-pressure conversations.
|
- **Discussion threads** — question-specific conversation threads and reactions for follow-up.
|
||||||
- **Date tools**: date matching, date planning preferences, and a shared bucket list foundation.
|
- **Partner pairing** — 6-character invite code, email invite flow, and partner-aware home states.
|
||||||
- **Settings and privacy**: account, notification, subscription, relationship, privacy, and deletion screens.
|
- **Spin the wheel** — category-based random questions for date nights, road trips, and low-pressure moments.
|
||||||
- **Subscriptions**: free and premium access paths powered by RevenueCat and Google Play Billing.
|
- **Date tools** — swipe-to-match date ideas, date planning preferences, and a shared bucket list.
|
||||||
|
- **Settings & privacy** — account, notifications, appearance, subscription, relationship, privacy, and account deletion flows.
|
||||||
|
- **Subscriptions** — free and premium tiers powered by RevenueCat (Google Play Billing on Android, StoreKit on iOS).
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
## Product Shape
|
## Product Shape
|
||||||
|
|
||||||
Closer is optimized for short, meaningful sessions rather than endless engagement.
|
Closer is optimized for **short, meaningful sessions** rather than endless engagement.
|
||||||
|
|
||||||
| Area | Product Behavior |
|
| Area | Behavior |
|
||||||
| --- | --- |
|
| --- | --- |
|
||||||
| Home | Shows the next best action based on pairing, daily question state, saved answers, and streaks. |
|
| Home | Surfaces the next best action — daily question, partner activity, streaks, saved answers |
|
||||||
| Questions | Uses local bundled content so the app remains fast and usable without waiting on the network. |
|
| Questions | Local-bundled content so the app stays fast and usable without waiting on the network |
|
||||||
| Partner Sync | Uses Firebase Auth and Firestore to isolate user, couple, invite, thread, and entitlement data. |
|
| Partner Sync | Firebase Auth + Firestore isolate user, couple, invite, thread, and entitlement data per couple |
|
||||||
| Reminders | Uses FCM plus local notification preferences and quiet-hour controls. |
|
| Reminders | FCM + local notification preferences and quiet-hour controls |
|
||||||
| Premium | Unlocks deeper packs, wheel history, expanded answer history, and future advanced suggestions. |
|
| Premium | Unlocks deeper packs, wheel session history, full answer history, and advanced suggestion features |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Platform Status
|
||||||
|
|
||||||
|
| Platform | Stack | Status |
|
||||||
|
| --- | --- | --- |
|
||||||
|
| **Android** | Kotlin · Jetpack Compose · Material 3 · Hilt · Room · DataStore | **Active development** — feature-complete MVP |
|
||||||
|
| **iOS** | SwiftUI · MVVM · async/await · Firebase iOS SDK | **In progress** — full scaffold + screen parity landed on `dev` branch |
|
||||||
|
| **Backend** | Firebase Auth · Firestore · Cloud Functions (TypeScript) · FCM · App Check | **Shared source of truth** for both platforms |
|
||||||
|
| **Billing** | RevenueCat · Google Play Billing · StoreKit | Server-verified entitlements via Cloud Function webhook |
|
||||||
|
|
||||||
|
The Android app is the **reference implementation** — the iOS port is built to mirror it screen-for-screen and consume the same backend.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## MVP Feature Scope
|
||||||
|
|
||||||
|
### Free Tier
|
||||||
|
- Anonymous trial → email or Google sign-up
|
||||||
|
- 6-character invite code pairing (with email invite fallback)
|
||||||
|
- Daily question with full answer modes (text, scale, multiple choice, this-or-that)
|
||||||
|
- Private answer reveal flow once both partners have answered
|
||||||
|
- Limited spin wheel sessions and basic question packs
|
||||||
|
- Recent answer history (last 30 days)
|
||||||
|
- Push reminders with quiet-hour support
|
||||||
|
- Account deletion and data export
|
||||||
|
|
||||||
|
### Premium Tier (via RevenueCat)
|
||||||
|
- Unlimited daily questions and premium question packs
|
||||||
|
- Full answer history and saved spin wheel session history
|
||||||
|
- Desire Sync (preferences alignment exercise)
|
||||||
|
- Connection Challenges (multi-day programs)
|
||||||
|
- Memory Lane (time capsules)
|
||||||
|
- Advanced analytics and relationship insights
|
||||||
|
- Priority support
|
||||||
|
|
||||||
|
### Out of Scope (Future)
|
||||||
|
- AI-assisted question suggestions
|
||||||
|
- Native group/relationship types beyond dyadic couples
|
||||||
|
- Wearable (Wear OS / watchOS) companions
|
||||||
|
- Live video or voice sessions
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
## Tech Stack
|
## Tech Stack
|
||||||
|
|
||||||
|
### Android
|
||||||
| Layer | Stack |
|
| Layer | Stack |
|
||||||
| --- | --- |
|
| --- | --- |
|
||||||
| Android | Kotlin, Jetpack Compose, Material 3, Navigation Compose |
|
| Language | Kotlin 2.x |
|
||||||
| Architecture | Clean architecture split across `core`, `data`, `domain`, and `ui` |
|
| UI | Jetpack Compose, Material 3, Navigation Compose |
|
||||||
| State | ViewModel, Kotlin Coroutines, Kotlin Flow |
|
| Architecture | Clean architecture — `core/` · `data/` · `domain/` · `ui/` |
|
||||||
|
| State | ViewModel · Kotlin Coroutines · Kotlin Flow |
|
||||||
| Dependency Injection | Hilt |
|
| Dependency Injection | Hilt |
|
||||||
| Local Data | Room, DataStore Preferences, bundled SQLite seed database |
|
| Local Data | Room · DataStore Preferences · bundled SQLite seed |
|
||||||
| Backend | Firebase Auth, Firestore, Cloud Functions, FCM |
|
| SDK | min 26 · target 35 · compile 35 |
|
||||||
| Observability | Firebase Analytics, Crashlytics |
|
|
||||||
| Billing | RevenueCat, Google Play Billing |
|
### iOS
|
||||||
| Security | Firebase App Check, Play Integrity |
|
| Layer | Stack |
|
||||||
| Backend Services | TypeScript Cloud Functions and an optional Express webhook service |
|
| --- | --- |
|
||||||
| Android SDK | min 26, target 35, compile 35 |
|
| Language | Swift 5.9 |
|
||||||
|
| UI | SwiftUI · NavigationStack · TabView |
|
||||||
|
| Architecture | MVVM · `AppState` ObservableObject · `EnvironmentObject` |
|
||||||
|
| Concurrency | async/await |
|
||||||
|
| Dependency Management | Swift Package Manager · XcodeGen (`project.yml`) |
|
||||||
|
| SDK | iOS 17+ |
|
||||||
|
|
||||||
|
### Backend (Shared)
|
||||||
|
| Layer | Stack |
|
||||||
|
| --- | --- |
|
||||||
|
| Auth | Firebase Authentication (anonymous, email/password, Google) |
|
||||||
|
| Database | Cloud Firestore |
|
||||||
|
| Server Logic | Firebase Cloud Functions (TypeScript) |
|
||||||
|
| Push | Firebase Cloud Messaging (FCM) |
|
||||||
|
| Security | Firebase App Check · Play Integrity (Android) · DeviceCheck (iOS) |
|
||||||
|
| Billing | RevenueCat (server-verified entitlements) |
|
||||||
|
| Analytics | Firebase Analytics · Crashlytics |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
## Repository Layout
|
## Repository Layout
|
||||||
|
|
||||||
```text
|
```text
|
||||||
.
|
.
|
||||||
├── app/ # Native Android app
|
├── app/ # Native Android app (Kotlin)
|
||||||
│ └── src/main/java/app/closer
|
│ └── src/main/java/app/closer
|
||||||
│ ├── core/ # Firebase, analytics, billing, navigation, notifications, security
|
│ ├── core/ # Firebase, analytics, billing, navigation, notifications, security
|
||||||
│ ├── data/ # Room, Firestore data sources, repositories, seed parsing
|
│ ├── data/ # Room, Firestore data sources, repositories, seed parsing
|
||||||
│ ├── domain/ # Models and repository contracts
|
│ ├── domain/ # Models and repository contracts
|
||||||
│ └── ui/ # Compose screens and feature ViewModels
|
│ └── ui/ # Compose screens and feature ViewModels
|
||||||
├── functions/ # Firebase Cloud Functions, TypeScript
|
├── iphone/ # Native iOS app (SwiftUI)
|
||||||
├── server/ # Optional Express webhook/health service
|
│ ├── ARCHITECTURE_AUDIT.md # iOS port blueprint (49 screens, schema, models)
|
||||||
├── seed/ # Question pack JSON and local DB generation
|
│ ├── project.yml # XcodeGen project spec
|
||||||
├── docs/ # QA notes, release prep, roadmap, screenshots
|
│ ├── Package.swift # SPM dependency manifest
|
||||||
└── firestore.rules # Firestore security rules
|
│ └── Closer/
|
||||||
|
│ ├── Models/ # Firestore + domain codable types
|
||||||
|
│ ├── Core/ # Auth, Billing, Notifications
|
||||||
|
│ ├── Services/ # FirestoreService (callable wrappers)
|
||||||
|
│ ├── Theme/ # CloserTheme (colors, typography, spacing)
|
||||||
|
│ ├── Components/ # Shared SwiftUI components
|
||||||
|
│ ├── Navigation/ # Root ContentView + TabView routing
|
||||||
|
│ ├── Onboarding/ # Onboarding, login, signup, profile creation
|
||||||
|
│ ├── Pairing/ # Invite code, partner confirm, recovery
|
||||||
|
│ ├── Home/ # Home dashboard, streak, partner mirror
|
||||||
|
│ ├── Questions/ # Daily question, answer reveal, history, packs
|
||||||
|
│ ├── Play/ # Play hub + games (ThisOrThat, HowWell, DesireSync)
|
||||||
|
│ ├── Wheel/ # Spin wheel with animated 8-slice picker
|
||||||
|
│ ├── Dates/ # Date swipe, matches, builder, bucket list
|
||||||
|
│ └── Settings/ # Settings, paywall, help, data export
|
||||||
|
├── functions/ # Firebase Cloud Functions (TypeScript)
|
||||||
|
│ └── src/
|
||||||
|
│ ├── auth/ # Auth flow + invite lifecycle
|
||||||
|
│ ├── billing/ # RevenueCat webhook + entitlement sync
|
||||||
|
│ ├── couples/ # Pairing, leave, daily question triggers
|
||||||
|
│ ├── questions/ # onAnswerWritten, onMessageWritten, threads
|
||||||
|
│ └── server/ # Internal Express webhook service (not client-facing)
|
||||||
|
├── server/ # Optional Express webhook/health service
|
||||||
|
├── seed/ # Question pack JSON and local DB generation
|
||||||
|
├── docs/ # QA notes, release prep, roadmap, screenshots
|
||||||
|
└── firestore.rules # Firestore security rules
|
||||||
```
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
## Getting Started
|
## Getting Started
|
||||||
|
|
||||||
### Prerequisites
|
### Prerequisites
|
||||||
|
|
||||||
- Android Studio / Android SDK with JDK 17
|
- **Android:** Android Studio · Android SDK · JDK 17
|
||||||
- Firebase project with Auth, Firestore, Cloud Messaging, Crashlytics, Analytics, and App Check
|
- **iOS:** Xcode 16 · macOS · [XcodeGen](https://github.com/yonaskolb/XcodeGen) (`brew install xcodegen`)
|
||||||
- `app/google-services.json`
|
- **Firebase:** Project with Auth, Firestore, Cloud Messaging, Crashlytics, Analytics, App Check
|
||||||
- RevenueCat project and Android API key
|
- **Android config:** `app/google-services.json`
|
||||||
- Node 20 for Firebase Functions and server tooling
|
- **iOS config:** `iphone/Closer/GoogleService-Info.plist`
|
||||||
|
- **Billing:** RevenueCat project with Android and iOS API keys
|
||||||
|
- **Node 20** for Firebase Functions tooling
|
||||||
|
|
||||||
Create local config:
|
### Local config
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
|
# Android
|
||||||
cp local.properties.example local.properties
|
cp local.properties.example local.properties
|
||||||
|
# iOS
|
||||||
|
cp iphone/Closer/GoogleService-Info.plist.example iphone/Closer/GoogleService-Info.plist
|
||||||
```
|
```
|
||||||
|
|
||||||
Then add local-only values such as:
|
Add local-only values such as:
|
||||||
|
|
||||||
```properties
|
```properties
|
||||||
sdk.dir=/path/to/Android/Sdk
|
sdk.dir=/path/to/Android/Sdk
|
||||||
RC_API_KEY=your_revenuecat_android_key
|
RC_API_KEY_ANDROID=your_revenuecat_android_key
|
||||||
|
RC_API_KEY_IOS=your_revenuecat_ios_key
|
||||||
```
|
```
|
||||||
|
|
||||||
### Android
|
### Android
|
||||||
|
|
@ -120,6 +227,23 @@ Useful verification command:
|
||||||
./gradlew :app:compileDebugKotlin
|
./gradlew :app:compileDebugKotlin
|
||||||
```
|
```
|
||||||
|
|
||||||
|
### iOS
|
||||||
|
|
||||||
|
```bash
|
||||||
|
cd iphone
|
||||||
|
xcodegen generate
|
||||||
|
xed Closer.xcodeproj
|
||||||
|
```
|
||||||
|
|
||||||
|
Build from the command line:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
xcodebuild -project iphone/Closer.xcodeproj \
|
||||||
|
-scheme Closer \
|
||||||
|
-destination 'platform=iOS Simulator,name=iPhone 15' \
|
||||||
|
build
|
||||||
|
```
|
||||||
|
|
||||||
### Firebase Functions
|
### Firebase Functions
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
|
|
@ -137,35 +261,62 @@ npm install
|
||||||
npm run dev
|
npm run dev
|
||||||
```
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
## Data Model
|
## Data Model
|
||||||
|
|
||||||
Closer combines local-first question content with cloud sync for shared relationship state:
|
Closer combines local-first question content with cloud sync for shared relationship state:
|
||||||
|
|
||||||
- **Room** stores bundled question categories, questions, date plans, and date preferences.
|
- **Room (Android) / local bundled JSON (iOS)** stores seeded question categories, questions, and date preferences — the app stays usable offline.
|
||||||
- **DataStore** stores local settings and lightweight preferences.
|
- **DataStore (Android) / UserDefaults (iOS)** stores local settings and lightweight preferences.
|
||||||
- **Firestore** stores users, couples, invites, question threads, date matches, bucket-list state, and entitlements.
|
- **Firestore** stores users, couples, invites, daily questions, answer history, date matches, bucket-list state, time capsules, gentle reminders, and entitlements.
|
||||||
- **Cloud Functions** handle reminder workflows, billing entitlement sync, RevenueCat webhooks, and security checks.
|
- **Cloud Functions** handle pairing lifecycle, reminder workflows, billing entitlement sync, RevenueCat webhooks, partner activity triggers (FCM), and security checks.
|
||||||
- **FCM** delivers reminders and partner activity notifications.
|
- **FCM** delivers reminders and partner activity notifications to both platforms.
|
||||||
|
|
||||||
## Privacy And Safety Principles
|
---
|
||||||
|
|
||||||
- Couple data is scoped by couple ID and protected by Firestore rules.
|
## Privacy & Safety Principles
|
||||||
- Answers are designed to be private first, then revealed intentionally.
|
|
||||||
- Account deletion and privacy screens are part of the first-class app surface.
|
- Couple data is **scoped by couple ID** and protected by Firestore security rules — no other couple can read another couple's data.
|
||||||
- App Check and Play Integrity are included to reduce abusive backend access.
|
- Answers are **private first**, then revealed intentionally by the partner.
|
||||||
- Subscription state is verified server-side instead of trusting only the client.
|
- Account deletion and privacy screens are first-class surfaces in the app.
|
||||||
|
- **App Check** (Play Integrity on Android, DeviceCheck on iOS) blocks abusive backend access.
|
||||||
|
- Subscription state is verified **server-side** via Cloud Functions rather than trusting the client.
|
||||||
|
- All private answer and capsule content is **end-to-end encrypted** with couple-owned keys (Android uses Tink; iOS uses Apple CryptoKit in a follow-up batch).
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
## Current Status
|
## Current Status
|
||||||
|
|
||||||
This is a private MVP/internal testing codebase. The Android app builds, includes seeded question content, and has active flows for onboarding, auth, home, daily questions, packs, answer history, pairing, settings, wheel sessions, date tools, billing, notifications, and Firebase integration.
|
This is a **private MVP / internal testing codebase**.
|
||||||
|
|
||||||
Release prep lives in:
|
### Complete
|
||||||
|
- Android MVP — onboarding, auth (anonymous/email/Google), pairing, home, daily questions, question packs, answer history, spin wheel, date tools, settings, billing, notifications, Firebase integration, E2EE crypto layer
|
||||||
|
- iOS port scaffold — all 49 screens mapped to SwiftUI views, Firebase/RevenueCat integration, full screen parity landed on `dev` branch
|
||||||
|
- Backend — 17 Cloud Functions (callable, triggers, scheduled, webhook) covering full relationship lifecycle
|
||||||
|
- Seeded question content — 6,000+ prompts across 22 categories
|
||||||
|
|
||||||
|
### In Progress
|
||||||
|
- iOS Xcode project generation and build verification
|
||||||
|
- iOS E2EE layer (CryptoKit interop with Android Tink)
|
||||||
|
- Final QA pass on both platforms
|
||||||
|
|
||||||
|
### Release Prep
|
||||||
- `docs/release/internal-testing-checklist.md`
|
- `docs/release/internal-testing-checklist.md`
|
||||||
- `docs/release/store-assets.md`
|
- `docs/release/store-assets.md`
|
||||||
- `docs/qa/private-mvp-checklist.md`
|
- `docs/qa/private-mvp-checklist.md`
|
||||||
- `docs/qa/ui-review.md`
|
- `docs/qa/ui-review.md`
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Project History
|
||||||
|
|
||||||
|
See `HISTORY.md` for the full changelog and release notes.
|
||||||
|
|
||||||
|
See `PROJECT.md` for detailed scope, feature matrix, and architectural decisions.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
## License
|
## License
|
||||||
|
|
||||||
Private project. All rights reserved.
|
Private project. All rights reserved.
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue