mirror of
https://github.com/Dimillian/Skills.git
synced 2026-03-25 08:55:54 +00:00
Update SKILL.md to refine project scaffolding instructions and introduce a state ownership matrix. Added detailed navigation and routing guidelines, preview guidance, async task lifecycle recommendations, performance guardrails, environment injection policies, and anti-patterns to enhance SwiftUI development practices.
This commit is contained in:
parent
ce79ef24da
commit
1ec2d7af93
1 changed files with 77 additions and 7 deletions
|
|
@ -20,7 +20,7 @@ Choose a track based on your goal:
|
|||
|
||||
### New project scaffolding
|
||||
|
||||
- Start with `references/app-scaffolding-wiring.md` to wire TabView + NavigationStack + sheets.
|
||||
- Start with `references/app-wiring.md` to wire TabView + NavigationStack + sheets.
|
||||
- Add a minimal `AppTab` and `RouterPath` based on the provided skeletons.
|
||||
- Choose the next component reference based on the UI you need first (TabView, NavigationStack, Sheets).
|
||||
- Expand the route and sheet enums as new screens are added.
|
||||
|
|
@ -36,14 +36,84 @@ Choose a track based on your goal:
|
|||
- **Sheets**: Prefer `.sheet(item:)` over `.sheet(isPresented:)` when state represents a selected model. Avoid `if let` inside a sheet body. Sheets should own their actions and call `dismiss()` internally instead of forwarding `onCancel`/`onConfirm` closures.
|
||||
- **Scroll-driven reveals**: Prefer deriving a normalized progress value from scroll offset and driving the visual state from that single source of truth. Avoid parallel gesture state machines unless scroll alone cannot express the interaction.
|
||||
|
||||
## State ownership matrix
|
||||
|
||||
Use the narrowest state tool that matches the ownership model:
|
||||
|
||||
| Scenario | Preferred pattern |
|
||||
| --- | --- |
|
||||
| Local UI state owned by one view | `@State` |
|
||||
| Child mutates parent-owned value state | `@Binding` |
|
||||
| Root-owned reference model on iOS 17+ | `@State` with an `@Observable` type |
|
||||
| Child reads or mutates an injected `@Observable` model | Pass it explicitly as a stored property |
|
||||
| Shared app service or configuration | `@Environment(Type.self)` |
|
||||
| Legacy reference model on iOS 16 and earlier | `@StateObject` at the root, `@ObservedObject` when injected |
|
||||
|
||||
Choose the ownership location first, then pick the wrapper. Do not introduce a reference model when plain value state is enough.
|
||||
|
||||
## Navigation and routing
|
||||
|
||||
- Use `NavigationStack` and local route state for most features. Keep navigation ownership close to the feature unless multiple entry points truly need shared routing.
|
||||
- In tabbed apps, prefer one navigation history per tab instead of a single shared stack for the entire app.
|
||||
- Use enum-driven sheet, alert, and destination routing when presentation is mutually exclusive or deep-linkable.
|
||||
- Centralize route enums only when the feature has deep links, handoff from multiple surfaces, or cross-feature navigation requirements.
|
||||
- Avoid global routers for simple push flows that can stay local to one screen tree.
|
||||
- See `references/navigationstack.md`, `references/sheets.md`, and `references/deeplinks.md` when the view needs more than straightforward local navigation.
|
||||
|
||||
## Preview guidance
|
||||
|
||||
- Add `#Preview` coverage for the primary state plus important secondary states such as loading, empty, and error.
|
||||
- Use deterministic fixtures, mocks, and sample data. Do not make previews depend on live network calls, real databases, or global singletons.
|
||||
- Install required environment dependencies directly in the preview so the view can render in isolation.
|
||||
- Keep preview setup close to the view until it becomes noisy; then extract lightweight preview helpers or fixtures.
|
||||
- If a preview crashes, fix the state initialization or dependency wiring before expanding the feature further.
|
||||
|
||||
## Async and task lifecycle
|
||||
|
||||
- Use `.task` for load-on-appear work that belongs to the view lifecycle.
|
||||
- Use `.task(id:)` when async work should restart for a changing input such as a query, selection, or identifier.
|
||||
- Treat cancellation as a normal path for view-driven tasks. Check `Task.isCancelled` in longer flows and avoid surfacing cancellation as a user-facing error.
|
||||
- Debounce or coalesce user-driven async work such as search before it fans out into repeated requests.
|
||||
- Keep UI-facing models and mutations main-actor-safe; do background work in services, then publish the result back to UI state.
|
||||
|
||||
## Performance guardrails
|
||||
|
||||
- Give `ForEach` and list content stable identity. Do not use unstable indices as identity when the collection can reorder or mutate.
|
||||
- Keep expensive filtering, sorting, and formatting out of `body`; precompute or move it into a model/helper when it is not trivial.
|
||||
- Narrow observation scope so only the views that read changing state need to update.
|
||||
- Prefer lazy containers for larger scrolling content and extract subviews when only part of a screen changes frequently.
|
||||
- Avoid swapping entire top-level view trees for small state changes; keep a stable root view and vary localized sections or modifiers.
|
||||
|
||||
## Environment injection policy
|
||||
|
||||
- Use `@Environment` for app-level services, shared clients, theme/configuration, and values that many descendants genuinely need.
|
||||
- Prefer initializer injection for feature-local dependencies and models. Do not move a dependency into the environment just to avoid passing one or two arguments.
|
||||
- Keep mutable feature state out of the environment unless it is intentionally shared across broad parts of the app.
|
||||
- Use `@EnvironmentObject` only as a legacy fallback or when the project already standardizes on it for a truly shared object.
|
||||
|
||||
## Anti-patterns
|
||||
|
||||
- Giant views that mix layout, business logic, networking, routing, and formatting in one file.
|
||||
- Multiple boolean flags for mutually exclusive sheets, alerts, or navigation destinations.
|
||||
- Live service calls directly inside `body`-driven code paths instead of view lifecycle hooks or injected models/services.
|
||||
- Reaching for `AnyView` to work around type mismatches that should be solved with better composition.
|
||||
- Defaulting every shared dependency to `@EnvironmentObject` or a global router without a clear ownership reason.
|
||||
|
||||
## Platform and version guidance
|
||||
|
||||
- Prefer the newest SwiftUI API that fits the deployment target, but call out the minimum OS whenever guidance depends on it.
|
||||
- When using iOS 17+ Observation or iOS 26+ UI APIs, include a fallback for older targets if the skill is meant to support them.
|
||||
- Keep compatibility notes next to the rule or example they affect so the fallback is easy to apply.
|
||||
- Avoid mixing the new Observation system and legacy Combine-based observation in the same feature unless compatibility requires it.
|
||||
|
||||
## Workflow for a new SwiftUI view
|
||||
|
||||
1. Define the view's state and its ownership location.
|
||||
2. Identify dependencies to inject via `@Environment`.
|
||||
3. Sketch the view hierarchy and extract repeated parts into subviews. **Build and verify no compiler errors before proceeding.**
|
||||
4. Implement async loading with `.task` and explicit state enum if needed.
|
||||
5. Add accessibility labels or identifiers when the UI is interactive.
|
||||
6. Validate with a build: confirm no compiler errors, check that previews render without crashing, and ensure state changes propagate correctly. For common SwiftUI compilation errors — missing `@State` annotations, ambiguous `ViewBuilder` closures, or mismatched generic types — resolve them before updating callsites. **If the build fails:** read the error message carefully, fix the identified issue, then rebuild before proceeding to the next step. If a preview crashes, isolate the offending subview, confirm its state initialisation is valid, and re-run the preview before continuing.
|
||||
1. Define the view's state, ownership location, and minimum OS assumptions before writing UI code.
|
||||
2. Identify which dependencies belong in `@Environment` and which should stay as explicit initializer inputs.
|
||||
3. Sketch the view hierarchy, routing model, and presentation points; extract repeated parts into subviews. **Build and verify no compiler errors before proceeding.**
|
||||
4. Implement async loading with `.task` or `.task(id:)`, plus explicit loading and error states when needed.
|
||||
5. Add previews for the primary and secondary states, then add accessibility labels or identifiers when the UI is interactive.
|
||||
6. Validate with a build: confirm no compiler errors, check that previews render without crashing, ensure state changes propagate correctly, and sanity-check that list identity and observation scope will not cause avoidable re-renders. For common SwiftUI compilation errors — missing `@State` annotations, ambiguous `ViewBuilder` closures, or mismatched generic types — resolve them before updating callsites. **If the build fails:** read the error message carefully, fix the identified issue, then rebuild before proceeding to the next step. If a preview crashes, isolate the offending subview, confirm its state initialisation is valid, and re-run the preview before continuing.
|
||||
|
||||
## Component references
|
||||
|
||||
|
|
|
|||
Loading…
Reference in a new issue