axiom-accessibility
Use when fixing or auditing ANY accessibility issue — VoiceOver, Dynamic Type, color contrast, touch targets, WCAG compliance, App Store accessibility review.
Use when the user wants to modernize iOS code to iOS 17/18 patterns, migrate from ObservableObject to @Observable, update @StateObject to @State, or adopt modern SwiftUI APIs.
$ npx -y skills add charleswiltgen/axiom --skill axiom-modernize --agent claude-codeHow it fires
How this skill gets triggered: by you, by Claude, or both.
/axiom-modernizeContext preview
The summary Claude sees to decide when to auto-load this skill.
Use when the user wants to modernize iOS code to iOS 17/18 patterns, migrate from ObservableObject to @Observable, update @StateObject to @State, or adopt modern SwiftUI APIs.
name: axiom-modernize description: Use when the user wants to modernize iOS code to iOS 17/18 patterns, migrate from ObservableObject to @Observable, update @StateObject to @State, or adopt modern SwiftUI APIs. license: MIT
You are an expert at migrating iOS apps to modern iOS 17/18+ patterns.
Scan the codebase for legacy patterns and provide migration paths:
Run every Glob, Grep, and Read this prompt lists. Do not reason from training data instead of scanning.
**Swift files**: `**/*.swift` Skip: `*Tests.swift`, `*Previews.swift`, `*/Pods/*`, `*/Carthage/*`, `*/.build/*`, `*/DerivedData/*`, `*/scratch/*`, `*/docs/*`, `*/.claude/*`, `*/.claude-plugin/*`
**Why migrate**: Better performance (view updates only when accessed properties change), simpler syntax, no `@Published` needed
**Requirement**: iOS 17+
**Detection**:
Grep: class.*ObservableObject Grep: : ObservableObject Grep: @Published
// ❌ LEGACY (iOS 14-16)
class ContentViewModel: ObservableObject {
@Published var items: [Item] = []
@Published var isLoading = false
@Published var errorMessage: String?
}
// ✅ MODERN (iOS 17+)
@Observable
class ContentViewModel {
var items: [Item] = []
var isLoading = false
var errorMessage: String?
// Use @ObservationIgnored for non-observed properties
@ObservationIgnored
var internalCache: [String: Any] = [:]
}**Migration steps**: 1. Replace `: ObservableObject` with `@Observable` macro 2. Remove all `@Published` property wrappers 3. Add `@ObservationIgnored` to properties that shouldn't trigger updates 4. Update consuming views (see patterns below)
**Do NOT apply this pattern to `GroupSession` (SharePlay/GroupActivities)**
`GroupSession` is a framework-owned `final class` conforming to `ObservableObject`. You cannot redeclare it, and code observing it must keep using Combine — the SDK ships **no** AsyncSequence for `state`, `activity`, or `activeParticipants` (`sessions()` is the only one).
More importantly, `@Published` publishes from `willSet`, so inside a sink the property still holds the **old** value. The standard late-joiner catch-up depends on exactly that timing:
groupSession.$activeParticipants
.sink { activeParticipants in
// groupSession.activeParticipants is still the OLD set here
let newParticipants = activeParticipants.subtracting(groupSession.activeParticipants)
// send current state to joiners only
}Rewriting this against `@Observable` or an AsyncSequence makes `subtracting` return an empty set. There is no crash and no warning — late joiners silently never receive state, and the bug only appears with 3+ participants on a device that joined late. Leave Combine observation of `GroupSession` alone and say why.
**Why migrate**: Simpler, consistent with value types, works with @Observable
**Requirement**: iOS 17+ with @Observable model
**Detection**:
Grep: @StateObject
// ❌ LEGACY
struct ContentView: View {
@StateObject private var viewModel = ContentViewModel()
var body: some View { ... }
}
// ✅ MODERN (with @Observable model)
struct ContentView: View {
@State private var viewModel = ContentViewModel()
var body: some View { ... }
}**Note**: Only migrate after the model uses `@Observable`. If model still uses `ObservableObject`, keep `@StateObject`.
**Why migrate**: Simpler code, explicit binding when needed
**Requirement**: iOS 17+ with @Observable model
**Detection**:
Grep: @ObservedObject
// ❌ LEGACY
struct ItemView: View {
@ObservedObject var item: ItemModel
var body: some View {
Text(item.name)
}
}
// ✅ MODERN - Direct property (read-only access)
struct ItemView: View {
var item: ItemModel // No wrapper needed!
var body: some View {
Text(item.name)
}
}
// ✅ MODERN - @Bindable (for two-way binding)
struct ItemEditorView: View {
@Bindable var item: ItemModel
var body: some View {
TextField("Name", text: $item.name) // Binding works
}
}**Decision tree**:
**Why migrate**: Type-safe, works with @Observable
**Requirement**: iOS 17+ with @Observable model
**Detection**:
Grep: @EnvironmentObject Grep: \.environmentObject\(
// ❌ LEGACY - Setting
ContentView()
.environmentObject(settings)
// ❌ LEGACY - Reading
struct SettingsView: View {
@EnvironmentObject var settings: AppSettings
var body: some View { ... }
}
// ✅ MODERN - Setting
ContentView()
.environment(settings)
// ✅ MODERN - Reading
struct SettingsView: View {
@Environment(AppSettings.self) var settings
var body: some View { ... }
}
// ✅ MODERN - With binding
struct SettingsEditorView: View {
@Environment(AppSettings.self) var settings
var body: some View {
@Bindable var settings = settings
Toggle("Dark Mode", isOn: $settings.darkMode)
}
}**Why migrate**: Deprecated mo
Battle-tested skills, agents, and tools for modern Apple OS development — Swift 6, SwiftUI, Liquid Glass, Apple Intelligence, and more. Supports Claude Code, Codex, and all other popular coding harnesses and AI-savvy IDEs.
Repo: charleswiltgen/axiom
Use when fixing or auditing ANY accessibility issue — VoiceOver, Dynamic Type, color contrast, touch targets, WCAG compliance, App Store accessibility review.
Use when implementing, testing, or evaluating ANY Apple Intelligence, on-device AI, or speech-to-text feature. Covers Foundation Models, @Generable,…
Use when the user has a crash log (.ips, MetricKit JSON, legacy .crash text, .xccrashpoint bundle, or pasted text) that needs analysis.
Use when the user mentions Swift performance audit, code optimization, or performance review.
Use when the user mentions SwiftUI performance, janky scrolling, slow animations, or view update issues.
Use when the user mentions flaky tests, tests that pass locally but fail in CI, race conditions in tests, or needs to diagnose WHY a specific test fails.