// Scaffold a new SwiftUI feature module, extend an existing feature, or create a picker sheet with correct project conventions. Use when the user asks to create a new feature, screen, page, module, view, tab, picker, calendar, schedule, dashboard, settings, or add functionality to an existing feature. Also use when asked to build a neue Seite, neues Feature, neuer Screen, new screen, or new page in the FitnessApp iOS project.
[HINT] Download the complete skill directory including SKILL.md and all related files
name
create-feature
description
Scaffold a new SwiftUI feature module, extend an existing feature, or create a picker sheet with correct project conventions. Use when the user asks to create a new feature, screen, page, module, view, tab, picker, calendar, schedule, dashboard, settings, or add functionality to an existing feature. Also use when asked to build a neue Seite, neues Feature, neuer Screen, new screen, or new page in the FitnessApp iOS project.
Create the feature folder at FitnessApp/Features/<FeatureName>/.
Create the ViewModel — <FeatureName>ViewModel.swift:
import Foundation
import SwiftUI
class<FeatureName>ViewModel: ObservableObject {
// State exposed to the View@Publishedvar items: [Item] = []
// Inject services, never instantiate singletons in Viewsprivatelet storageService =<Service>.shared
init() {
// Bind to service publishers or load initial data
}
// All business logic lives herefuncperformAction() { }
}
Register navigation — add a case to NavigationDestination in FitnessAppApp.swift:
// In the enumcase<featureName>// In .navigationDestinationcase.<featureName>:
<FeatureName>View(navigationPath: $navigationPath)
.navigationBarBackButtonHidden(true)
.onAppear { overlayState.currentScene =.<scene> }
Verify placement — not everything is a feature module. See the "Where to Place New Code" table in architecture-documentation.md for pickers, shared views, utilities, and services.
Run the checklist below before finishing.
Extending an Existing Feature
When adding to an existing feature (new view, new service method, new chart type, etc.):
Read existing code first — understand the ViewModel's state and the View's structure before adding.
Add to the ViewModel — new data, new methods. Never add business logic to the View.
Reuse shared components — check the Shared Components table before building custom UI.
If adding a new navigable screen within the feature, register it in NavigationDestination.
If changing a model — new fields on Codable types must be optional or have a default value (existing JSON data will lack the field).
Add accessibility identifiers to all new interactive elements (.accessibilityIdentifier("id_<context>_<element>")) and matching Selector constants in FitnessAppUITests/Selectors/.
Run the checklist below.
Creating a Picker Sheet
Pickers belong in Features/Picker/, not in a feature folder. Use existing picker infrastructure:
Create the picker view in Features/Picker/<Name>PickerView.swift.
Use OverlaySheetContainer as the outermost wrapper — provides backdrop, grabber, swipe-dismiss, appear animation, and exercisePickerSheet styling automatically.
Use ExercisePickerActionButtons for the Cancel/Save button row (inside the container content).
Use ExerciseWheelPickerRow if the picker involves sets, reps, or weight wheels.
Use ExercisePickerInputField for any text input inside the picker.
Use WeightOptionsGenerator for weight option arrays.
Run the checklist below.
Checklist
All styling uses AppStyle tokens (Color, Font, Padding, CornerRadius, Opacity)
No hardcoded Color(hex:), .font(.system(...)), numeric .padding(), .cornerRadius(), .opacity()
If new font sizes/weights are needed, add tokens to AppStyle.Font first
ViewModel is ObservableObject with @Published properties
View owns ViewModel via @StateObject
No business logic in the View
Navigation registered in NavigationDestination enum
AppCurrentScene enum updated if new scene type
All interactive elements (buttons, text fields, tappable views) have .accessibilityIdentifier("id_<context>_<element>") — see naming patterns in ui-test-conventions.md
Matching Selector constants added in FitnessAppUITests/Selectors/<ScreenName>Selectors.swift
Unit tests written for ViewModel and Service logic (at minimum: initial state, main action, edge case). Place in the relevant Packages/*/Tests/ target. New services must have a protocol so they can be mocked in tests.
Snapshot test added when introducing a new public View in Packages/FitnessUI/Sources/ or Packages/FitnessPersistenceUI/Sources/. Add a @Suite to the package's SnapshotTests.swift (or IdleCardSnapshotTests.swift in FitnessPersistenceUI). One @Test per meaningful visual variant; use assertSnapshot(of:named:size:) — 100×100 for small components, or scoped to the component's natural bounds for larger ones. See reviewing-test-quality skill, "Snapshot Tests" section, for conventions.
architecture-documentation.md updated (Feature Map, Navigation, new shared components/services)