SwiftUI WorkoutApp — iOS street workout app, Swift 6.2, iOS 16+, MVVM architecture, modular structure. Xcode project (.xcodeproj) with local Swift Packages under SwiftUI-WorkoutApp/Libraries/ and external SPM dependencies (e.g. Firebase).
For app build and tests, use xcodebuild-mcp first. Use make commands below only as fallback if MCP is unavailable and cannot be fixed.
make formatRuns swiftformat . using .swiftformat config, then markdownlint --fix. A pre-push git hook (.githooks/pre-push) enforces swiftformat --lint . — unformatted code will be rejected on push.
make buildmake testTest plan includes: WorkoutAppTests, SWNetworkTests, SWModelsTest, CachedAsyncImageTests, SWUtilsTests, SWKeychainTests, ClusteringMapViewTests.
# Single test target
xcodebuild ... test -only-testing:WorkoutAppTests
# Single test class
xcodebuild ... test -only-testing:WorkoutAppTests/DefaultsServiceTests
# Single test function
xcodebuild ... test -only-testing:WorkoutAppTests/DefaultsServiceTests/triggerLogoutManuallyTrueCallsAuthHelperswift test --package-path SwiftUI-WorkoutApp/Libraries/SWModels
swift test --package-path SwiftUI-WorkoutApp/Libraries/SWUtils
swift test --package-path SwiftUI-WorkoutApp/Libraries/SWKeychain
swift test --package-path SwiftUI-WorkoutApp/Libraries/SWNetworkSwiftUI-WorkoutApp/
├── Screens/ # All screens (Root, Parks, Events, Profile, Messages, More, Common)
├── Services/ # Business logic (DefaultsService, ParksManager, GeocodingService, etc.)
├── Libraries/ # Local Swift Packages
│ ├── SWModels/ # Shared data models (Codable structs)
│ ├── SWNetwork/ # Network layer
│ ├── SWNetworkClient/ # API client (SWClient implementing protocol-based clients)
│ ├── SWUtils/ # Shared utilities
│ ├── SWKeychain/ # Keychain wrapper
│ ├── SWDesignSystem/ # Design system (colors, fonts, components)
│ ├── CachedAsyncImage/ # Async image caching
│ └── ClusteringMapView/# Map clustering
├── Extensions/ # Swift extensions
├── EnvironmentKeys/ # Custom SwiftUI environment keys
├── PreviewContent/ # SwiftUI preview data
├── Resources/ # Assets and resources
├── WorkoutAppTests/ # Unit tests
└── WorkoutAppUITests/ # UI tests
ViewModels live as extensions in the same file or a +ViewModel.swift file next to their screen.
- MVVM: Views → ViewModel (ObservableObject) → Service/Manager → Client
- DI:
@EnvironmentObjectfor passing services/view models down the hierarchy - Single user: Only one user at a time; logout clears all user data
- Network layer: Protocol-based client interfaces in
Services/Protocols/.SWClientconforms to all client protocols - State management:
@Statefor local,@Published+ObservableObjectfor view models,@AppStoragefor UserDefaults,@KeychainWrapperfor Keychain,SWFileManagerfor JSON file storage
- Sort imports alphabetically (swiftformat
sortImports) @testable importlast in test files- Framework imports first (
import Foundation,import SwiftUI), then project modules (import SWModels)
- Max line width: 140 characters, trailing/semicolons: never
self: only ininitand closures#if/#endif: no indent; braces: same-line@ViewBuilderonly for conditional logic (if/else) or multiple views — NOT for simple containers
- ViewModels:
SomeScreen.ViewModel(nested type) orSomeScreen+ViewModel.swift(extension) - Services/Managers:
SomethingService/SomethingManager - Models: structs with
Codable, suffixResponsefor API response models - One file = one component/type
- View properties without params:
var someView: some View - View factory methods with params:
func makeSomeView(for:) -> some View - Test descriptions: Use
@Test("description in Russian")
- ViewModels:
@MainActor final class ... : ObservableObject - Services with mutable state:
final class; without:struct - Models:
structconforming toCodable
- NEVER force unwrap (
!) — useguard let,if let,??, optional chaining - In tests:
try #require(optionalValue) - Use
OSLog(Logger), NOTprint()or TODO comments - Logger:
Logger(subsystem: Bundle.main.bundleIdentifier!, category: "ClassName")
- Swift Testing framework (
import Testing,@Test,#expect,#require) — NOT XCTest - Test structs (not classes):
struct SomeTests { ... } - Mocks go in
WorkoutAppTests/Mocks/or alongside test files - TDD: write failing test → implement minimum code →
make format && make test→ refactor
- Do NOT use UIKit when SwiftUI suffices (UIKit acceptable when SwiftUI cannot provide needed functionality)
- Do NOT use Core Data
- Do NOT leave unused code after refactoring
- Do NOT add unused methods/functions "just in case"
- Do NOT skip
make formatafter code changes - Do NOT use force unwrap (
!) anywhere - Do NOT use
print()— useLogger - Do NOT modify files outside this project without explicit approval