# iOS Code Quality & Consistency Refactoring Design

**Date:** November 11, 2025
**Project:** BaoLife iOS Application
**Architecture:** Feature-Based Modular Design
**Estimated Effort:** 31 hours across 10 phases

---

## Executive Summary

This document outlines a comprehensive refactoring of the BaoLife iOS codebase from a monolithic structure (1,843-line ContentView, 1,120-line WebSocketService) to a maintainable, scalable feature-based modular architecture. The refactoring addresses code quality, consistency, and prepares the codebase for commercial features (monetization, retention mechanics) identified in PROJECT_STATUS.md.

**Key Improvements:**
- ContentView.swift: 1,843 → <100 lines (97% reduction)
- WebSocketService.swift: 1,120 → <350 lines (70% reduction)
- File organization: 13 → 110+ files with clear responsibilities
- Establishes design system for consistency
- Creates reusable component library
- Enables rapid feature development

---

## Current State Analysis

### Problems Identified

**1. Monolithic View Files**
- ContentView.swift contains 1,843 lines with 26 separate view structs
- Violates single responsibility principle
- Difficult to navigate and maintain
- Merge conflicts inevitable with multiple developers

**2. Bloated Service Layer**
- WebSocketService.swift (1,120 lines) mixes concerns:
  - Network communication
  - State management
  - Business logic
  - Model definitions
  - Feature-specific logic (dating, messaging, activities)

**3. Inconsistent Naming**
- `messagesView.swift` (lowercase) vs `DatingView.swift` (uppercase)
- No clear naming conventions

**4. Tight Coupling**
- 34 @EnvironmentObject declarations across 11 files
- Views directly access WebSocketService for everything
- No separation between presentation and business logic

**5. No Design System**
- Hardcoded colors: `Color(hex: 0x007AFF)` scattered throughout
- Magic numbers: `.padding(16)`, `.font(.system(size: 17))`
- Inconsistent spacing and typography
- Duplicate UI patterns (cards, buttons, stat displays)

**6. Poor Organization**
- All views in flat structure
- No feature grouping
- Helper code scattered
- No clear architectural pattern

### Code Metrics (Current)
```
Total Lines:     4,433
Total Files:     13
Avg Lines/File:  341
Largest File:    ContentView.swift (1,843 lines)
```

---

## Architecture Vision

### Feature-Based Modular Architecture

The refactored codebase organizes code by game features, with each feature owning its views, view models, and components. This aligns naturally with how the game is structured and scales well as new features are added.

**Four Primary Layers:**

1. **App Layer** - Application lifecycle and dependency injection
2. **Core Layer** - Shared infrastructure (services, models, core view models)
3. **Features Layer** - Self-contained feature modules
4. **Shared Layer** - Reusable components and design system

**Key Principles:**
- Unidirectional data flow: Services → ViewModels → Views
- Single source of truth: WebSocketService publishes events, ViewModels interpret
- Dependency injection: @EnvironmentObject only for Core services
- Feature isolation: Features communicate through Core layer

---

## Complete Folder Structure

```
lichunWebsocket/
├── App/                                    [~150 lines total]
│   ├── lichunWebsocketApp.swift           [~80 lines - DI setup]
│   ├── AppDelegate.swift                  [~3 lines - keep as-is]
│   └── ContentView.swift                  [~60 lines - routing only]
│
├── Core/                                   [~1200 lines total]
│   ├── Services/
│   │   ├── WebSocketService.swift         [~300 lines - networking only]
│   │   ├── StoreService.swift             [~150 lines - StoreKit]
│   │   └── NotificationService.swift      [~50 lines - push notifications]
│   ├── Models/
│   │   ├── Person.swift                   [~150 lines]
│   │   ├── Player.swift                   [~100 lines]
│   │   ├── Activity.swift                 [~120 lines]
│   │   ├── Conversation.swift             [~80 lines]
│   │   ├── Question.swift                 [~40 lines]
│   │   ├── MessageEvent.swift             [~30 lines]
│   │   └── StoreItem.swift                [~40 lines]
│   └── ViewModels/
│       ├── GameStateViewModel.swift       [~200 lines - energy, money, time]
│       └── PlayerViewModel.swift          [~100 lines - player state]
│
├── Features/                               [~2500 lines total]
│   ├── Home/
│   │   ├── Views/
│   │   │   ├── HomeView.swift
│   │   │   ├── HeaderView.swift
│   │   │   ├── RelationshipsView.swift
│   │   │   └── SpeedControlView.swift
│   │   ├── Components/
│   │   │   ├── StatDisplayRow.swift
│   │   │   └── SeasonBackground.swift
│   │   └── HomeViewModel.swift
│   │
│   ├── Character/
│   │   ├── Views/
│   │   │   ├── CharacterCreation/
│   │   │   │   ├── WelcomeView.swift
│   │   │   │   ├── CharacterSetupView.swift
│   │   │   │   └── ConfirmationView.swift
│   │   │   ├── CharacterDetailView.swift
│   │   │   ├── PersonDetailView.swift
│   │   │   └── DeathView.swift
│   │   ├── Components/
│   │   │   ├── CharacterCard.swift
│   │   │   ├── AvatarView.swift
│   │   │   ├── SVGImageView.swift
│   │   │   └── StatBar.swift
│   │   └── ViewModels/
│   │       ├── CharacterCreationViewModel.swift
│   │       └── CharacterDetailViewModel.swift
│   │
│   ├── Dating/
│   │   ├── Views/
│   │   │   ├── DatingView.swift
│   │   │   ├── SwipeDatingView.swift
│   │   │   └── DateOptionsView.swift
│   │   ├── Components/
│   │   │   ├── MatchCard.swift
│   │   │   ├── DateLocationCard.swift
│   │   │   └── RelationshipStatusBadge.swift
│   │   └── ViewModels/
│   │       ├── DatingViewModel.swift
│   │       └── SwipeViewModel.swift
│   │
│   ├── Messaging/
│   │   ├── Views/
│   │   │   ├── MessagesView.swift
│   │   │   ├── ConversationView.swift
│   │   │   └── ChatView.swift
│   │   ├── Components/
│   │   │   ├── MessageBubble.swift
│   │   │   ├── ConversationRow.swift
│   │   │   └── MessageInputBar.swift
│   │   └── ViewModels/
│   │       └── MessagingViewModel.swift
│   │
│   ├── Activities/
│   │   ├── Views/
│   │   │   ├── ActivityView.swift
│   │   │   ├── ActivityButtonsView.swift
│   │   │   ├── OccupationListView.swift
│   │   │   └── ExtracurricularListView.swift
│   │   ├── Components/
│   │   │   ├── ActivityCard.swift
│   │   │   ├── ActivityProgressBar.swift
│   │   │   └── EnergyCostBadge.swift
│   │   └── ViewModels/
│   │       └── ActivityViewModel.swift
│   │
│   ├── Store/
│   │   ├── Views/
│   │   │   ├── StoreView.swift
│   │   │   ├── ItemsView.swift
│   │   │   ├── StoreItemView.swift
│   │   │   └── ProductGridView.swift
│   │   ├── Components/
│   │   │   ├── DiamondPackCard.swift
│   │   │   ├── ItemCard.swift
│   │   │   └── PurchaseButton.swift
│   │   └── ViewModels/
│   │       └── StoreViewModel.swift
│   │
│   └── Events/
│       ├── Views/
│       │   └── EventModalView.swift
│       ├── Components/
│       │   ├── QuestionCard.swift
│       │   └── AnswerButton.swift
│       └── ViewModels/
│           └── EventViewModel.swift
│
├── Shared/                                 [~800 lines total]
│   ├── DesignSystem/
│   │   ├── AppColors.swift                [~80 lines]
│   │   ├── AppTypography.swift            [~60 lines]
│   │   ├── AppSpacing.swift               [~40 lines]
│   │   └── AppImages.swift                [~40 lines]
│   ├── Components/
│   │   ├── Buttons/
│   │   │   ├── PrimaryButton.swift
│   │   │   ├── SecondaryButton.swift
│   │   │   └── IconButton.swift
│   │   ├── Cards/
│   │   │   └── BaseCard.swift
│   │   ├── Indicators/
│   │   │   ├── LoadingView.swift
│   │   │   └── ProgressBar.swift
│   │   └── Overlays/
│   │       ├── ToastView.swift
│   │       └── ConfettiView.swift
│   ├── Extensions/
│   │   ├── Color+Hex.swift
│   │   ├── View+Extensions.swift
│   │   └── String+Extensions.swift
│   └── Utilities/
│       ├── DateFormatters.swift
│       └── Constants.swift
│
└── Resources/
    ├── Assets.xcassets/
    └── Info.plist
```

---

## Core Layer Details

### WebSocketService Refactoring

**Current State (1,120 lines):**
- Mixes networking, parsing, business logic, state management
- Contains all model definitions
- Has feature-specific logic

**New Responsibilities (300 lines - networking ONLY):**

```swift
class WebSocketService: ObservableObject {
    // ONLY raw connection state
    @Published var isConnected: Bool = false
    @Published var rawPlayerData: [String: Any] = [:]
    @Published var rawPersonData: [String: Any] = [:]

    // Networking methods
    func connect()
    func disconnect()
    func sendMessage(message: [String: Any])
    private func readMessage()

    // Delegate callbacks for updates
    var onPlayerUpdate: (([String: Any]) -> Void)?
    var onPersonUpdate: (([String: Any]) -> Void)?
    var onConversationEvent: (([String: Any]) -> Void)?
    var onQuestionEvent: (([String: Any]) -> Void)?
}
```

**What Moves Out:**
- **Models** → Core/Models/ (Person, Player, Activity, etc.)
- **Parsing logic** → ViewModels (each VM parses its domain)
- **Business logic** → Feature ViewModels
- **StoreKit** → Core/Services/StoreService.swift
- **Device token** → Core/Services/NotificationService.swift

### GameStateViewModel

Handles cross-feature game state (energy, money, time):

```swift
class GameStateViewModel: ObservableObject {
    @Published var energy: Int = 100
    @Published var money: Int = 0
    @Published var diamonds: Int = 0
    @Published var gameDate: String = ""
    @Published var hourOfDay: Int = 8
    @Published var minuteOfHour: Int = 0
    @Published var season: String = "spring"
    @Published var gameSpeed: Int = 5000

    private var webSocketService: WebSocketService

    func setGameSpeed(_ speed: Int)
    func canAfford(energy: Int, money: Int, diamonds: Int) -> Bool
    private func parseGameState(_ data: [String: Any])
}
```

### PlayerViewModel

Handles player character state:

```swift
class PlayerViewModel: ObservableObject {
    @Published var person: Person = Person()
    @Published var relationships: [Person] = []
    @Published var activities: [Activity] = []
    @Published var inventory: [StoreItem] = []

    private var webSocketService: WebSocketService
    private func parsePerson(_ data: [String: Any])
}
```

### Model Extraction

Each model becomes a clean, focused file:

```swift
// Core/Models/Person.swift (~150 lines)
struct Person: Identifiable, Codable {
    let id: String
    var name: String
    var ageYears: Int
    var gender: String
    var health: Int
    var happiness: Int
    var intelligence: Int

    // Computed properties only
    var fullName: String { "\(name) \(lastName)" }
    var ageDisplay: String { "\(ageYears) years old" }
}
```

---

## Feature Modules

### Character Feature

**Purpose:** Character creation, death, and character details

**Structure:**
- Views/CharacterCreation/ - Welcome, setup, confirmation flow
- Views/ - CharacterDetailView, PersonDetailView, DeathView
- Components/ - CharacterCard, AvatarView, SVGImageView, StatBar
- ViewModels/ - CharacterCreationViewModel, CharacterDetailViewModel

**Key Component: CharacterCard (reusable across features)**
```swift
struct CharacterCard: View {
    let person: Person
    let showStats: Bool = true
    let onTap: (() -> Void)?

    var body: some View {
        BaseCard {
            HStack {
                AvatarView(person: person, size: 60)
                VStack(alignment: .leading) {
                    Text(person.fullName).font(.appHeadline)
                    Text(person.ageDisplay).font(.appCaption)
                    if showStats {
                        HStack {
                            StatBar(value: person.health, color: .red)
                            StatBar(value: person.happiness, color: .yellow)
                        }
                    }
                }
            }
        }
    }
}
```

Used in: RelationshipsView, MessagesView, DatingView, PersonDetailView

### Home Feature

**Purpose:** Main dashboard, time controls, relationships overview

**Structure:**
- Views/ - HomeView, HeaderView, RelationshipsView, SpeedControlView
- Components/ - StatDisplayRow, SeasonBackground
- HomeViewModel.swift

**Key Responsibility:** Combine PlayerViewModel + GameStateViewModel data for display

### Dating Feature

**Purpose:** Swipe dating, matches, date planning

**Structure:**
- Views/ - DatingView, SwipeDatingView, DateOptionsView
- Components/ - MatchCard, DateLocationCard, RelationshipStatusBadge
- ViewModels/ - DatingViewModel, SwipeViewModel

**Key Improvement:** SwipeViewModel implements compatibility algorithm (addresses PROJECT_STATUS.md feedback on 50% random matching):

```swift
class SwipeViewModel: ObservableObject {
    func calculateCompatibility(player: Person, match: Person) -> Int {
        var score = 50

        // Shared interests
        let commonInterests = Set(player.likes).intersection(Set(match.likes))
        score += commonInterests.count * 5

        // Age compatibility
        let ageDiff = abs(player.ageYears - match.ageYears)
        if ageDiff <= 3 { score += 15 }
        else if ageDiff <= 5 { score += 5 }
        else { score -= 10 }

        // Prestige compatibility
        let prestigeDiff = abs(player.prestige - match.prestige)
        if prestigeDiff < 20 { score += 10 }

        return max(0, min(100, score))
    }

    func swipeRight() {
        let compatibility = calculateCompatibility(...)
        let didMatch = compatibility > 60 || Bool.random()
        // Send to server
    }
}
```

### Messaging Feature

**Purpose:** Conversations with NPCs

**Structure:**
- Views/ - MessagesView, ConversationView, ChatView
- Components/ - MessageBubble, ConversationRow, MessageInputBar
- ViewModels/ - MessagingViewModel

**Key Improvement:** Centralized date formatting in Shared/Utilities/DateFormatters.swift (currently duplicated in messagesView.swift:19-21)

### Activities Feature

**Purpose:** School, work, extracurriculars, focus modes

**Structure:**
- Views/ - ActivityView, ActivityButtonsView, OccupationListView, ExtracurricularListView
- Components/ - ActivityCard, ActivityProgressBar, EnergyCostBadge
- ViewModels/ - ActivityViewModel

**ActivityViewModel Responsibilities:**
- Filter available activities by age/education level
- Check energy/money costs before enrollment
- Track current activities vs activity records
- Calculate performance and achievements

### Store Feature

**Purpose:** IAP purchases, diamond packs, in-game items

**Structure:**
- Views/ - StoreView, ItemsView, StoreItemView, ProductGridView
- Components/ - DiamondPackCard, ItemCard, PurchaseButton
- ViewModels/ - StoreViewModel

**Key Change:** Extract StoreKit logic from StoreManager (lichunWebsocketApp.swift:61-117) into StoreViewModel

### Events Feature

**Purpose:** Game questions, life events, modal notifications

**Structure:**
- Views/ - EventModalView
- Components/ - QuestionCard, AnswerButton
- ViewModels/ - EventViewModel

**EventViewModel Responsibilities:**
- Manage question queue (FIFO)
- Submit answers to WebSocket
- Handle message events (birth, death, graduation)

---

## Design System

### AppColors.swift

Centralized color definitions to replace hardcoded values:

```swift
struct AppColors {
    // Primary Colors
    static let primary = Color(hex: 0x007AFF)
    static let secondary = Color(hex: 0x5856D6)
    static let accent = Color(hex: 0xFF9500)

    // Game-Specific
    static let energy = Color(hex: 0x00D084)
    static let money = Color(hex: 0x4CD964)
    static let diamond = Color(hex: 0x5AC8FA)
    static let health = Color(hex: 0xFF3B30)
    static let happiness = Color(hex: 0xFFCC00)
    static let intelligence = Color(hex: 0x5856D6)

    // Backgrounds
    static let primaryBackground = Color("Background")
    static let cardBackground = Color.white.opacity(0.1)
    static let modalOverlay = Color.black.opacity(0.6)

    // Messaging
    static let playerBubble = Color(hex: 0x007AFF)
    static let npcBubble = Color.gray

    // Seasons
    static func seasonGradient(_ season: String) -> LinearGradient
}
```

### AppTypography.swift

Consistent text styles:

```swift
extension Font {
    static let appTitle = Font.system(size: 28, weight: .bold, design: .rounded)
    static let appHeadline = Font.system(size: 17, weight: .semibold, design: .rounded)
    static let appBody = Font.system(size: 16, weight: .regular, design: .rounded)
    static let appCaption = Font.system(size: 12, weight: .regular, design: .rounded)
}

extension Text {
    func appTitleStyle() -> Text
    func appHeadlineStyle() -> Text
    func appBodyStyle() -> Text
}
```

### AppSpacing.swift

Consistent spacing values:

```swift
struct AppSpacing {
    static let xs: CGFloat = 4
    static let sm: CGFloat = 8
    static let md: CGFloat = 16
    static let lg: CGFloat = 24
    static let xl: CGFloat = 32

    static let buttonHeight: CGFloat = 50
    static let cornerRadius: CGFloat = 12
    static let avatarSizeMedium: CGFloat = 60
}
```

**Usage Example:**
```swift
// BEFORE (hardcoded)
Text("Energy").font(.system(size: 16, weight: .semibold))
    .foregroundColor(Color(hex: 0x00D084))
    .padding(16)

// AFTER (design system)
Text("Energy").appHeadlineStyle()
    .foregroundColor(AppColors.energy)
    .padding(AppSpacing.md)
```

---

## Reusable Components

### BaseCard

Foundation for all card-style UI:

```swift
struct BaseCard<Content: View>: View {
    let content: Content
    var backgroundColor: Color = AppColors.cardBackground
    var cornerRadius: CGFloat = AppSpacing.cornerRadius
    var shadow: Bool = true

    var body: some View {
        content
            .background(backgroundColor)
            .cornerRadius(cornerRadius)
            .if(shadow) { view in
                view.shadow(color: .black.opacity(0.1), radius: 8, y: 4)
            }
    }
}
```

### PrimaryButton

Consistent button styling:

```swift
struct PrimaryButton: View {
    let title: String
    let action: () -> Void
    var isDisabled: Bool = false
    var isLoading: Bool = false

    var body: some View {
        Button(action: action) {
            HStack {
                if isLoading {
                    ProgressView()
                } else {
                    Text(title).font(.appHeadline)
                }
            }
            .frame(maxWidth: .infinity)
            .frame(height: AppSpacing.buttonHeight)
            .background(isDisabled ? AppColors.disabledText : AppColors.primary)
            .cornerRadius(AppSpacing.cornerRadius)
        }
        .disabled(isDisabled || isLoading)
    }
}
```

### StatBar

Reusable stat display (health, happiness, intelligence):

```swift
struct StatBar: View {
    let label: String?
    let value: Int
    let maxValue: Int = 100
    let color: Color

    var body: some View {
        VStack(alignment: .leading) {
            if let label = label {
                HStack {
                    Text(label).font(.appCaption)
                    Spacer()
                    Text("\(value)%").font(.appCaptionBold)
                }
            }

            GeometryReader { geometry in
                ZStack(alignment: .leading) {
                    Rectangle().fill(color.opacity(0.3))
                    Rectangle().fill(color)
                        .frame(width: geometry.size.width * Double(value) / 100)
                }
                .cornerRadius(4)
            }
            .frame(height: 8)
        }
    }
}
```

### ToastView

Improved feedback notifications:

```swift
struct ToastView: View {
    let message: String
    let type: ToastType // .success, .error, .info, .warning
    @Binding var isShowing: Bool

    // Auto-dismisses after 2 seconds
    // Shows icon + message
    // Animates from top
}
```

---

## Migration Strategy

### 10-Phase Implementation Plan

**Total Estimated Duration:** 31 hours

Each phase is independently testable and can be committed separately.

#### Phase 1: Foundation Setup (1 hour) ✅ Safe
- Create folder structure
- Create design system files (AppColors, AppTypography, AppSpacing)
- Create utility files (DateFormatters, Constants)
- Extract Color+Hex extension
- **Test:** App compiles and runs

#### Phase 2: Extract Core Models (2 hours) ✅ Low Risk
- Extract Person → Core/Models/Person.swift
- Extract Player → Core/Models/Player.swift
- Extract Activity classes → Core/Models/Activity.swift
- Extract Conversation, Question, MessageEvent, StoreItem
- Update imports in WebSocketService
- **Test:** App builds, models accessible

#### Phase 3: Create Core ViewModels (4 hours) ⚠️ Medium Risk
- Create GameStateViewModel (energy, money, time)
- Create PlayerViewModel (person, relationships)
- Refactor WebSocketService to networking-only (~300 lines)
- Update lichunWebsocketApp.swift dependency injection
- **Test:** ViewModels receive WebSocket updates correctly

#### Phase 4: Extract Shared Components (2 hours) ✅ Low Risk
- Create button components (PrimaryButton, SecondaryButton, IconButton)
- Create BaseCard component
- Extract LoadingView, ConfettiView
- Create ToastView, ProgressBar
- **Test:** Components render in previews

#### Phase 5: Split Character Feature (4 hours) ⚠️ Medium Risk
- Extract SVGImageView, create CharacterCard, AvatarView, StatBar
- Extract WelcomeView, CharacterSetupView, ConfirmationView
- Extract CharacterDetailView, DeathView
- Move PersonDetailView
- Create CharacterCreationViewModel, CharacterDetailViewModel
- **Test:** Character creation flow works end-to-end

#### Phase 6: Split Home Feature (3 hours) ✅ Low Risk
- Extract HeaderView, HomeView, RelationshipsView, SpeedControlView
- Create StatDisplayRow, SeasonBackground components
- Create HomeViewModel
- **Test:** Home tab displays correctly, speed controls work

#### Phase 7: Split Dating Feature (4 hours) ⚠️ Medium Risk
- Create MatchCard, DateLocationCard, RelationshipStatusBadge
- Move and refactor DatingView, SwipeDatingView
- Extract DateOptionsView
- Create DatingViewModel, SwipeViewModel (with compatibility algorithm)
- **Test:** Swiping, matching, and dates work

#### Phase 8: Split Messaging Feature (3 hours) ✅ Low Risk
- Create MessageBubble, ConversationRow, MessageInputBar
- Rename messagesView → MessagesView
- Extract ConversationView, ChatView
- Create MessagingViewModel
- **Test:** Conversations load, messages send, AI responds

#### Phase 9: Split Activities & Store Features (5 hours) ✅ Low Risk
- **Activities:** Create ActivityCard, ActivityProgressBar, EnergyCostBadge
- Extract ActivityButtonsView, refactor ActivityView, OccupationListView, ExtracurricularListView
- Create ActivityViewModel
- **Store:** Create DiamondPackCard, ItemCard, PurchaseButton
- Extract StoreView, StoreItemView, ProductGridView
- Create StoreViewModel
- **Events:** Extract EventModalView, create QuestionCard, AnswerButton, EventViewModel
- **Test:** All features work independently

#### Phase 10: Final Cleanup (3 hours) ✅ Low Risk
- Reduce ContentView to routing-only (~60 lines)
- Replace hardcoded colors/fonts with design system
- Run linter
- Update CLAUDE.md with new architecture
- **Test:** Full regression test

---

### Testing Checkpoints

Run after EVERY phase:
- ✅ App compiles without errors
- ✅ App launches and connects to WebSocket
- ✅ Can create new character
- ✅ Can navigate all tabs
- ✅ Core gameplay loop works (activities, messaging, dating)
- ✅ StoreKit purchases work
- ✅ Time simulation works
- ✅ No crashes during 5-minute play session

### Git Branch Strategy

```bash
git checkout -b refactor/feature-based-architecture
git checkout -b refactor/phase-1-foundation
# Complete phase 1
git add . && git commit -m "Phase 1: Foundation setup"
git checkout refactor/feature-based-architecture
git merge refactor/phase-1-foundation
# Test thoroughly before phase 2
```

### Risk Mitigation

**High-Risk Areas:**
1. WebSocketService refactoring (Phase 3) - State management changes
2. Character creation flow (Phase 5) - Critical onboarding path
3. Dating swipe logic (Phase 7) - New compatibility algorithm

**Strategies:**
- Keep old ContentView as ContentView.old.swift temporarily
- Feature flags for new algorithms (optional)
- Automated testing for critical paths
- Rollback plan: `git reset --hard HEAD~1` if phase fails

---

## Conventions & Standards

### Naming Conventions

- **Files:** PascalCase (DatingView.swift, not datingView.swift)
- **Folders:** PascalCase (Dating/, not dating/)
- **ViewModels:** {Feature}ViewModel.swift
- **Components:** Descriptive names (CharacterCard, not Card)
- **Views:** Always end with "View" suffix

### Code Organization Within Files

```swift
// 1. Imports
import SwiftUI

// 2. Main struct/class
struct DatingView: View {

    // 3. Properties (grouped by type)
    @EnvironmentObject var gameState: GameStateViewModel
    @State private var isShowingSheet = false
    @ObservedObject var viewModel: DatingViewModel

    // 4. Computed properties
    var hasMatches: Bool { !viewModel.matches.isEmpty }

    // 5. Body
    var body: some View { }

    // 6. Private methods
    private func loadMatches() { }
}

// 7. Preview
#if DEBUG
struct DatingView_Previews: PreviewProvider { }
#endif
```

### File Size Guidelines

- Views: < 200 lines
- ViewModels: < 200 lines
- Components: < 100 lines
- Models: < 200 lines (can be larger if many properties)

### SwiftUI Best Practices

- Prefer `@StateObject` for owned ViewModels
- Use `@ObservedObject` for passed ViewModels
- Use `@EnvironmentObject` ONLY for Core services
- Extract subviews at 3+ levels of nesting
- Use view builders for conditional logic

### Error Handling Pattern

```swift
enum DatingError: LocalizedError {
    case notEnoughEnergy
    case characterNotAvailable

    var errorDescription: String? {
        switch self {
        case .notEnoughEnergy: return "Not enough energy to go on a date"
        case .characterNotAvailable: return "Character is not available"
        }
    }
}
```

---

## Success Criteria

### Quantitative Metrics

- ✅ ContentView.swift: 1843 → <100 lines (>95% reduction)
- ✅ WebSocketService.swift: 1120 → <350 lines (>70% reduction)
- ✅ No files exceed 250 lines
- ✅ Average file size < 60 lines
- ✅ 0 compiler warnings
- ✅ 0 crashes during 30-minute play test
- ✅ All features work identically to before

### Qualitative Metrics

- ✅ Can find feature code in <10 seconds ("where's dating?" → Features/Dating/)
- ✅ Can add new feature without modifying existing features
- ✅ Design changes require editing only 1-2 files
- ✅ New developer understands architecture from folder structure
- ✅ No duplicate code between features

### Maintainability Improvements

Examples from PROJECT_STATUS.md roadmap:
- ✅ Adding Achievements: Create Features/Achievements/ (no touch existing)
- ✅ Adding Children: Create Features/Family/ (isolated)
- ✅ Adding Property: Create Features/Property/ (isolated)
- ✅ Changing button styles: Edit Shared/Components/Buttons/ (affects all)
- ✅ Debugging dating bug: Only read Features/Dating/

---

## Future Feature Readiness

Based on PROJECT_STATUS.md monetization and retention roadmap:

**Monetization Features:**
- Subscriptions → Features/Store/ViewModels/SubscriptionViewModel.swift
- Energy refills → Features/Store/Views/EnergyRefillView.swift
- Time skips → Features/Store/Views/TimeSkipView.swift
- Premium content → Features/Store/ components

**Retention Features:**
- Achievements → Features/Achievements/ (new folder)
- Daily login rewards → Features/Rewards/ (new folder)
- Daily quests → Features/Quests/ (new folder)
- Battle pass → Features/BattlePass/ (new folder)

**Gameplay Features:**
- Children/Family → Features/Family/ (new folder)
- Property ownership → Features/Property/ (new folder)
- Pets → Features/Pets/ (new folder)
- Health system → Features/Health/ (new folder)
- Photo album → Features/Memories/ (new folder)

---

## Appendix: Code Examples

### Before & After: View Access Pattern

**Before:**
```swift
struct DatingView: View {
    @EnvironmentObject var webSocketService: WebSocketService

    var body: some View {
        Text("Energy: \(webSocketService.person.calcEnergy)")
        Button("Go on Date") {
            webSocketService.sendMessage(["type": "date", ...])
        }
    }
}
```

**After:**
```swift
struct DatingView: View {
    @EnvironmentObject var gameState: GameStateViewModel
    @ObservedObject var viewModel: DatingViewModel

    var body: some View {
        Text("Energy: \(gameState.energy)")
        PrimaryButton(title: "Go on Date") {
            viewModel.startDate(with: character)
        }
    }
}
```

### Before & After: Styling

**Before:**
```swift
Text("Welcome")
    .font(.system(size: 28, weight: .bold, design: .rounded))
    .foregroundColor(Color(hex: 0x007AFF))
    .padding(16)
```

**After:**
```swift
Text("Welcome")
    .appTitleStyle()
    .padding(AppSpacing.md)
```

---

## Implementation Notes

1. **Preserve Functionality:** Every phase must maintain 100% feature parity
2. **Test Continuously:** Build and test after every file extraction
3. **Commit Frequently:** Commit after each phase completes
4. **Document Changes:** Update CLAUDE.md when architecture changes
5. **Seek Help:** If blocked on a phase, ask before proceeding

---

## Conclusion

This refactoring transforms the BaoLife iOS codebase from a maintenance liability into a scalable foundation for commercial success. The feature-based architecture aligns with the game's natural structure and enables rapid implementation of monetization and retention features identified in PROJECT_STATUS.md.

The 10-phase migration strategy ensures safe, incremental progress with testing checkpoints at every step. Upon completion, the codebase will be:
- 97% smaller in largest files
- Organized by clear feature boundaries
- Consistent in design and styling
- Ready for team collaboration
- Prepared for feature expansion

**Next Steps:**
1. Review and approve this design
2. Create git branch: `refactor/feature-based-architecture`
3. Begin Phase 1: Foundation Setup
4. Execute phases sequentially with testing between each
5. Update documentation upon completion
