slash-do 2.9.0 → 2.10.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/commands/do/better-swift.md +27 -3
- package/package.json +1 -1
|
@@ -397,18 +397,42 @@ Skip step 4 if steps 1-3 reveal the code is correct.
|
|
|
397
397
|
- Inconsistent use of `@Observable` (iOS 17+) vs `ObservableObject` — pick one per minimum deployment target
|
|
398
398
|
- Missing Transferable conformance for drag & drop on shareable data types
|
|
399
399
|
- `@AppStorage` with string keys that risk collision — use namespaced enum
|
|
400
|
-
- View preview providers not covering:
|
|
400
|
+
- View preview providers not covering the Dynamic Type test matrix: `.large` (baseline), `.xxxLarge` (largest non-accessibility), `.accessibility5` (AX5). Also Dark Mode, RTL layout, smallest/largest device for each platform
|
|
401
401
|
|
|
402
402
|
**Accessibility (ALL projects):**
|
|
403
403
|
- Images without `.accessibilityLabel()` or `.accessibilityHidden(true)` for decorative images
|
|
404
404
|
- Custom interactive views missing `.accessibilityAddTraits(.isButton)`
|
|
405
|
-
- Dynamic Type not supported — hardcoded font sizes instead of `.font(.body)` or `@ScaledMetric`
|
|
406
405
|
- Color-only indicators without shape/text alternatives
|
|
407
406
|
- Tap targets smaller than 44x44pt without `.contentShape()` expansion
|
|
408
407
|
- Missing `.accessibilityElement(children: .combine)` grouping
|
|
409
408
|
- VoiceOver reading order not matching visual order
|
|
410
409
|
- Animations not respecting `@Environment(\.accessibilityReduceMotion)`
|
|
411
410
|
|
|
411
|
+
**Dynamic Type responsive-layout audit (iOS, iPadOS, watchOS, visionOS — HIGH priority):**
|
|
412
|
+
Users can set text size from Settings > Display & Brightness > Text Size AND Settings > Accessibility > Display & Text Size > Larger Text, scaling text up through `AX1`–`AX5`. Most layout bugs only surface at the accessibility tiers (AX1–AX5), not at `xxxLarge`. App Store reviewers routinely test at the largest size — clipped or unreachable UI is a rejection vector.
|
|
413
|
+
|
|
414
|
+
**Test matrix — verify every user-facing view renders correctly at these three points:**
|
|
415
|
+
- `.large` — baseline (default system size)
|
|
416
|
+
- `.xxxLarge` — largest non-accessibility tier, catches most truncation
|
|
417
|
+
- `.accessibility5` (AX5) — catches clipping, overflow, broken layouts, and unreachable controls
|
|
418
|
+
|
|
419
|
+
**Patterns to flag as findings:**
|
|
420
|
+
- Hardcoded font sizes (`.font(.system(size: 14))`, `Font.custom(_, size:)` without `relativeTo:`) that won't scale. Fix: use semantic styles (`.font(.body)`, `.headline`, etc.) or `.font(.custom("Name", size: 14, relativeTo: .body))`, or gate numeric spacing with `@ScaledMetric`
|
|
421
|
+
- Hardcoded spacing / frame sizes (padding, width, height, corner radius on text-bearing containers) that don't grow with text. Fix: `@ScaledMetric(relativeTo: .body) var padding: CGFloat = 16`
|
|
422
|
+
- Multi-line `Text` that truncates or clips in constrained layouts (especially inside `HStack`s or narrow/fixed-width containers) — SwiftUI sometimes prefers horizontal truncation (ellipsis) over wrapping, so long strings can clip instead of expanding vertically. Verify the text actually wraps and expands vertically at AX5. If it truncates instead of wrapping, apply `.fixedSize(horizontal: false, vertical: true)` as a targeted fix — do NOT apply it as a blanket rule to every multi-line `Text`, since it can fight parent layout constraints when wrapping already works correctly.
|
|
423
|
+
- Full-screen content views (screens, sheets, detail views) NOT wrapped in a `ScrollView` — at AX5, almost any content taller than ~4 rows overflows. Flag any top-level view body that uses `VStack` / `Form`-less layouts without a scroll container. If the view needs a `Spacer` to push content, wrap in `ScrollView` + `GeometryReader` with a `.frame(minHeight: geo.size.height)` inner container instead of dropping the scroll
|
|
424
|
+
- Fixed `.frame(height:)` or `.frame(width:height:)` on containers that hold `Text` — flag unless paired with `@ScaledMetric` or `.dynamicTypeSize(...DynamicTypeSize.xxxLarge)` cap
|
|
425
|
+
- `HStack` layouts with multiple text elements and no wrap fallback — at AX sizes these truncate off-screen. Suggest `ViewThatFits { HStack { ... }; VStack { ... } }` or split to `VStack` when `dynamicTypeSize.isAccessibilitySize` is true
|
|
426
|
+
- `Label`, `Button`, list rows, and tab/toolbar items with adjacent icons + text using fixed `HStack` spacing — verify icons also scale (`Image(systemName:).imageScale(.large)` or `@ScaledMetric` for sized assets)
|
|
427
|
+
- Views that call `.lineLimit(1)` or `.truncationMode(.tail)` on content users must read in full (titles, button labels, form values) — at AX5 the ellipsis hides critical UI. Allow only for non-critical captions or metadata
|
|
428
|
+
- Views that use `.minimumScaleFactor(...)` below `0.8` as a "fix" for Dynamic Type — this shrinks text back below the user's chosen size and defeats the accessibility request. Prefer wrapping/scrolling
|
|
429
|
+
- **Hero typography / fixed-size displays that legitimately can't grow (slider numbers, countdown digits, watch face values, tight chrome)**: use `.dynamicTypeSize(...DynamicTypeSize.xxxLarge)` as an upper cap on that subtree — **cap, don't ignore**. Flag any such element that uses `.dynamicTypeSize(.large)` or a narrower cap, or that uses hardcoded fonts without any cap (silent regression when user bumps text size)
|
|
430
|
+
- TabView items, NavigationStack titles, and alert buttons that truncate at AX sizes — test with `.dynamicTypeSize(.accessibility5)` in previews
|
|
431
|
+
- Custom `Text` measurements with `GeometryReader` or `TextRenderer` that assume a fixed size category
|
|
432
|
+
- Forms and list rows where trailing controls (Toggle, disclosure indicator, value text) collide with leading labels at AX sizes — use `LabeledContent` (iOS 16+) or switch to vertical layout via `if dynamicTypeSize.isAccessibilitySize`
|
|
433
|
+
- Missing `@Environment(\.dynamicTypeSize) var dynamicTypeSize` branch in custom layouts that need to reflow (e.g., side-by-side → stacked) at accessibility sizes
|
|
434
|
+
- Launch screens / onboarding / paywall screens specifically — these are the most common rejection points because they're full-bleed and often pixel-designed
|
|
435
|
+
|
|
412
436
|
**Dark Mode & theming:**
|
|
413
437
|
- Hardcoded colors (`.white`, `.black`, `Color(red:green:blue:)`) instead of semantic colors (`.primary`, `.secondary`, asset catalog colors with dark variant)
|
|
414
438
|
- Assets without dark mode variants in asset catalog
|
|
@@ -429,7 +453,7 @@ Skip step 4 if steps 1-3 reveal the code is correct.
|
|
|
429
453
|
- Missing view model state transition tests (initial → action → expected state)
|
|
430
454
|
- Missing `@Published` / `@Observable` property change sequence tests
|
|
431
455
|
- Missing `XCUITest` for critical navigation flows and platform-specific interactions
|
|
432
|
-
- Missing preview coverage: all views should have `#Preview` for each platform × Dark Mode × Dynamic Type
|
|
456
|
+
- Missing preview coverage: all views should have `#Preview` for each platform × Dark Mode × the Dynamic Type test matrix (`.large`, `.xxxLarge`, `.accessibility5`). Previews with only the default size ship blind to accessibility layout bugs
|
|
433
457
|
- Missing error path tests for network failures, decode failures, and permission denials
|
|
434
458
|
- **Missing `testModelContainerSchemaIsValid()` test** when `@Model` classes are present — every project using SwiftData should construct an in-memory `ModelContainer` with ALL model types in a unit test. This catches missing inverse relationships before they reach production (the actual error message gives no hint which relationship is broken). Required pattern:
|
|
435
459
|
```swift
|
package/package.json
CHANGED