Add scan flow MVP and local Axiom skill workspace
This snapshot establishes the camera-to-result recognition flow and related tests while checking in the project skill/docs assets required for the configured local tooling.
This commit is contained in:
7
.claude/skills/axiom-swift-modern/.openskills.json
Normal file
7
.claude/skills/axiom-swift-modern/.openskills.json
Normal file
@@ -0,0 +1,7 @@
|
||||
{
|
||||
"source": "CharlesWiltgen/Axiom",
|
||||
"sourceType": "git",
|
||||
"repoUrl": "https://github.com/CharlesWiltgen/Axiom",
|
||||
"subpath": "axiom-codex/skills/axiom-swift-modern",
|
||||
"installedAt": "2026-04-12T08:06:44.096Z"
|
||||
}
|
||||
89
.claude/skills/axiom-swift-modern/SKILL.md
Normal file
89
.claude/skills/axiom-swift-modern/SKILL.md
Normal file
@@ -0,0 +1,89 @@
|
||||
---
|
||||
name: axiom-swift-modern
|
||||
description: Use when reviewing or generating Swift code for modern idiom correctness — catches outdated APIs, pre-Swift 5.5 patterns, and Foundation legacy usage that Claude defaults to
|
||||
license: MIT
|
||||
---
|
||||
|
||||
# Modern Swift Idioms
|
||||
|
||||
## Purpose
|
||||
|
||||
Claude frequently generates outdated Swift patterns from its training data. This skill corrects the most common ones — patterns that compile fine but use legacy APIs when modern equivalents are clearer, more efficient, or more correct.
|
||||
|
||||
**Philosophy**: "Don't repeat what LLMs already know — focus on edge cases, surprises, soft deprecations." (Paul Hudson)
|
||||
|
||||
## Modern API Replacements
|
||||
|
||||
| Old Pattern | Modern Swift | Since | Why |
|
||||
|-------------|-------------|-------|-----|
|
||||
| `Date()` | `Date.now` | 5.6 | Clearer intent |
|
||||
| `filter { }.count` | `count(where:)` | 5.0 | Single pass, no intermediate allocation |
|
||||
| `replacingOccurrences(of:with:)` | `replacing(_:with:)` | 5.7 | Swift native, no Foundation bridge |
|
||||
| `CGFloat` | `Double` | 5.5 | Implicit bridging; exceptions: optionals, inout, ObjC-bridged APIs |
|
||||
| `Task.sleep(nanoseconds:)` | `Task.sleep(for: .seconds(1))` | 5.7 | Type-safe Duration API |
|
||||
| `DateFormatter()` | `.formatted()` / `FormatStyle` | 5.5 | No instance management, localizable by default |
|
||||
| `String(format: "%.2f", val)` | `val.formatted(.number.precision(.fractionLength(2)))` | 5.5 | Type-safe, localized |
|
||||
| `localizedCaseInsensitiveContains()` | `localizedStandardContains()` | 5.0 | Handles diacritics, ligatures, width variants |
|
||||
| `"\(firstName) \(lastName)"` | `PersonNameComponents` with `.formatted()` | 5.5 | Respects locale name ordering |
|
||||
| `"yyyy-MM-dd"` with DateFormatter | `try Date(string, strategy: .iso8601)` | 5.6 | Modern parsing (throws); use "y" not "yyyy" for display |
|
||||
| `contains()` on user input | `localizedStandardContains()` | 5.0 | Required for correct text search/filtering |
|
||||
|
||||
## Modern Syntax
|
||||
|
||||
| Old Pattern | Modern Swift | Since |
|
||||
|-------------|-------------|-------|
|
||||
| `if let value = value {` | `if let value {` | 5.7 |
|
||||
| Explicit `return` in single-expression | Omit `return`; `if`/`switch` are expressions | 5.9 |
|
||||
| `Circle()` in modifiers | `.circle` (static member lookup) | 5.5 |
|
||||
| `import UIKit` alongside `import SwiftUI` | Often not needed — SwiftUI re-exports most UIKit/AppKit types. Retain for UIKit-only APIs (`UIApplication`, etc.) | 5.5 |
|
||||
|
||||
## Foundation Modernization
|
||||
|
||||
| Old Pattern | Modern Foundation | Since |
|
||||
|-------------|------------------|-------|
|
||||
| `FileManager.default.urls(for: .documentDirectory, ...)` | `URL.documentsDirectory` | 5.7 |
|
||||
| `url.appendingPathComponent("file")` | `url.appending(path: "file")` | 5.7 |
|
||||
| `books.sorted { $0.author < $1.author }` (repeated) | Conform to `Comparable`, call `.sorted()` | — |
|
||||
| `"yyyy"` in date format for display | `"y"` — correct in all calendar systems | — |
|
||||
|
||||
## SwiftUI Convenience APIs Claude Misses
|
||||
|
||||
- **`ContentUnavailableView.search(text: searchText)`** (iOS 17+) automatically includes the search term — no need to compose a custom string
|
||||
- **`LabeledContent` in Forms** (iOS 16+) provides consistent label alignment without manual HStack layout
|
||||
- **`confirmationDialog()` must attach to triggering UI** — Liquid Glass morphing animations depend on the source element
|
||||
|
||||
## Swift 6.3 Concurrency Posture
|
||||
|
||||
Write Swift 6.3-first code, not Swift 5-era code. These defaults apply to ALL new Swift code, not just when concurrency errors appear.
|
||||
|
||||
| Default | Rationale |
|
||||
|---------|-----------|
|
||||
| Assume strict concurrency and MainActor default isolation for app/UI modules | Swift 6.3 language mode; Xcode 26+ default for new projects |
|
||||
| Prefer async/await over GCD, DispatchGroup, and callback pyramids | GCD is a bridge pattern for legacy APIs, not default architecture |
|
||||
| Async does not mean background — use `@concurrent` (Swift 6.2+) to force off-main | Async functions resume on the same actor they were called from |
|
||||
| Prefer structured concurrency (`async let`, `TaskGroup`) over unstructured `Task {}` | Structured tasks propagate cancellation and errors automatically |
|
||||
| Do not use `Task.detached` unless there is a specific, stated reason | Loses actor context, priority, and task-local values |
|
||||
| Prefer Sendable structs/enums for data that crosses actor boundaries | Value types are inherently safe to share |
|
||||
| Use actors only for truly shared mutable state across concurrency domains | Don't make every class an actor — UI code stays @MainActor |
|
||||
| Treat `@unchecked Sendable`, `@preconcurrency`, `nonisolated(unsafe)` as temporary bridge tools | Each should have a removal ticket, not be permanent |
|
||||
| Do not add escape hatches just to silence compiler errors | They hide data races that crash in production |
|
||||
|
||||
For detailed patterns, decision trees, and error-specific guidance, see `axiom-swift-concurrency`.
|
||||
|
||||
## Common Claude Hallucinations
|
||||
|
||||
These patterns appear frequently in Claude-generated code:
|
||||
|
||||
1. **Creates `DateFormatter` instances inline** — Use `.formatted()` or `FormatStyle` instead. If a formatter must exist, make it `static let`.
|
||||
2. **Uses `DispatchQueue.main.async`** — Use `@MainActor` or `MainActor.run`. GCD is a bridge pattern, not a default.
|
||||
3. **Uses `DispatchQueue.global().async` for background work** — Use `@concurrent` (Swift 6.2+) or extract to an actor.
|
||||
4. **Uses `Task.detached` to "make it background"** — Use `@concurrent`. `Task.detached` loses actor context.
|
||||
5. **Uses `CGFloat` for SwiftUI parameters** — `Double` works everywhere since Swift 5.5 implicit bridging.
|
||||
6. **Generates `guard let x = x else`** — Use `guard let x else` shorthand.
|
||||
7. **Returns explicitly in single-expression computed properties** — Omit `return`.
|
||||
8. **Spawns unstructured `Task {}` in loops** — Use `TaskGroup` for dynamic parallel work.
|
||||
9. **Adds `@unchecked Sendable` to silence warnings** — Convert to actor or proper Sendable type.
|
||||
|
||||
## Resources
|
||||
|
||||
**Skills**: axiom-swift-performance, axiom-swift-concurrency, axiom-swiftui-architecture
|
||||
3
.claude/skills/axiom-swift-modern/agents/openai.yaml
Normal file
3
.claude/skills/axiom-swift-modern/agents/openai.yaml
Normal file
@@ -0,0 +1,3 @@
|
||||
interface:
|
||||
display_name: "Swift Modern"
|
||||
short_description: "Reviewing or generating Swift code for modern idiom correctness"
|
||||
Reference in New Issue
Block a user