Claude Code Plugins

Community-maintained marketplace

Feedback

Swift 6.x language best practices, macros, project setup, and modern patterns. Use when user asks about Swift syntax, Package.swift configuration, project structure, macros like @Observable/@Model/@MainActor, property wrappers, or modern Swift development patterns.

Install Skill

1Download skill
2Enable skills in Claude

Open claude.ai/settings/capabilities and find the "Skills" section

3Upload to Claude

Click "Upload skill" and select the downloaded ZIP file

Note: Please verify skill by going through its instructions before using it.

SKILL.md

name swift-fundamentals
description Swift 6.x language best practices, macros, project setup, and modern patterns. Use when user asks about Swift syntax, Package.swift configuration, project structure, macros like @Observable/@Model/@MainActor, property wrappers, or modern Swift development patterns.
allowed-tools Bash, Read, Write, Edit

Swift Fundamentals

Comprehensive guide to Swift 6.x language features, best practices, macros, and project configuration for iOS 26 and macOS Tahoe development.

Prerequisites

  • Xcode 26+ or Swift 6.x toolchain
  • macOS 15.5+ (Sequoia) for full iOS 26 support

Swift Version Check

swift --version
# Swift version 6.x

Swift 6.x Key Features

Swift 6.0 - Major Changes

  1. Complete Concurrency Checking - Strict data-race safety by default
  2. Typed Throws - Functions can specify error types: throws(MyError)
  3. Noncopyable Types - ~Copyable for unique ownership
  4. Pack Iteration - Iterate over parameter packs
  5. 128-bit Integer Types - Int128 and UInt128
  6. count(where:) - Count elements matching predicate

Swift 6.2 - Recent Improvements

  1. Approachable Concurrency - Easier adoption path
  2. Single-threaded by default - New defaultIsolation setting
  3. Observations Async Sequence - Stream state changes
  4. Pre-built swift-syntax - Faster macro compilation
  5. InlineArray - Fixed-size inline storage

Swift 6.3 - Latest (2025)

  1. Enhanced Type Inference - Better generic inference
  2. Improved Error Messages - Clearer diagnostics
  3. Performance Optimizations - Faster compilation

Core Macros

@Observable (iOS 17+)

Modern replacement for ObservableObject. Automatically tracks property access.

import Observation

@Observable
class UserSettings {
    var username: String = ""
    var isLoggedIn: Bool = false
    var preferences: Preferences = Preferences()

    // Computed properties are also tracked
    var displayName: String {
        isLoggedIn ? username : "Guest"
    }
}

// Usage in SwiftUI
struct SettingsView: View {
    var settings: UserSettings  // No @ObservedObject needed

    var body: some View {
        Text(settings.displayName)
        // View automatically updates when displayName changes
    }
}

Key Differences from ObservableObject:

  • No @Published property wrappers needed
  • No objectWillChange publisher
  • Finer-grained updates (per-property, not whole object)
  • Use @Bindable for two-way bindings
struct EditView: View {
    @Bindable var settings: UserSettings

    var body: some View {
        TextField("Username", text: $settings.username)
    }
}

@Model (SwiftData)

Declares a SwiftData model with automatic persistence.

import SwiftData

@Model
class Note {
    var title: String
    var content: String
    var createdAt: Date
    var tags: [Tag]?  // Optional relationship for CloudKit

    init(title: String, content: String = "") {
        self.title = title
        self.content = content
        self.createdAt = Date()
    }
}

@Model
class Tag {
    var name: String
    var notes: [Note]?  // Inverse relationship

    init(name: String) {
        self.name = name
    }
}

Important for CloudKit Sync:

  • All relationships must be optional
  • No @Attribute(.unique) constraints
  • Default values required for non-optional properties

@MainActor

Ensures code runs on the main thread/actor.

@MainActor
class ViewModel {
    var items: [Item] = []
    var isLoading = false

    func loadItems() async {
        isLoading = true
        defer { isLoading = false }

        // Network call can be off main actor
        let fetched = await fetchItems()

        // Assignment happens on main actor
        items = fetched
    }

    nonisolated func fetchItems() async -> [Item] {
        // This can run on any thread
        try? await URLSession.shared.data(from: url)
        // ...
    }
}

@Generable (Foundation Models - iOS 26)

Enables structured AI output generation.

import FoundationModels

@Generable
struct MovieRecommendation {
    var title: String
    var year: Int
    var genre: String
    var reason: String
}

// Usage
let session = LanguageModelSession()
let recommendation: MovieRecommendation = try await session.respond(
    to: "Recommend a sci-fi movie from the 2020s"
)

@Test (Swift Testing)

Marks a function as a test case.

import Testing

@Test("User can create account with valid email")
func createAccountWithValidEmail() async throws {
    let result = try await authService.createAccount(email: "test@example.com")
    #expect(result.success)
    #expect(result.user?.email == "test@example.com")
}

@Test("Password validation", arguments: [
    ("abc", false),
    ("abc12345", true),
    ("ABC12345!", true)
])
func validatePassword(password: String, expected: Bool) {
    #expect(validator.isValid(password) == expected)
}

Property Wrappers

SwiftUI Property Wrappers

struct ContentView: View {
    // Local state - view owns this value
    @State private var count = 0

    // Two-way binding from parent
    @Binding var selectedTab: Int

    // Environment value from system or parent
    @Environment(\.colorScheme) var colorScheme

    // Custom environment object
    @Environment(AppState.self) var appState

    // SwiftData query
    @Query(sort: \Note.createdAt, order: .reverse)
    var notes: [Note]

    // Focus state for text fields
    @FocusState private var isFocused: Bool

    // Namespace for matched geometry effects
    @Namespace private var animation

    var body: some View {
        // ...
    }
}

@Bindable (iOS 17+)

Creates bindings to @Observable properties.

struct EditorView: View {
    @Bindable var document: Document  // Document is @Observable

    var body: some View {
        TextEditor(text: $document.content)
        Toggle("Published", isOn: $document.isPublished)
    }
}

@AppStorage

Persists values to UserDefaults.

struct SettingsView: View {
    @AppStorage("hasCompletedOnboarding") var hasCompletedOnboarding = false
    @AppStorage("preferredTheme") var preferredTheme = "system"
    @AppStorage("notificationsEnabled") var notificationsEnabled = true

    var body: some View {
        Toggle("Notifications", isOn: $notificationsEnabled)
    }
}

@SceneStorage

Persists view state per scene (for state restoration).

struct DocumentView: View {
    @SceneStorage("selectedDocumentID") var selectedDocumentID: String?
    @SceneStorage("scrollPosition") var scrollPosition: Double = 0

    var body: some View {
        // State restored when scene is recreated
    }
}

Package.swift Configuration

Basic Package

// swift-tools-version: 6.0
import PackageDescription

let package = Package(
    name: "MyApp",
    platforms: [
        .iOS(.v26),
        .macOS(.v26)
    ],
    products: [
        .library(name: "MyApp", targets: ["MyApp"])
    ],
    dependencies: [
        // External dependencies
    ],
    targets: [
        .target(
            name: "MyApp",
            dependencies: [],
            swiftSettings: [
                .enableExperimentalFeature("StrictConcurrency")
            ]
        ),
        .testTarget(
            name: "MyAppTests",
            dependencies: ["MyApp"]
        )
    ]
)

With Dependencies

// swift-tools-version: 6.0
import PackageDescription

let package = Package(
    name: "MyApp",
    platforms: [
        .iOS(.v26),
        .macOS(.v26)
    ],
    products: [
        .library(name: "MyApp", targets: ["MyApp"])
    ],
    dependencies: [
        .package(url: "https://github.com/apple/swift-algorithms", from: "1.2.0"),
        .package(url: "https://github.com/apple/swift-collections", from: "1.1.0"),
        .package(url: "https://github.com/apple/swift-async-algorithms", from: "1.0.0")
    ],
    targets: [
        .target(
            name: "MyApp",
            dependencies: [
                .product(name: "Algorithms", package: "swift-algorithms"),
                .product(name: "Collections", package: "swift-collections"),
                .product(name: "AsyncAlgorithms", package: "swift-async-algorithms")
            ]
        )
    ]
)

Strict Concurrency Settings

swiftSettings: [
    // Full Swift 6 concurrency
    .enableExperimentalFeature("StrictConcurrency"),

    // Or gradual adoption
    .swiftLanguageMode(.v6),

    // Enable upcoming features
    .enableUpcomingFeature("ExistentialAny"),
    .enableUpcomingFeature("InternalImportsByDefault")
]

Project Structure

Recommended Layout

MyApp/
├── Package.swift
├── Sources/
│   └── MyApp/
│       ├── App/
│       │   ├── MyAppApp.swift
│       │   └── AppState.swift
│       ├── Models/
│       │   ├── Note.swift
│       │   ├── Tag.swift
│       │   └── User.swift
│       ├── Views/
│       │   ├── ContentView.swift
│       │   ├── Notes/
│       │   │   ├── NoteListView.swift
│       │   │   ├── NoteDetailView.swift
│       │   │   └── NoteEditorView.swift
│       │   └── Settings/
│       │       └── SettingsView.swift
│       ├── Services/
│       │   ├── NetworkService.swift
│       │   └── StorageService.swift
│       ├── Utilities/
│       │   ├── Extensions/
│       │   └── Helpers/
│       └── Resources/
│           ├── Localizable.xcstrings
│           └── Assets.xcassets
└── Tests/
    └── MyAppTests/
        ├── ModelTests/
        ├── ServiceTests/
        └── ViewTests/

App Entry Point

import SwiftUI
import SwiftData

@main
struct MyAppApp: App {
    var body: some Scene {
        WindowGroup {
            ContentView()
        }
        .modelContainer(for: [Note.self, Tag.self])
    }
}

With Custom Container Configuration

@main
struct MyAppApp: App {
    let container: ModelContainer

    init() {
        let schema = Schema([Note.self, Tag.self])
        let config = ModelConfiguration(
            schema: schema,
            isStoredInMemoryOnly: false,
            cloudKitDatabase: .automatic  // Enable iCloud sync
        )

        do {
            container = try ModelContainer(for: schema, configurations: config)
        } catch {
            fatalError("Failed to configure SwiftData: \(error)")
        }
    }

    var body: some Scene {
        WindowGroup {
            ContentView()
        }
        .modelContainer(container)
    }
}

Modern Swift Patterns

Result Builders

@resultBuilder
struct HTMLBuilder {
    static func buildBlock(_ components: String...) -> String {
        components.joined()
    }

    static func buildOptional(_ component: String?) -> String {
        component ?? ""
    }

    static func buildEither(first component: String) -> String {
        component
    }

    static func buildEither(second component: String) -> String {
        component
    }
}

func html(@HTMLBuilder content: () -> String) -> String {
    "<html>\(content())</html>"
}

let page = html {
    "<head><title>Hello</title></head>"
    "<body>"
    if showHeader {
        "<h1>Welcome</h1>"
    }
    "<p>Content here</p>"
    "</body>"
}

Typed Throws (Swift 6)

enum NetworkError: Error {
    case invalidURL
    case noData
    case decodingFailed
}

func fetchUser(id: Int) throws(NetworkError) -> User {
    guard let url = URL(string: "https://api.example.com/users/\(id)") else {
        throw .invalidURL
    }
    // ...
}

// Caller knows exactly what errors to handle
do {
    let user = try fetchUser(id: 123)
} catch .invalidURL {
    // Handle invalid URL
} catch .noData {
    // Handle no data
} catch .decodingFailed {
    // Handle decoding error
}

Noncopyable Types (Swift 6)

struct UniqueResource: ~Copyable {
    let handle: Int

    init(handle: Int) {
        self.handle = handle
    }

    deinit {
        // Clean up resource
        closeHandle(handle)
    }

    consuming func close() {
        // Explicitly consume the resource
    }
}

func useResource() {
    let resource = UniqueResource(handle: 42)
    // resource cannot be copied
    // processResource(resource)  // This moves resource
    // resource.doSomething()     // Error: resource was moved
}

Parameter Packs (Swift 6)

func all<each T: Equatable>(
    _ values: repeat each T,
    equalTo comparisons: repeat each T
) -> Bool {
    for (value, comparison) in repeat (each values, each comparisons) {
        if value != comparison {
            return false
        }
    }
    return true
}

let result = all(1, "hello", true, equalTo: 1, "hello", true)  // true

Error Handling Best Practices

Define Domain Errors

enum AppError: LocalizedError {
    case networkUnavailable
    case unauthorized
    case notFound(resource: String)
    case validationFailed(field: String, reason: String)
    case unknown(underlying: Error)

    var errorDescription: String? {
        switch self {
        case .networkUnavailable:
            return "Network connection unavailable"
        case .unauthorized:
            return "You are not authorized to perform this action"
        case .notFound(let resource):
            return "\(resource) was not found"
        case .validationFailed(let field, let reason):
            return "\(field): \(reason)"
        case .unknown(let error):
            return error.localizedDescription
        }
    }
}

Result Type Pattern

func fetchData() async -> Result<Data, AppError> {
    guard NetworkMonitor.shared.isConnected else {
        return .failure(.networkUnavailable)
    }

    do {
        let data = try await networkService.fetch()
        return .success(data)
    } catch {
        return .failure(.unknown(underlying: error))
    }
}

// Usage
switch await fetchData() {
case .success(let data):
    process(data)
case .failure(let error):
    showError(error)
}

Extensions Best Practices

Organize by Functionality

// String+Validation.swift
extension String {
    var isValidEmail: Bool {
        let pattern = #"^[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\.[A-Za-z]{2,}$"#
        return range(of: pattern, options: .regularExpression) != nil
    }

    var isValidPassword: Bool {
        count >= 8 &&
        range(of: "[A-Z]", options: .regularExpression) != nil &&
        range(of: "[0-9]", options: .regularExpression) != nil
    }
}

// Date+Formatting.swift
extension Date {
    var relativeDescription: String {
        let formatter = RelativeDateTimeFormatter()
        formatter.unitsStyle = .short
        return formatter.localizedString(for: self, relativeTo: Date())
    }

    var iso8601String: String {
        ISO8601DateFormatter().string(from: self)
    }
}

// View+Modifiers.swift
extension View {
    func cardStyle() -> some View {
        modifier(CardModifier())
    }

    @ViewBuilder
    func `if`<Content: View>(_ condition: Bool, transform: (Self) -> Content) -> some View {
        if condition {
            transform(self)
        } else {
            self
        }
    }
}

Code Quality

SwiftLint Configuration

# .swiftlint.yml
included:
  - Sources
  - Tests

excluded:
  - .build
  - Package.swift

disabled_rules:
  - trailing_whitespace
  - line_length

opt_in_rules:
  - empty_count
  - explicit_init
  - closure_spacing
  - overridden_super_call
  - redundant_nil_coalescing
  - private_outlet
  - nimble_operator
  - attributes
  - operator_usage_whitespace
  - closure_end_indentation
  - first_where
  - object_literal
  - number_separator
  - prohibited_super_call
  - fatal_error_message
  - weak_delegate

line_length:
  warning: 120
  error: 200

type_body_length:
  warning: 300
  error: 500

file_length:
  warning: 500
  error: 1000

identifier_name:
  min_length: 2
  max_length: 50

Swift Format Configuration

// .swift-format
{
  "version": 1,
  "lineLength": 120,
  "indentation": {
    "spaces": 4
  },
  "tabWidth": 4,
  "maximumBlankLines": 1,
  "respectsExistingLineBreaks": true,
  "lineBreakBeforeControlFlowKeywords": false,
  "lineBreakBeforeEachArgument": false
}

Official Resources