Files
FrenoCorp/Lendair
Michael Freno d74f65b9d5 Fix error alert infinite loop in ClubsView and ChallengesView (FRE-4664)
- Replace unused lastError state with alertIsPresented boolean
- OK button now clears viewModel.error to prevent re-trigger
- Add onChange observer to show alert when error becomes non-nil
- Applies to both ClubsView.swift and ChallengesView.swift

Co-Authored-By: Paperclip <noreply@paperclip.ing>
2026-05-10 10:42:40 -04:00
..

Lendair iOS App

Overview

SwiftUI iOS app with modular feature architecture following MVVM pattern.

Architecture

MVVM Pattern

  • View: Views/ - SwiftUI views for all feature screens
  • ViewModel: ViewModels/ - State management and business logic
  • Service: Services/ - Data layer with API communication
  • Model: Models/ - Data structures and type definitions

File Structure

Lendair/
├── Models/
│   ├── Notification.swift          # NotificationItem, NotificationType, API response types
│   ├── TrainingPlan.swift          # TrainingPlan, PlanType, WorkoutSession, PlanProgress
│   ├── Race.swift                  # Race, RaceType, RaceFilter, API response types
│   ├── FamilyPlan.swift            # FamilyPlan, FamilyMember, LeaderboardMetric
│   ├── BeginnerMode.swift          # BeginnerConfig, Milestone, OnboardingStep
│   ├── CommunityEvent.swift        # CommunityEvent, EventType, RSVPStatus
│   ├── Club.swift                  # Club, ClubType, ClubPrivacy, MembershipStatus, ClubMember
│   └── Challenge.swift             # Challenge, ChallengeType, ChallengeStatus, LeaderboardEntry
├── Services/
│   ├── NotificationService.swift   # NotificationsServiceProtocol + implementation
│   ├── TrainingPlanService.swift   # TrainingPlanServiceProtocol + implementation
│   ├── RaceService.swift           # RaceServiceProtocol + implementation
│   ├── FamilyPlanService.swift     # FamilyPlanServiceProtocol + implementation
│   ├── BeginnerModeService.swift   # BeginnerModeServiceProtocol + implementation
│   ├── CommunityEventService.swift # CommunityEventServiceProtocol + implementation
│   ├── ClubService.swift           # ClubServiceProtocol + implementation
│   └── ChallengeService.swift      # ChallengeServiceProtocol + implementation
├── ViewModels/
│   ├── NotificationsViewModel.swift
│   ├── TrainingPlanViewModel.swift
│   ├── RaceDiscoveryViewModel.swift
│   ├── FamilyPlanViewModel.swift
│   ├── BeginnerModeViewModel.swift
│   ├── CommunityEventViewModel.swift
│   ├── ClubViewModel.swift
│   └── ChallengeViewModel.swift
├── Views/
│   ├── NotificationsView.swift
│   ├── NotificationRowView.swift
│   ├── TrainingPlanView.swift
│   ├── TrainingPlanDetailView.swift
│   ├── WorkoutSessionView.swift
│   ├── RaceDiscoveryView.swift
│   ├── RaceDetailView.swift
│   ├── FamilyPlanView.swift
│   ├── FamilyMemberView.swift
│   ├── BeginnerModeView.swift
│   ├── CommunityEventsView.swift
│   ├── CommunityEventDetailView.swift
│   ├── ClubsView.swift
│   ├── ClubDetailView.swift
│   ├── ChallengesView.swift
│   ├── ChallengeDetailView.swift
│   ├── MainTabView.swift
│   └── SettingsView.swift
├── Models/
│   ├── Notification.swift
│   ├── TrainingPlan.swift
│   ├── Race.swift
│   ├── FamilyPlan.swift
│   ├── BeginnerMode.swift
│   ├── CommunityEvent.swift
│   ├── Club.swift
│   ├── Challenge.swift
│   └── AppSettings.swift
└── README.md

Features

Notifications

  • Notification list with pull-to-refresh
  • Mark-as-read (individual and bulk)
  • Type-specific icons and color coding
  • Empty state handling

AI Training Plans (Phase 3 - Premium)

  • Personalized training plan generation (5K, 10K, Half/Full Marathon, Custom)
  • Difficulty levels: Beginner, Intermediate, Advanced, Elite
  • Weekly/daily workout scheduling with progressive overload
  • Plan progress tracking with session completion
  • Workout session execution with metrics display
  • Plan following/unfollowing

Race Discovery (Phase 3 - Premium)

  • Browse upcoming races by location, distance, type, terrain
  • Race detail pages with registration links
  • Save/bookmark races
  • Filter by race type (Road, Trail, Track, Virtual)
  • Calendar integration ready

Family Plans (Phase 3 - Premium)

  • Multi-member household management (up to 6 members)
  • Invite members via email
  • Individual progress tracking per member
  • Family leaderboard (distance, workouts, streak)
  • Subscription status management

Beginner Mode (Phase 3 - Premium)

  • Guided onboarding with step tracking
  • Progressive levels: Just Started → Getting Comfortable → Building Consistency → Progressing
  • Milestone achievements and tracking
  • Contextual tips and educational content
  • Simplified metric display

Community Events (Phase 3 - Premium)

  • Event discovery and creation
  • RSVP system (Going, Maybe, Not Going)
  • Event types: Group Run, Race, Workshop, Social, Charity, Training Camp
  • Participant tracking
  • Upcoming/ongoing/past event categorization

Clubs (Phase 2 - Community)

  • Persistent community groups for runners and fitness enthusiasts
  • Club types: Running, Walking, Cycling, Triathlon, CrossFit, General Fitness
  • Privacy levels: Public, Private, Invitation Only
  • Member management with roles (Owner, Admin, Member)
  • Invite members via email
  • Club rules and capacity limits
  • Discover clubs by type, location, and privacy

Challenges (Phase 2 - Community)

  • Time-bound competitive goals with progress tracking
  • Challenge types: Distance, Time, Frequency, Elevation, Calories, Streak
  • Real-time leaderboard with rankings
  • Progress submission and percentage tracking
  • Join/leave challenges
  • Create custom challenges with rules and targets
  • Active/upcoming/completed challenge categorization

Settings/About (Phase 2 - Core)

  • App version and build number display
  • Links to Terms of Service and Privacy Policy documents
  • User logout functionality
  • Account deletion option
  • Profile information display

Service Pattern

All services follow the same architecture:

  • Protocol: Sendable protocol for testability
  • Implementation: Configurable baseURL, URLSession, authToken
  • Error Handling: Typed error enums with LocalizedError conformance
  • HTTP Methods: GET, POST, PATCH, DELETE via shared helpers

API Endpoints

Notifications

Method Endpoint Description
GET /api/notifications?limit=&offset= List notifications
PATCH /api/notifications/:id/read Mark single as read
PATCH /api/notifications/read-all Mark all as read

Training Plans

Method Endpoint Description
GET /api/training-plans?type=&difficulty= List plans
GET /api/training-plans/:id Get plan detail
POST /api/training-plans/generate Generate AI plan
POST /api/training-plans/:id/follow Follow plan
DELETE /api/training-plans/:id/follow Unfollow plan
PATCH /api/training-plans/sessions/:id/status Update session status

Races

Method Endpoint Description
GET /api/races?type=&terrain=&... List races with filters
GET /api/races/:id Get race detail
POST/DELETE /api/races/:id/save Save/unsave race
POST /api/races/:id/register Register for race

Family Plans

Method Endpoint Description
GET /api/family-plan Get family plan
POST /api/family-plan/invite Invite member
DELETE /api/family-plan/members/:id Remove member
GET /api/family-plan/leaderboard?metric= Get leaderboard

Beginner Mode

Method Endpoint Description
GET /api/beginner-mode/config Get config
PATCH /api/beginner-mode/config Update config
GET /api/beginner-mode/milestones Get milestone progress

Community Events

Method Endpoint Description
GET /api/events?type=&rsvp=&... List events with filters
GET /api/events/:id Get event detail
POST /api/events Create event
PATCH /api/events/:id Update event
POST /api/events/:id/rsvp RSVP to event

Clubs

Method Endpoint Description
GET /api/clubs?type=&privacy=&... List clubs with filters
GET /api/clubs/:id Get club detail with members
POST /api/clubs Create club
PATCH /api/clubs/:id Update club
POST /api/clubs/:id/join Join club
POST /api/clubs/:id/leave Leave club
POST /api/clubs/:id/invite Invite member by email
DELETE /api/clubs/:id/members/:memberId Remove member

Challenges

Method Endpoint Description
GET /api/challenges?type=&status=&... List challenges with filters
GET /api/challenges/:id Get challenge detail with participants
POST /api/challenges Create challenge
PATCH /api/challenges/:id Update challenge
POST /api/challenges/:id/join Join challenge
POST /api/challenges/:id/leave Leave challenge
GET /api/challenges/:id/leaderboard Get challenge leaderboard
POST /api/challenges/:id/progress Submit progress

Settings/About

Method Endpoint Description
GET /api/settings/version Get app version and build number
GET /api/settings/legal/terms Get Terms of Service document
GET /api/settings/legal/privacy Get Privacy Policy document
POST /api/settings/logout User logout
DELETE /api/settings/account Delete user account

Testing

Tests are in LendairTests/:

  • Uses mock services conforming to feature protocols
  • ViewModel tests cover fetch, update, error handling, and computed properties
  • Model tests cover enum cases, display values, equality, and computed properties
  • Available test files: NotificationServiceTests.swift, ClubServiceTests.swift, ChallengeServiceTests.swift

Usage

// Feature views can be integrated into your navigation stack
NavigationStack {
    ClubsView()
}

NavigationStack {
    ChallengesView()
}

NavigationStack {
    CommunityEventsView()
}

Premium Features

All Phase 3 features (Training Plans, Race Discovery, Family Plans, Beginner Mode, Community Events) require a Pro subscription ($9.99/mo). Subscription status should be verified via the existing SubscriptionService before feature access.