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.
/plugin marketplace add bluewaves-creations/bluewaves-skills/plugin install swift-apple-dev@bluewaves-skillsThis skill is limited to using the following tools:
Comprehensive guide to Swift 6.x language features, best practices, macros, and project configuration for iOS 26 and macOS Tahoe development.
swift --version
# Swift version 6.x
throws(MyError)~Copyable for unique ownershipInt128 and UInt128defaultIsolation settingModern 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:
@Published property wrappers neededobjectWillChange publisher@Bindable for two-way bindingsstruct EditView: View {
@Bindable var settings: UserSettings
var body: some View {
TextField("Username", text: $settings.username)
}
}
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:
@Attribute(.unique) constraintsEnsures 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)
// ...
}
}
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"
)
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)
}
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 {
// ...
}
}
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)
}
}
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)
}
}
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
}
}
// 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"]
)
]
)
// 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")
]
)
]
)
swiftSettings: [
// Full Swift 6 concurrency
.enableExperimentalFeature("StrictConcurrency"),
// Or gradual adoption
.swiftLanguageMode(.v6),
// Enable upcoming features
.enableUpcomingFeature("ExistentialAny"),
.enableUpcomingFeature("InternalImportsByDefault")
]
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/
import SwiftUI
import SwiftData
@main
struct MyAppApp: App {
var body: some Scene {
WindowGroup {
ContentView()
}
.modelContainer(for: [Note.self, Tag.self])
}
}
@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)
}
}
@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>"
}
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
}
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
}
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
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
}
}
}
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)
}
// 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
}
}
}
# .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
{
"version": 1,
"lineLength": 120,
"indentation": {
"spaces": 4
},
"tabWidth": 4,
"maximumBlankLines": 1,
"respectsExistingLineBreaks": true,
"lineBreakBeforeControlFlowKeywords": false,
"lineBreakBeforeEachArgument": false
}
This skill should be used when the user asks to "create a slash command", "add a command", "write a custom command", "define command arguments", "use command frontmatter", "organize commands", "create command with file references", "interactive command", "use AskUserQuestion in command", or needs guidance on slash command structure, YAML frontmatter fields, dynamic arguments, bash execution in commands, user interaction patterns, or command development best practices for Claude Code.
This skill should be used when the user asks to "create an agent", "add an agent", "write a subagent", "agent frontmatter", "when to use description", "agent examples", "agent tools", "agent colors", "autonomous agent", or needs guidance on agent structure, system prompts, triggering conditions, or agent development best practices for Claude Code plugins.
This skill should be used when the user asks to "create a hook", "add a PreToolUse/PostToolUse/Stop hook", "validate tool use", "implement prompt-based hooks", "use ${CLAUDE_PLUGIN_ROOT}", "set up event-driven automation", "block dangerous commands", or mentions hook events (PreToolUse, PostToolUse, Stop, SubagentStop, SessionStart, SessionEnd, UserPromptSubmit, PreCompact, Notification). Provides comprehensive guidance for creating and implementing Claude Code plugin hooks with focus on advanced prompt-based hooks API.