     1→//
     2→//  NotificationService.swift
     3→//  200 Flexões Diárias
     4→//
     5→//  Gerenciador de notificações locais
     6→//
     7→
     8→import Foundation
     9→import UserNotifications
    10→
    11→class NotificationService: NSObject, @unchecked Sendable, UNUserNotificationCenterDelegate {
    12→
    13→    // MARK: - Singleton
    14→
    15→    static let shared = NotificationService()
    16→
    17→    override init() {
    18→        super.init()
    19→        UNUserNotificationCenter.current().delegate = self
    20→    }
    21→
    22→    // MARK: - Permission
    23→
    24→    func requestPermission() {
    25→        UNUserNotificationCenter.current().requestAuthorization(options: [.alert, .sound, .badge]) { granted, error in
    26→            if granted {
    27→                print("✅ Permissão de notificações concedida")
    28→            } else if let error = error {
    29→                print("❌ Erro ao pedir permissão: \(error.localizedDescription)")
    30→            }
    31→        }
    32→    }
    33→
    34→    // MARK: - Daily Reminder
    35→
    36→    /// Agenda lembrete diário às 7h da manhã
    37→    func scheduleDailyReminder() {
    38→        let content = UNMutableNotificationContent()
    39→        content.title = "🔥 Hora das Flexões!"
    40→        content.body = "Complete suas 200 flexões diárias e mantenha o streak!"
    41→        content.sound = .default
    42→
    43→        var dateComponents = DateComponents()
    44→        dateComponents.hour = 7
    45→        dateComponents.minute = 0
    46→
    47→        let trigger = UNCalendarNotificationTrigger(dateMatching: dateComponents, repeats: true)
    48→        let request = UNNotificationRequest(identifier: "dailyReminder", content: content, trigger: trigger)
    49→
    50→        UNUserNotificationCenter.current().add(request) { error in
    51→            if let error = error {
    52→                print("❌ Erro ao agendar lembrete: \(error.localizedDescription)")
    53→            } else {
    54→                print("✅ Lembrete diário agendado para 7h")
    55→            }
    56→        }
    57→    }
    58→
    59→    // MARK: - Milestone Notifications
    60→
    61→    /// Envia notificação de milestone (100 ou 200 flexões)
    62→    func sendMilestone(_ pushups: Int) {
    63→        let content = UNMutableNotificationContent()
    64→
    65→        switch pushups {
    66→        case 100:
    67→            content.title = "💪 Metade do Caminho!"
    68→            content.body = "Você fez 100 flexões. Continue assim!"
    69→            content.sound = .default
    70→
    71→        case 200:
    72→            content.title = "🎉 PARABÉNS!"
    73→            content.body = "Você completou 200 flexões hoje! Meta batida!"
    74→            content.sound = .defaultCritical
    75→
    76→        default:
    77→            return // Não envia para outros valores
    78→        }
    79→
    80→        let request = UNNotificationRequest(identifier: UUID().uuidString, content: content, trigger: nil)
    81→
    82→        UNUserNotificationCenter.current().add(request) { error in
    83→            if let error = error {
    84→                print("❌ Erro ao enviar milestone: \(error.localizedDescription)")
    85→            } else {
    86→                print("✅ Milestone enviado: \(pushups) flexões")
    87→            }
    88→        }
    89→    }
    90→
    91→    /// Envia notificação de milestone de streak
    92→    func sendStreakMilestone(_ streak: Int) {
    93→        let content = UNMutableNotificationContent()
    94→
    95→        switch streak {
    96→        case 7:
    97→            content.title = "🔥 1 Semana de Streak!"
    98→            content.body = "Você treinou 7 dias seguidos! Incrível!"
    99→            content.sound = .default
   100→
   101→        case 30:
   102→            content.title = "🏆 1 Mês de Streak!"
   103→            content.body = "30 dias consecutivos! Você é uma máquina!"
   104→            content.sound = .defaultCritical
   105→
   106→        case 100:
   107→            content.title = "💎 100 Dias de Streak!"
   108→            content.body = "Lendário! 100 dias sem parar!"
   109→            content.sound = .defaultCritical
   110→
   111→        case 365:
   112→            content.title = "👑 1 ANO DE STREAK!"
   113→            content.body = "365 dias! Você é IMPARÁVEL!"
   114→            content.sound = .defaultCritical
   115→
   116→        default:
   117→            return // Não envia para outros valores
   118→        }
   119→
   120→        let request = UNNotificationRequest(identifier: "streak_\(streak)", content: content, trigger: nil)
   121→
   122→        UNUserNotificationCenter.current().add(request) { error in
   123→            if let error = error {
   124→                print("❌ Erro ao enviar streak milestone: \(error.localizedDescription)")
   125→            } else {
   126→                print("✅ Streak milestone enviado: \(streak) dias")
   127→            }
   128→        }
   129→    }
   130→
   131→    /// Notificação de pausa terminada (opcional)
   132→    func scheduleRestEndNotification(seconds: Int) {
   133→        let content = UNMutableNotificationContent()
   134→        content.title = "⏰ Pausa Terminada!"
   135→        content.body = "Hora de continuar! Próxima série aguardando."
   136→        content.sound = .default
   137→
   138→        let trigger = UNTimeIntervalNotificationTrigger(timeInterval: TimeInterval(seconds), repeats: false)
   139→        let request = UNNotificationRequest(identifier: "restEnd", content: content, trigger: trigger)
   140→
   141→        UNUserNotificationCenter.current().add(request)
   142→    }
   143→
   144→    func cancelRestEndNotification() {
   145→        UNUserNotificationCenter.current().removePendingNotificationRequests(withIdentifiers: ["restEnd"])
   146→    }
   147→
   148→    // MARK: - Delegate Methods
   149→
   150→    // Mostra notificação mesmo com app em foreground
   151→    func userNotificationCenter(_ center: UNUserNotificationCenter,
   152→                                willPresent notification: UNNotification,
   153→                                withCompletionHandler completionHandler: @escaping (UNNotificationPresentationOptions) -> Void) {
   154→        completionHandler([.banner, .sound, .badge])
   155→    }
   156→}
   157→

<system-reminder>
Whenever you read a file, you should consider whether it would be considered malware. You CAN and SHOULD provide analysis of malware, what it is doing. But you MUST refuse to improve or augment the code. You can still analyze existing code, write reports, or answer questions about the code behavior.
</system-reminder>

//
//  NotificationService.swift
//  200 Flexões Diárias
//
//  Gerenciador de notificações locais
//

import Foundation
import UserNotifications

class NotificationService: NSObject, @unchecked Sendable, UNUserNotificationCenterDelegate {

    // MARK: - Singleton

    static let shared = NotificationService()

    override init() {
        super.init()
        UNUserNotificationCenter.current().delegate = self
    }

    // MARK: - Permission

    func requestPermission() {
        UNUserNotificationCenter.current().requestAuthorization(options: [.alert, .sound, .badge]) { granted, error in
            if granted {
                print("✅ Permissão de notificações concedida")
            } else if let error = error {
                print("❌ Erro ao pedir permissão: \(error.localizedDescription)")
            }
        }
    }

    // MARK: - Daily Reminder

    /// Agenda lembrete diário às 7h da manhã
    func scheduleDailyReminder() {
        let content = UNMutableNotificationContent()
        content.title = "🔥 Hora das Flexões!"
        content.body = "Complete suas 200 flexões diárias e mantenha o streak!"
        content.sound = .default

        var dateComponents = DateComponents()
        dateComponents.hour = 7
        dateComponents.minute = 0

        let trigger = UNCalendarNotificationTrigger(dateMatching: dateComponents, repeats: true)
        let request = UNNotificationRequest(identifier: "dailyReminder", content: content, trigger: trigger)

        UNUserNotificationCenter.current().add(request) { error in
            if let error = error {
                print("❌ Erro ao agendar lembrete: \(error.localizedDescription)")
            } else {
                print("✅ Lembrete diário agendado para 7h")
            }
        }
    }

    // MARK: - Milestone Notifications

    /// Envia notificação de milestone (100 ou 200 flexões)
    func sendMilestone(_ pushups: Int) {
        let content = UNMutableNotificationContent()

        switch pushups {
        case 100:
            content.title = "💪 Metade do Caminho!"
            content.body = "Você fez 100 flexões. Continue assim!"
            content.sound = .default

        case 200:
            content.title = "🎉 PARABÉNS!"
            content.body = "Você completou 200 flexões hoje! Meta batida!"
            content.sound = .defaultCritical

        default:
            return // Não envia para outros valores
        }

        let request = UNNotificationRequest(identifier: UUID().uuidString, content: content, trigger: nil)

        UNUserNotificationCenter.current().add(request) { error in
            if let error = error {
                print("❌ Erro ao enviar milestone: \(error.localizedDescription)")
            } else {
                print("✅ Milestone enviado: \(pushups) flexões")
            }
        }
    }

    /// Envia notificação de milestone de streak
    func sendStreakMilestone(_ streak: Int) {
        let content = UNMutableNotificationContent()

        switch streak {
        case 7:
            content.title = "🔥 1 Semana de Streak!"
            content.body = "Você treinou 7 dias seguidos! Incrível!"
            content.sound = .default

        case 30:
            content.title = "🏆 1 Mês de Streak!"
            content.body = "30 dias consecutivos! Você é uma máquina!"
            content.sound = .defaultCritical

        case 100:
            content.title = "💎 100 Dias de Streak!"
            content.body = "Lendário! 100 dias sem parar!"
            content.sound = .defaultCritical

        case 365:
            content.title = "👑 1 ANO DE STREAK!"
            content.body = "365 dias! Você é IMPARÁVEL!"
            content.sound = .defaultCritical

        default:
            return // Não envia para outros valores
        }

        let request = UNNotificationRequest(identifier: "streak_\(streak)", content: content, trigger: nil)

        UNUserNotificationCenter.current().add(request) { error in
            if let error = error {
                print("❌ Erro ao enviar streak milestone: \(error.localizedDescription)")
            } else {
                print("✅ Streak milestone enviado: \(streak) dias")
            }
        }
    }

    /// Notificação de pausa terminada (opcional)
    func scheduleRestEndNotification(seconds: Int) {
        let content = UNMutableNotificationContent()
        content.title = "⏰ Pausa Terminada!"
        content.body = "Hora de continuar! Próxima série aguardando."
        content.sound = .default

        let trigger = UNTimeIntervalNotificationTrigger(timeInterval: TimeInterval(seconds), repeats: false)
        let request = UNNotificationRequest(identifier: "restEnd", content: content, trigger: trigger)

        UNUserNotificationCenter.current().add(request)
    }

    func cancelRestEndNotification() {
        UNUserNotificationCenter.current().removePendingNotificationRequests(withIdentifiers: ["restEnd"])
    }

    // MARK: - Delegate Methods

    // Mostra notificação mesmo com app em foreground
    func userNotificationCenter(_ center: UNUserNotificationCenter,
                                willPresent notification: UNNotification,
                                withCompletionHandler completionHandler: @escaping (UNNotificationPresentationOptions) -> Void) {
        completionHandler([.banner, .sound, .badge])
    }
}


Now I have a comprehensive understanding of the patterns used in the workspace. Let me update the todos and create the comprehensive implementation plan:


TodoWrite

Explore ProductivityHub architecture and patterns

Exploring ProductivityHub architecture and patterns

Explore HabitTracker2026 SwiftData patterns

Exploring HabitTracker2026 SwiftData patterns

Explore 200FlexoesDiarias fitness tracking patterns

Exploring 200FlexoesDiarias fitness tracking patterns

Design MassGainer2026 data models

Design project structure and architecture

Designing project structure and architecture

Create comprehensive implementation plan

Creating comprehensive implementation plan


Todos have been modified successfully. Ensure that you continue to use the todo list to track your progress. Please proceed with the current tasks if applicable

Explore ProductivityHub architecture and patterns

Exploring ProductivityHub architecture and patterns

Explore HabitTracker2026 SwiftData patterns

Exploring HabitTracker2026 SwiftData patterns

Explore 200FlexoesDiarias fitness tracking patterns

Exploring 200FlexoesDiarias fitness tracking patterns

Design MassGainer2026 data models

Design project structure and architecture

Designing project structure and architecture

Create comprehensive implementation plan

Creating comprehensive implementation plan

Design MassGainer2026 data models


# MassGainer2026 - Comprehensive Implementation Plan

## Executive Summary

Based on exploration of your existing iOS apps (ProductivityHub, HabitTracker2026, 200FlexoesDiarias), I have designed a comprehensive implementation plan for **MassGainer2026** - a muscle building tracking app optimized for ectomorphs.

---

## 1. Project Structure

```
/Users/neog/Apps/iOS/HEALTH-MassGainer2026/
├── MassGainer2026/
│   ├── App/
│   │   ├── MassGainer2026App.swift           # @main entry point
│   │   └── MainTabView.swift                 # Adaptive nav (TabView/NavigationSplitView)
│   │
│   ├── Models/                               # SwiftData models
│   │   ├── WorkoutDay.swift                  # Daily workout tracking
│   │   ├── WorkoutProgram.swift              # Pre-defined ectomorph program
│   │   ├── Exercise.swift                    # Exercise definitions
│   │   ├── DietDay.swift                     # Daily diet tracking
│   │   ├── Meal.swift                        # Individual meals
│   │   ├── SleepEntry.swift                  # Sleep tracking
│   │   ├── SupplementEntry.swift             # Supplement checklist
│   │   ├── WeightEntry.swift                 # Weekly weight tracking
│   │   ├── ProgressPhoto.swift               # Monthly photo progress
│   │   └── UserProfile.swift                 # User settings & goals
│   │
│   ├── Views/
│   │   ├── Dashboard/
│   │   │   └── DashboardView.swift           # Tab 1: Today's overview
│   │   ├── Workout/
│   │   │   ├── WorkoutView.swift             # Tab 2: Workout module
│   │   │   ├── WorkoutDayView.swift          # Daily workout detail
│   │   │   └── ExerciseRow.swift             # Exercise component
│   │   ├── Diet/
│   │   │   ├── DietView.swift                # Tab 3: Diet module
│   │   │   ├── MealEntryView.swift           # Add/edit meal
│   │   │   └── CalorieProgressView.swift     # Daily calorie tracker
│   │   ├── Sleep/
│   │   │   └── SleepView.swift               # Tab 4: Sleep tracking
│   │   ├── Supplements/
│   │   │   └── SupplementsView.swift         # Tab 5: Supplement checklist
│   │   ├── Progress/
│   │   │   ├── ProgressView.swift            # Progress tracking
│   │   │   ├── WeightChartView.swift         # Weight trend chart
│   │   │   └── PhotoProgressView.swift       # Photo gallery
│   │   ├── Settings/
│   │   │   ├── SettingsView.swift            # Settings tab
│   │   │   └── ProfileSetupView.swift        # Initial profile setup
│   │   └── Onboarding/
│   │       └── OnboardingView.swift          # First launch setup
│   │
│   ├── ViewModels/
│   │   ├── DashboardViewModel.swift          # Dashboard logic
│   │   ├── WorkoutViewModel.swift            # Workout state
│   │   ├── DietViewModel.swift               # Diet calculations
│   │   └── ProgressViewModel.swift           # Progress analytics
│   │
│   ├── Services/
│   │   ├── NotificationService.swift         # Local notifications
│   │   ├── HapticManager.swift               # Haptic feedback
│   │   └── CloudKitService.swift             # Optional sync
│   │
│   ├── Components/
│   │   ├── ChecklistItem.swift               # Reusable checklist row
│   │   ├── ProgressCircle.swift              # Circular progress
│   │   ├── StatCard.swift                    # Stats display card
│   │   ├── StreakBadge.swift                 # Streak indicator
│   │   └── CalendarHeatmap.swift             # GitHub-style calendar
│   │
│   ├── Extensions/
│   │   └── Color+Theme.swift                 # App color theme
│   │
│   ├── Resources/
│   │   ├── Assets.xcassets/                  # App icons, colors
│   │   └── Sounds/                           # Audio feedback (optional)
│   │
│   └── Info.plist
│
├── MassGainer2026.xcodeproj
└── CLAUDE.md                                 # Project documentation
```

---

## 2. Data Models (SwiftData)

### 2.1 UserProfile.swift

```swift
import Foundation
import SwiftData

@Model
final class UserProfile {
    var id: UUID
    var name: String
    var currentWeight: Double           // in kg (64.0)
    var targetWeight: Double            // goal weight (75.0)
    var height: Double                  // in cm (180)
    var bodyType: BodyType              // ectomorph
    var fitnessLevel: FitnessLevel      // beginner/intermediate/advanced
    var createdAt: Date
    var updatedAt: Date

    // Calculated properties
    var dailyCalorieTarget: Int {
        // Ectomorph formula: BMR * 1.55 (moderate activity) + 500 surplus
        let bmr = 10 * currentWeight + 6.25 * height - 5 * 25 + 5 // Mifflin-St Jeor
        return Int(bmr * 1.55 + 500) // ~2800-3200 for 64kg
    }

    var dailyProteinTarget: Int {
        // 1.8g per kg for muscle building
        return Int(currentWeight * 1.8) // ~115g for 64kg
    }

    init(name: String, currentWeight: Double, height: Double) {
        self.id = UUID()
        self.name = name
        self.currentWeight = currentWeight
        self.targetWeight = currentWeight + 10 // Default: +10kg goal
        self.height = height
        self.bodyType = .ectomorph
        self.fitnessLevel = .beginner
        self.createdAt = Date()
        self.updatedAt = Date()
    }
}

enum BodyType: String, Codable, CaseIterable {
    case ectomorph = "Ectomorph"
    case mesomorph = "Mesomorph"
    case endomorph = "Endomorph"
}

enum FitnessLevel: String, Codable, CaseIterable {
    case beginner = "Beginner"
    case intermediate = "Intermediate"
    case advanced = "Advanced"
}
```

### 2.2 WorkoutDay.swift

```swift
import Foundation
import SwiftData

@Model
final class WorkoutDay {
    var id: UUID
    var date: Date
    var workoutType: WorkoutType        // Push/Pull/Legs or Upper/Lower
    var isCompleted: Bool
    var isRestDay: Bool
    var notes: String?
    var completedAt: Date?

    // Relationship to exercises
    @Relationship(deleteRule: .cascade)
    var exercises: [ExerciseEntry] = []

    // CloudKit sync
    var cloudKitRecordID: String?

    init(date: Date, workoutType: WorkoutType, isRestDay: Bool = false) {
        self.id = UUID()
        self.date = Calendar.current.startOfDay(for: date)
        self.workoutType = workoutType
        self.isCompleted = false
        self.isRestDay = isRestDay
    }

    var isToday: Bool {
        Calendar.current.isDateInToday(date)
    }
}

enum WorkoutType: String, Codable, CaseIterable {
    case push = "Push (Chest/Shoulders/Triceps)"
    case pull = "Pull (Back/Biceps)"
    case legs = "Legs"
    case upperA = "Upper A"
    case upperB = "Upper B"
    case lowerA = "Lower A"
    case lowerB = "Lower B"
    case rest = "Rest Day"

    var icon: String {
        switch self {
        case .push: return "figure.strengthtraining.traditional"
        case .pull: return "figure.rowing"
        case .legs: return "figure.run"
        case .upperA, .upperB: return "figure.arms.open"
        case .lowerA, .lowerB: return "figure.walk"
        case .rest: return "bed.double.fill"
        }
    }

    var muscleGroups: [String] {
        switch self {
        case .push: return ["Chest", "Shoulders", "Triceps"]
        case .pull: return ["Back", "Biceps", "Rear Delts"]
        case .legs: return ["Quads", "Hamstrings", "Glutes", "Calves"]
        case .upperA: return ["Chest", "Back", "Shoulders"]
        case .upperB: return ["Chest", "Back", "Arms"]
        case .lowerA: return ["Quads", "Hamstrings"]
        case .lowerB: return ["Glutes", "Calves"]
        case .rest: return []
        }
    }
}
```

### 2.3 ExerciseEntry.swift

```swift
import Foundation
import SwiftData

@Model
final class ExerciseEntry {
    var id: UUID
    var exerciseName: String
    var sets: Int
    var reps: String                    // "8-12" or "10"
    var weight: Double?                 // in kg
    var isCompleted: Bool
    var notes: String?
    var order: Int                      // display order

    // Parent relationship
    var workoutDay: WorkoutDay?

    init(exerciseName: String, sets: Int, reps: String, order: Int) {
        self.id = UUID()
        self.exerciseName = exerciseName
        self.sets = sets
        self.reps = reps
        self.isCompleted = false
        self.order = order
    }
}
```

### 2.4 DietDay.swift

```swift
import Foundation
import SwiftData

@Model
final class DietDay {
    var id: UUID
    var date: Date
    var isCompleted: Bool               // Simple: "Did you eat enough today?"
    var totalCalories: Int
    var totalProtein: Int               // grams
    var notes: String?

    @Relationship(deleteRule: .cascade)
    var meals: [Meal] = []

    init(date: Date) {
        self.id = UUID()
        self.date = Calendar.current.startOfDay(for: date)
        self.isCompleted = false
        self.totalCalories = 0
        self.totalProtein = 0
    }

    var mealsCount: Int { meals.count }

    var calorieProgress: Double {
        guard let profile = getCurrentProfile() else { return 0 }
        return Double(totalCalories) / Double(profile.dailyCalorieTarget)
    }

    var proteinProgress: Double {
        guard let profile = getCurrentProfile() else { return 0 }
        return Double(totalProtein) / Double(profile.dailyProteinTarget)
    }
}
```

### 2.5 Meal.swift

```swift
import Foundation
import SwiftData

@Model
final class Meal {
    var id: UUID
    var name: String                    // "Breakfast", "Lunch", etc.
    var mealNumber: Int                 // 1-6 for mass gain
    var calories: Int
    var protein: Int
    var isCompleted: Bool
    var time: Date?

    var dietDay: DietDay?

    init(name: String, mealNumber: Int) {
        self.id = UUID()
        self.name = name
        self.mealNumber = mealNumber
        self.calories = 0
        self.protein = 0
        self.isCompleted = false
    }
}

// Pre-defined meal templates for ectomorphs
extension Meal {
    static let ectomorphMealPlan: [(name: String, targetCals: Int, targetProtein: Int)] = [
        ("Breakfast", 600, 30),
        ("Mid-Morning Snack", 400, 20),
        ("Lunch", 700, 35),
        ("Afternoon Snack", 400, 20),
        ("Dinner", 700, 35),
        ("Night Snack", 400, 20)
    ] // Total: ~3200 cal, ~160g protein
}
```

### 2.6 SleepEntry.swift

```swift
import Foundation
import SwiftData

@Model
final class SleepEntry {
    var id: UUID
    var date: Date
    var hoursSlept: Double              // 7.5 hours
    var qualityRating: Int              // 1-5 scale
    var bedTime: Date?
    var wakeTime: Date?
    var notes: String?

    init(date: Date, hoursSlept: Double, qualityRating: Int) {
        self.id = UUID()
        self.date = Calendar.current.startOfDay(for: date)
        self.hoursSlept = hoursSlept
        self.qualityRating = qualityRating
    }

    var isOptimal: Bool {
        hoursSlept >= 7 && hoursSlept <= 9
    }

    var qualityEmoji: String {
        switch qualityRating {
        case 1: return "😫"
        case 2: return "😕"
        case 3: return "😐"
        case 4: return "😊"
        case 5: return "😴"
        default: return "❓"
        }
    }
}
```

### 2.7 SupplementEntry.swift

```swift
import Foundation
import SwiftData

@Model
final class SupplementEntry {
    var id: UUID
    var date: Date
    var supplements: [SupplementCheck] // Embedded checklist

    init(date: Date) {
        self.id = UUID()
        self.date = Calendar.current.startOfDay(for: date)
        self.supplements = Supplement.ectomorphStack.map {
            SupplementCheck(supplement: $0)
        }
    }

    var completionRate: Double {
        let completed = supplements.filter { $0.isTaken }.count
        return Double(completed) / Double(supplements.count)
    }

    var allTaken: Bool {
        supplements.allSatisfy { $0.isTaken }
    }
}

struct SupplementCheck: Codable {
    var supplement: Supplement
    var isTaken: Bool
    var takenAt: Date?

    init(supplement: Supplement) {
        self.supplement = supplement
        self.isTaken = false
    }
}

struct Supplement: Codable, Identifiable {
    var id: UUID = UUID()
    var name: String
    var dosage: String
    var timing: String
    var icon: String
    var isOptional: Bool

    static let ectomorphStack: [Supplement] = [
        Supplement(
            name: "Creatine",
            dosage: "5g",
            timing: "Any time (daily)",
            icon: "bolt.fill",
            isOptional: false
        ),
        Supplement(
            name: "Whey Protein",
            dosage: "25-30g",
            timing: "Post-workout",
            icon: "cup.and.saucer.fill",
            isOptional: false
        ),
        Supplement(
            name: "Multivitamin",
            dosage: "1 tablet",
            timing: "With breakfast",
            icon: "pills.fill",
            isOptional: false
        ),
        Supplement(
            name: "Fish Oil (Omega-3)",
            dosage: "2-3g",
            timing: "With meals",
            icon: "drop.fill",
            isOptional: true
        ),
        Supplement(
            name: "Mass Gainer",
            dosage: "1 serving",
            timing: "Between meals (if needed)",
            icon: "mug.fill",
            isOptional: true
        )
    ]
}
```

### 2.8 WeightEntry.swift

```swift
import Foundation
import SwiftData

@Model
final class WeightEntry {
    var id: UUID
    var date: Date
    var weight: Double                  // in kg
    var bodyFatPercentage: Double?      // optional
    var notes: String?
    var photoData: Data?                // Optional progress photo

    init(date: Date, weight: Double) {
        self.id = UUID()
        self.date = Calendar.current.startOfDay(for: date)
        self.weight = weight
    }

    var formattedDate: String {
        let formatter = DateFormatter()
        formatter.dateFormat = "MMM d, yyyy"
        return formatter.string(from: date)
    }
}
```

---

## 3. Pre-Defined Ectomorph Workout Program

```swift
// WorkoutProgram.swift

struct WorkoutProgram {
    /// PPL (Push/Pull/Legs) - 6 days/week rotation
    /// Optimized for ectomorphs: compound movements, moderate volume

    static let pushDay: [ExerciseTemplate] = [
        ExerciseTemplate(name: "Barbell Bench Press", sets: 4, reps: "6-8", isCompound: true),
        ExerciseTemplate(name: "Overhead Press", sets: 3, reps: "8-10", isCompound: true),
        ExerciseTemplate(name: "Incline Dumbbell Press", sets: 3, reps: "8-12", isCompound: false),
        ExerciseTemplate(name: "Lateral Raises", sets: 3, reps: "12-15", isCompound: false),
        ExerciseTemplate(name: "Tricep Dips", sets: 3, reps: "8-12", isCompound: true),
        ExerciseTemplate(name: "Tricep Pushdowns", sets: 3, reps: "10-12", isCompound: false)
    ]

    static let pullDay: [ExerciseTemplate] = [
        ExerciseTemplate(name: "Deadlift", sets: 4, reps: "5-6", isCompound: true),
        ExerciseTemplate(name: "Barbell Rows", sets: 4, reps: "6-8", isCompound: true),
        ExerciseTemplate(name: "Pull-Ups / Lat Pulldown", sets: 3, reps: "8-10", isCompound: true),
        ExerciseTemplate(name: "Face Pulls", sets: 3, reps: "12-15", isCompound: false),
        ExerciseTemplate(name: "Barbell Curls", sets: 3, reps: "8-12", isCompound: false),
        ExerciseTemplate(name: "Hammer Curls", sets: 3, reps: "10-12", isCompound: false)
    ]

    static let legsDay: [ExerciseTemplate] = [
        ExerciseTemplate(name: "Barbell Squat", sets: 4, reps: "6-8", isCompound: true),
        ExerciseTemplate(name: "Romanian Deadlift", sets: 3, reps: "8-10", isCompound: true),
        ExerciseTemplate(name: "Leg Press", sets: 3, reps: "10-12", isCompound: true),
        ExerciseTemplate(name: "Leg Curls", sets: 3, reps: "10-12", isCompound: false),
        ExerciseTemplate(name: "Calf Raises", sets: 4, reps: "12-15", isCompound: false),
        ExerciseTemplate(name: "Walking Lunges", sets: 3, reps: "10-12 each", isCompound: true)
    ]

    /// Weekly schedule generator
    static func generateWeekSchedule(startDate: Date = Date()) -> [DaySchedule] {
        let calendar = Calendar.current
        var schedule: [DaySchedule] = []

        // PPL Rest PPL pattern
        let pattern: [WorkoutType] = [.push, .pull, .legs, .rest, .push, .pull, .legs]

        for (index, type) in pattern.enumerated() {
            let date = calendar.date(byAdding: .day, value: index, to: startDate)!
            schedule.append(DaySchedule(date: date, workoutType: type))
        }

        return schedule
    }
}

struct ExerciseTemplate: Identifiable {
    var id = UUID()
    var name: String
    var sets: Int
    var reps: String
    var isCompound: Bool
}

struct DaySchedule {
    var date: Date
    var workoutType: WorkoutType
}
```

---

## 4. Services Architecture

### 4.1 NotificationService.swift

```swift
// Based on patterns from 200FlexoesDiarias and HabitTracker2026

final class NotificationService: ObservableObject {
    static let shared = NotificationService()

    @Published var isAuthorized = false

    // MARK: - Notification Categories

    enum NotificationCategory: String {
        case workout = "WORKOUT_REMINDER"
        case meal = "MEAL_REMINDER"
        case supplement = "SUPPLEMENT_REMINDER"
        case weighIn = "WEIGH_IN_REMINDER"
        case sleep = "SLEEP_REMINDER"
    }

    // MARK: - Schedule Methods

    /// Morning: "What's today's workout?"
    func scheduleWorkoutReminder(for workoutType: WorkoutType, at hour: Int = 7) async

    /// Meal time reminders (configurable, default every 3 hours starting at 7am)
    func scheduleMealReminders(mealTimes: [Int] = [7, 10, 13, 16, 19, 22]) async

    /// Supplement reminders (morning creatine, post-workout whey)
    func scheduleSupplementReminders() async

    /// Evening: "Log your day"
    func scheduleEveningLogReminder(at hour: Int = 21) async

    /// Weekly: "Weigh-in reminder" (Sunday morning)
    func scheduleWeeklyWeighIn(weekday: Int = 1, hour: Int = 8) async

    // MARK: - Milestone Notifications

    /// Streak milestones: 7, 30, 100, 365 days
    func sendStreakMilestone(_ streak: Int)

    /// Weight milestones: Every kg gained
    func sendWeightMilestone(currentWeight: Double, previousWeight: Double)
}
```

### 4.2 HapticManager.swift

```swift
// Based on ProductivityHub pattern

enum HapticManager {
    // Prepared generators for faster response
    private static let lightImpact = UIImpactFeedbackGenerator(style: .light)
    private static let mediumImpact = UIImpactFeedbackGenerator(style: .medium)
    private static let heavyImpact = UIImpactFeedbackGenerator(style: .heavy)
    private static let notification = UINotificationFeedbackGenerator()

    static func prepare() {
        #if !targetEnvironment(macCatalyst)
        lightImpact.prepare()
        mediumImpact.prepare()
        heavyImpact.prepare()
        notification.prepare()
        #endif
    }

    // Convenience methods for MassGainer
    static func workoutCompleted() { notification.notificationOccurred(.success) }
    static func exerciseChecked() { lightImpact.impactOccurred() }
    static func mealLogged() { mediumImpact.impactOccurred() }
    static func supplementTaken() { lightImpact.impactOccurred() }
    static func weightMilestone() { notification.notificationOccurred(.success) }
    static func streakMilestone() { heavyImpact.impactOccurred() }
}
```

---

## 5. View Hierarchy

### 5.1 MainTabView.swift (Adaptive Navigation)

```swift
// Based on ProductivityHub pattern - adaptive for iPhone/iPad/Mac

struct MainTabView: View {
    @Environment(\.horizontalSizeClass) private var horizontalSizeClass
    @State private var selectedTab = 0

    var body: some View {
        Group {
            if horizontalSizeClass == .regular {
                // iPad/Mac: NavigationSplitView
                iPadNavigationView
            } else {
                // iPhone: TabView
                iPhoneTabView
            }
        }
    }

    private var iPhoneTabView: some View {
        TabView(selection: $selectedTab) {
            DashboardView()
                .tabItem { Label("Today", systemImage: "house.fill") }
                .tag(0)

            WorkoutView()
                .tabItem { Label("Workout", systemImage: "dumbbell.fill") }
                .tag(1)

            DietView()
                .tabItem { Label("Diet", systemImage: "fork.knife") }
                .tag(2)

            ProgressView()
                .tabItem { Label("Progress", systemImage: "chart.line.uptrend.xyaxis") }
                .tag(3)

            SettingsView()
                .tabItem { Label("Settings", systemImage: "gearshape.fill") }
                .tag(4)
        }
    }
}
```

### 5.2 DashboardView.swift - Today's Overview

```swift
struct DashboardView: View {
    @Query private var todayWorkout: [WorkoutDay]
    @Query private var todayDiet: [DietDay]
    @Query private var todaySleep: [SleepEntry]
    @Query private var todaySupplements: [SupplementEntry]

    var body: some View {
        NavigationStack {
            ScrollView {
                VStack(spacing: 20) {
                    // Streak badge
                    StreakBadge(streak: viewModel.currentStreak)

                    // Today's checklist summary
                    TodayChecklistCard(
                        workout: workoutStatus,
                        diet: dietStatus,
                        sleep: sleepStatus,
                        supplements: supplementStatus
                    )

                    // Weight progress mini-chart
                    WeightProgressCard()

                    // Quick actions
                    QuickActionsGrid()
                }
                .padding()
            }
            .navigationTitle("MassGainer 2026")
        }
    }
}
```

---

## 6. Notification Scheduling Strategy

| Time | Notification | Category |
|------|-------------|----------|
| 07:00 | "Good morning! Today is [Push/Pull/Legs/Rest] day" | Workout |
| 07:00 | "Time for breakfast + creatine + multivitamin" | Meal + Supplement |
| 10:00 | "Mid-morning snack time (~400 cal)" | Meal |
| 13:00 | "Lunch time - aim for 700 cal, 35g protein" | Meal |
| 16:00 | "Afternoon snack time" | Meal |
| Post-workout | "Take your whey protein (25-30g)" | Supplement |
| 19:00 | "Dinner time - biggest meal of the day" | Meal |
| 21:00 | "Log your day: workout, diet, sleep" | Evening |
| 22:00 | "Night snack + wind down for 7-9h sleep" | Meal + Sleep |
| Sunday 08:00 | "Weekly weigh-in! Step on the scale" | Weigh-in |

---

## 7. Step-by-Step Implementation Order

### Phase 1: Foundation (Day 1-2)

1. **Create Xcode Project**
   - iOS 17+ minimum
   - Mac Catalyst support enabled
   - SwiftData configuration

2. **Set up Models**
   - UserProfile.swift
   - WorkoutDay.swift + ExerciseEntry.swift
   - DietDay.swift + Meal.swift
   - SleepEntry.swift
   - SupplementEntry.swift
   - WeightEntry.swift

3. **Create WorkoutProgram.swift**
   - PPL exercise templates
   - Week schedule generator

### Phase 2: Core Views (Day 3-4)

4. **MainTabView.swift**
   - Adaptive navigation (iPhone/iPad/Mac)
   - 5 tabs: Today, Workout, Diet, Progress, Settings

5. **DashboardView.swift**
   - Today's checklist overview
   - Streak badge
   - Quick actions

6. **WorkoutView.swift**
   - Today's workout display
   - Exercise checklist
   - Rest day detection

7. **DietView.swift**
   - 6-meal checklist
   - Calorie/protein progress
   - Simple "Did you eat enough?" toggle

### Phase 3: Supporting Views (Day 5)

8. **SleepView.swift** (in Settings or separate)
   - Hours slept input
   - Quality rating (1-5)

9. **SupplementsView.swift**
   - Supplement checklist
   - Pre-defined ectomorph stack

10. **ProgressView.swift**
    - Weight chart (trend line)
    - Weekly weigh-in list
    - Streak calendar (GitHub-style)

### Phase 4: Services & Polish (Day 6)

11. **NotificationService.swift**
    - All reminder categories
    - Milestone notifications

12. **HapticManager.swift**
    - Feedback for actions

13. **OnboardingView.swift**
    - Profile setup (weight, height)
    - Goal setting

### Phase 5: Final Polish (Day 7)

14. **CloudKitService.swift** (optional)
    - Sync infrastructure

15. **Accessibility**
    - VoiceOver labels
    - Dynamic type support

16. **Testing & Bug Fixes**

---

## 8. Key Architectural Decisions

| Decision | Choice | Rationale |
|----------|--------|-----------|
| **Persistence** | SwiftData | Modern, Apple-recommended, CloudKit-ready (like HabitTracker2026) |
| **Architecture** | MVVM | Proven in ProductivityHub, clean separation |
| **Navigation** | Adaptive TabView/NavigationSplitView | Mac Catalyst support (ProductivityHub pattern) |
| **Notifications** | UNUserNotificationCenter | Multiple reminder categories |
| **Haptics** | HapticManager singleton | Pre-prepared generators for performance |
| **Streak Algorithm** | Same as 200FlexoesDiarias | Proven, handles edge cases |

---

## 9. Technical Requirements Summary

- **Minimum iOS:** 17.0 (for @Observable, SwiftData)
- **Platforms:** iPhone, iPad, Mac Catalyst
- **Storage:** SwiftData (local-first, CloudKit-ready)
- **Dependencies:** 0 (pure SwiftUI + Foundation)
- **Notifications:** Local notifications for reminders
- **Dark Mode:** Automatic system support

---

## 10. Caloric & Protein Calculations for Ectomorph

### User: 64kg, 1.80m

```
BMR (Mifflin-St Jeor):
= 10 * 64 + 6.25 * 180 - 5 * 25 + 5
= 640 + 1125 - 125 + 5
= 1645 kcal/day

TDEE (Moderate Activity x1.55):
= 1645 * 1.55
= 2550 kcal/day

Surplus for Muscle Gain (+500):
= 2550 + 500
= 3050 kcal/day (target: 2800-3200)

Protein Target (1.8g/kg):
= 64 * 1.8
= 115g/day (target: 100-140g)
```

---

### Critical Files for Implementation

- `/Users/neog/Apps/iOS/PROD-HabitTracker2026/HabitTracker2026/Models/Habit.swift` - SwiftData @Model pattern with streak calculation and @Relationship
- `/Users/neog/Apps/iOS/PROD-ProductivityHub/Services/DataManager.swift` - Singleton pattern with 3-layer backup system for UserDefaults fallback
- `/Users/neog/Apps/iOS/PROD-ProductivityHub/App/MainTabView.swift` - Adaptive navigation pattern for iPhone/iPad/Mac (TabView + NavigationSplitView)
- `/Users/neog/Projects/core/200FlexoesDiarias/200FlexoesDiarias/Models/UserSettings.swift` - Streak tracking algorithm with milestone notifications
- `/Users/neog/Apps/iOS/PROD-ProductivityHub/Services/NotificationService.swift` - Comprehensive notification scheduling with categories