Skip to content

swift-optimization

Type Skill
Plugin awl-ios · v0.0.6
Invoke /awl-ios:swift-optimization
Tools Read, Write, Edit, Bash, Glob, Grep
Source plugins/awl-ios/skills/swift-optimization/SKILL.md

Optimize Swift/SwiftUI code with modern patterns. Triggers: “optimize swift”, “refactor swiftui”, “fix deprecated”, “modernize swift”, “swift best practices”, “review swift code”, “migrate to Swift 6”, “fix concurrency warnings”. Replaces deprecated APIs, improves accessibility, fixes Swift 6 strict-concurrency errors under main-actor default isolation, and targets the AWL template baseline (Swift 6.0, iOS 26.4). NOT for writing tests (use awl-testing testing-swift), not for build or simulator commands (use awl-ios-ref), not for KMP shared Kotlin code (use awl-android kotlin-expert).

Trigger phrases: optimize swift · refactor swiftui · fix deprecated · modernize swift · swift best practices · review swift code · migrate to Swift 6 · fix concurrency warnings

Modernize Swift/SwiftUI code by replacing deprecated APIs, improving accessibility, and applying performance best practices.

“Modernize” means the AWL iOS template baseline from Project.swift, not an older floor:

Setting Value
SWIFT_VERSION 6.0 (strict concurrency, data races are errors)
SWIFT_DEFAULT_ACTOR_ISOLATION MainActor (everything is main-actor isolated unless nonisolated)
SWIFT_APPROACHABLE_CONCURRENCY YES (nonisolated async runs on the caller; @concurrent moves work off the main actor)
minIosVersion 26.4 for the app template, 18.0 for the KMP iOS shell

Consequences: @Observable, NavigationStack, Tab, containerRelativeFrame, and two-parameter onChange are always available, so drop #available(iOS 17, *) and #available(iOS 18, *) gates; only Liquid Glass (iOS 26) needs a gate, and only in the KMP shell. Check Project.swift before applying this to a project that was not built from the template.

Terminal window
# Find Swift files to optimize
find . -name "*.swift" -type f | head -20

Search for these common issues:

  • foregroundColor( - replace with foregroundStyle(
  • cornerRadius( - replace with clipShape(.rect(cornerRadius:
  • NavigationView - replace with NavigationStack
  • ObservableObject - consider @Observable macro
  • onTapGesture on interactive elements - replace with Button
  • Task.sleep(nanoseconds: - replace with Task.sleep(for:
  • DispatchQueue.main.async and await MainActor.run - remove the hop; code is already main-actor isolated under default isolation
  • DispatchQueue.global, Task.detached for CPU work - replace with a @concurrent nonisolated func
  • @MainActor on views, view models, or @Observable classes in template targets - remove, it is redundant under SWIFT_DEFAULT_ACTOR_ISOLATION = MainActor
  • #available(iOS 17, *) / #available(iOS 18, *) - remove, the deployment target already covers them

For each file, apply modernizations in order:

  1. Import changes (if needed)
  2. Type-level changes (@Observable)
  3. Modifier replacements
  4. Accessibility improvements
Deprecated Modern
foregroundColor(.blue) foregroundStyle(.blue)
cornerRadius(10) clipShape(.rect(cornerRadius: 10))
NavigationView { } NavigationStack { }
@StateObject var vm @State var vm (with @Observable)
.onChange(of: x) { val in } .onChange(of: x) { old, new in }
// Before
NavigationView {
NavigationLink(destination: DetailView(item: item)) {
Text(item.name)
}
}
// After
NavigationStack {
List(items) { item in
NavigationLink(value: item) { Text(item.name) }
}
.navigationDestination(for: Item.self) { DetailView(item: $0) }
}
// Before
class ViewModel: ObservableObject {
@Published var items: [Item] = []
}
struct ContentView: View {
@StateObject var viewModel = ViewModel()
}
// After
@Observable class ViewModel {
var items: [Item] = []
}
struct ContentView: View {
@State var viewModel = ViewModel()
}
// Before (bad for VoiceOver)
Image(systemName: "plus").onTapGesture { addItem() }
// After
Button("Add Item", systemImage: "plus", action: addItem)
Issue Solution
@Observable not available Only on targets below iOS 17; every AWL template target is above that, so check Project.swift before keeping ObservableObject.
NavigationStack crashes Ensure navigation values conform to Hashable.
onChange signature error Use two-param { old, new in } or zero-param { } version.
“Main actor-isolated … cannot be called from a nonisolated context” The caller is nonisolated (protocol witness, @concurrent function, or a package without default isolation). Either make the caller main-actor isolated or pass Sendable values across and hop with await.
“Sending value of non-Sendable type … risks causing data races” Mark the type Sendable (struct with let properties, or final class with immutable state), or keep the value inside one isolation region as the KMP template’s GreetingViewModel does.
Heavy work blocks the UI Move it to a @concurrent nonisolated func; a plain nonisolated async function still runs on the main actor under approachable concurrency.
  • Deprecated modifiers replaced
  • Navigation modernized
  • Observable pattern updated
  • Accessibility improved (Button over onTapGesture)
  • Concurrency uses structured patterns; no DispatchQueue, no redundant @MainActor, heavy work in @concurrent
  • Builds without Swift 6 data-race errors
  • No #available gates below the deployment target
  • No fixed frame sizes (use flexible layouts)
  • One type per file
  • Detailed patterns: references/swift-patterns.md
  • Based on: hackingwithswift.com/articles/281/what-to-fix-in-ai-generated-swift-code