swift-lang
Swift 6.2 Language Patterns
Corrective guide for Swift code generation. Swift 6.2 fundamentally changed the concurrency model — most LLM training data reflects the old model.
Critical Constraints
- ❌ DO NOT use
ObservableObject+@Publishedfor macOS 14+ / iOS 17+ → ✅ Use@Observablemacro (import Observation) - ❌ DO NOT use
@StateObject→ ✅ Use@Statewith@Observableclasses - ❌ DO NOT use
@ObservedObject→ ✅ Use@Bindablefor bindings, or just pass the object - ❌ DO NOT use
@EnvironmentObject→ ✅ Use@Environmentwith custom key - ❌ DO NOT assume async functions always hop off the actor → ✅ In Swift 6.2, async functions run on the caller's actor by default
- ❌ DO NOT use
Task.detachedto get background execution → ✅ Use@concurrentattribute on the function - ❌ DO NOT add
@Sendableto everything to silence warnings → ✅ Use default MainActor isolation mode, opt out withnonisolated - ❌ DO NOT use
DispatchQueue.main.asyncfor main thread work → ✅ Use@MainActororMainActor.run {} - ❌ DO NOT use
DispatchQueue.global().asyncfor background work → ✅ Use@concurrentfunctions withawait - ❌ DO NOT create
NavigationView→ ✅ UseNavigationSplitVieworNavigationStack
Decision Tree: Concurrency Model
Starting a new project or file?
├── Is this an app/script/executable target?
│ └── YES → Enable default MainActor isolation (recommended)
│ └── All code is @MainActor by default
│ └── No data-race errors for single-threaded code
│ └── Use nonisolated + @concurrent for background work
├── Is this a library/framework?
│ └── YES → Use explicit annotations (@MainActor, nonisolated, Sendable)
│
Need background execution?
├── Use @concurrent on the function
├── Mark the containing type as nonisolated
├── Add async to the function signature
└── Callers must await the result
Decision Tree: Observable Pattern
Need observable state in a view?
├── macOS 14+ / iOS 17+ target?
│ ├── YES → Use @Observable macro
│ │ ├── View owns it? → @State private var model = MyModel()
│ │ ├── View receives it? → just use the parameter (no wrapper needed)
│ │ ├── Need binding? → @Bindable var model
│ │ └── Environment? → @Environment(MyModel.self) var model
│ └── NO (older targets) → Use ObservableObject + @Published
│ ├── View owns it? → @StateObject
│ ├── View receives it? → @ObservedObject
│ └── Environment? → @EnvironmentObject
Verified Patterns
See references/concurrency.md for comprehensive Swift 6.2 concurrency patterns.
See references/performance.md for InlineArray, Span, and memory optimization.
Pattern: @Observable Class (Modern)
import SwiftUI
import Observation
@Observable
class StickerModel {
var stickers: [Sticker] = []
var isLoading = false
func loadStickers() async {
isLoading = true
stickers = await StickerService.fetch()
isLoading = false
}
}
struct StickerListView: View {
@State private var model = StickerModel() // NOT @StateObject
var body: some View {
List(model.stickers) { sticker in
StickerRow(sticker: sticker)
}
.task { await model.loadStickers() }
}
}
// Passing to child — no wrapper needed
struct StickerRow: View {
var sticker: Sticker // plain property, NOT @ObservedObject
var body: some View {
Text(sticker.name)
}
}
// When child needs to mutate
struct StickerEditor: View {
@Bindable var model: StickerModel // NOT @ObservedObject
var body: some View {
TextField("Name", text: $model.stickers[0].name)
}
}
Pattern: Environment with @Observable
import SwiftUI
// Define environment key
struct AppModelKey: EnvironmentKey {
static let defaultValue = AppModel()
}
extension EnvironmentValues {
var appModel: AppModel {
get { self[AppModelKey.self] }
set { self[AppModelKey.self] = newValue }
}
}
// Inject
@main
struct MyApp: App {
@State private var appModel = AppModel()
var body: some Scene {
WindowGroup {
ContentView()
.environment(\.appModel, appModel)
}
}
}
// Consume
struct ContentView: View {
@Environment(\.appModel) private var appModel
var body: some View { /* use appModel */ }
}
Pattern: Default MainActor Isolation (Swift 6.2)
// With default MainActor isolation enabled in build settings,
// everything is @MainActor by default. No annotations needed.
final class StickerLibrary {
static let shared = StickerLibrary() // Safe — implicitly @MainActor
var stickers: [Sticker] = [] // Safe — on main actor
}
final class StickerModel {
let photoProcessor = PhotoProcessor()
func extractSticker(_ item: PhotosPickerItem) async throws -> Sticker? {
guard let data = try await item.loadTransferable(type: Data.self) else {
return nil
}
// No data-race error in Swift 6.2 — runs on caller's actor
return await photoProcessor.extractSticker(data: data, with: item.itemIdentifier)
}
}
Pattern: @concurrent for Background Work
// To explicitly run work OFF the main actor, use @concurrent
nonisolated struct PhotoProcessor {
@concurrent
func process(data: Data) async -> ProcessedPhoto? {
// This runs on the concurrent thread pool, NOT the main actor
// Heavy image processing here
return ProcessedPhoto(data: data)
}
}
// Caller — must await
let processor = PhotoProcessor()
let result = await processor.process(data: imageData)
Pattern: Isolated Conformances
protocol Exportable {
func export()
}
// Mark the conformance as @MainActor — safe because compiler
// ensures it's only used on the main actor
extension StickerModel: @MainActor Exportable {
func export() {
photoProcessor.exportAsPNG() // Can access MainActor state
}
}
// This works — ImageExporter is also on MainActor
@MainActor
struct ImageExporter {
var items: [any Exportable]
mutating func add(_ item: StickerModel) {
items.append(item) // OK — same actor isolation
}
}
Common Mistakes & Fixes
| Mistake | Fix |
|---|---|
class MyModel: ObservableObject with @Published |
@Observable class MyModel — remove @Published, remove protocol |
@StateObject var model = MyModel() |
@State private var model = MyModel() |
@ObservedObject var model: MyModel |
var model: MyModel (or @Bindable for bindings) |
@EnvironmentObject var model: MyModel |
@Environment(MyModel.self) var model or custom key |
Task.detached { await heavy() } |
Mark heavy() as @concurrent nonisolated |
DispatchQueue.global().async { } |
@concurrent func doWork() async { } |
static let shared causes Sendable warning |
Enable default MainActor isolation, or add @MainActor |
Adding @Sendable everywhere |
Use actor isolation instead — @MainActor or explicit actors |
await MainActor.run { } inside already-MainActor code |
Just call directly — already on MainActor |
Build Settings for Swift 6.2
Enable approachable concurrency in Xcode Build Settings → Swift Compiler - Concurrency:
- Default Actor Isolation:
MainActor(infer @MainActor by default) - Strict Concurrency Checking:
complete
Or in Package.swift:
.target(
name: "MyTarget",
swiftSettings: [
.defaultIsolation(MainActor.self)
]
)
References
More from makgunay/claude-swift-skills
global-hotkeys
System-wide keyboard shortcut registration on macOS using NSEvent monitoring (simple, app-level) and Carbon EventHotKey API (reliable, system-wide). Covers NSEvent.addGlobalMonitorForEvents and addLocalMonitorForEvents, CGEvent tap for keystroke simulation, Carbon RegisterEventHotKey for system-wide hotkeys, modifier flag handling (.deviceIndependentFlagsMask), common key code mappings, debouncing, Accessibility permission requirements (AXIsProcessTrusted), and SwiftUI .onKeyPress for in-app shortcuts. Use when implementing global keyboard shortcuts, hotkey-triggered panels, or system-wide key event monitoring.
11swiftui-webkit
Native SwiftUI WebKit integration with the new WebView struct and WebPage observable class. Covers WebView creation from URLs, WebPage for navigation control and state management, JavaScript execution (callJavaScript with arguments and content worlds), custom URL scheme handlers, navigation management (load, reload, back/forward), navigation decisions, text search (findNavigator), content capture (snapshots, PDF generation, web archives), and configuration (data stores, user agents, JS permissions). Use when embedding web content in SwiftUI apps instead of the old WKWebView + UIViewRepresentable/NSViewRepresentable bridge pattern. This is a brand new API — do NOT use the old WKWebView wrapping approach.
10liquid-glass
Comprehensive guide to Apple's Liquid Glass design system introduced in macOS 26, iOS 26, and across all Apple platforms. Covers SwiftUI (.glassEffect(), GlassEffectContainer, .interactive(), .tint(), glassEffectID morphing, .buttonStyle(.glass), .buttonStyle(.glassProminent), glassEffectUnion), AppKit (NSGlassEffectView, NSGlassEffectContainerView), UIKit (UIGlassEffect, UIGlassContainerEffect, UIScrollEdgeEffect), and WidgetKit (accented rendering mode, widgetAccentable). Use whenever building UI with the new Apple design language, adopting glass effects, styling buttons or toolbars, or creating modern macOS/iOS interfaces. Always consult this skill when asked about new Apple design.
10tech-stack-validator
Validates and recommends technology stacks for native macOS/iOS app projects against PRD and architecture requirements. Reads the PRD and architecture documents (or gathers requirements interactively), then systematically checks every technology choice for: OS version availability, framework capability gaps, performance feasibility, distribution compatibility (sandbox vs direct), API deprecation risks, dependency conflicts, and timeline realism for the team size. Produces a structured validation report with go/no-go verdicts, risk flags, and alternative recommendations. Use when user says things like 'validate my tech stack', 'check if this architecture works', 'what should I build this with', 'is SwiftData the right choice', 'can I ship this on the App Store', 'review my architecture', or before starting implementation of any PRD. Also use when migrating between tech stacks or evaluating whether to adopt a new framework.
9cross-platform
Patterns for sharing code between macOS and iOS in SwiftUI apps. Covers project structure (70% shared / 15% macOS / 15% iOS), platform abstraction via protocols and #if os() conditional compilation, adaptive navigation (NavigationSplitView on Mac/iPad → NavigationStack on iPhone), shared components with platform styling, iOS-specific extensions (custom keyboard extension, interactive widgets, share extension, action extension, Control Center widget, lock screen widget), App Groups for data sharing with extensions, CloudKit sync monitoring, JSON export/import, schema versioning and migration, URL scheme deep linking, and the full macOS→iOS migration checklist. Use when building apps that target both macOS and iOS, when adding iOS support to a macOS app, when building widgets or keyboard extensions, or when setting up iCloud sync with SwiftData.
8swiftdata
SwiftData persistence framework patterns for macOS and iOS apps. Covers @Model definitions, @Query, relationships (with delete rules), ModelContainer/ModelContext configuration, class inheritance with @Model subclasses, type-based predicates (#Predicate with is/as? casting), polymorphic relationships, @Attribute options (.preserveValueOnDeletion), schema migrations, and import/export. Use when implementing data persistence, defining models, querying data, or working with SwiftData. Prevents common LLM errors like generating Core Data syntax, inventing nonexistent SwiftData APIs, or misusing @Query.
8