Attributes and Compiler Control
|
This section documents the Swift 6 language mode as shipped by Swift 6.3, as published in The Swift Programming Language at docs.swift.org, which is the reference these pages are written and verified against. 6.4-beta-only features are always flagged as such — never presented as baseline. This content was generated with the assistance of AI and should be verified against docs.swift.org before being relied on in production. This section’s bibliography lists the reference material consulted while preparing these pages. |
Attributes attach compiler-visible metadata to a declaration or a type; declaration modifiers (final,
static, override, …) and compiler control statements (#if, #warning, …) round out the language’s
remaining ways of talking to the compiler rather than to the running program.
Declaration Attributes
@available(iOS 17, macOS 14, *)
func modernAPI() { }
@available(*, deprecated, renamed: "modernAPI()", message: "use modernAPI() instead")
func legacyAPI() { }
@discardableResult
func logAndReturn(_ value: Int) -> Int {
print(value)
return value // callers may ignore this return value with no warning, thanks to @discardableResult
}
@main
struct EntryPoint {
static func main() { print("started") }
}
@available(…) states the platforms/versions a declaration requires, or marks it deprecated/unavailable/
renamed:, and is checked at every call site; @discardableResult silences the "result unused" warning a
non-Void-returning function would otherwise get when its return value is ignored; @main designates the
program’s entry-point type, replacing a top-level main.swift file.
@frozen
public struct Point { // library-evolution promise: no new stored properties will ever be added
public var x, y: Double
}
@inlinable
public func distance(_ a: Point, _ b: Point) -> Double {
((a.x - b.x) * (a.x - b.x) + (a.y - b.y) * (a.y - b.y)).squareRoot()
}
@usableFromInline
internal func helper() -> Int { 42 } // internal, but callable from an @inlinable function in the same module
@backDeployed(before: iOS 17)
public func newConvenienceAPI() { } // callable on OS versions older than where it actually shipped
@frozen and @inlinable are library-evolution attributes for binary frameworks: @frozen promises a struct/
enum’s layout won’t change in a future library version (letting clients store it inline rather than through an
indirection), and @inlinable exposes a function’s body for cross-module inlining, which in turn requires any
internal symbol it touches to be marked @usableFromInline. @backDeployed(before:) lets a newer API run on
OS versions that shipped before it, by embedding a compatibility shim in the calling binary.
@objc
class LegacyBridge: NSObject {
@objc(displayNameForUser:)
func displayName(for user: String) -> String { user.capitalized }
}
@objcMembers
class AllExposed: NSObject { // every member below is implicitly @objc, no need to annotate each one
var count = 0
func increment() { count += 1 }
}
class PlainSwift {
@nonobjc
func swiftOnly() { } // opts one member out of an otherwise @objc-exposed type
}
@preconcurrency
import SomeUnauditedObjCFramework // suppresses Sendable/isolation warnings for this import only
@testable import MyAppModule // exposes `internal` declarations to a test target
@objc/@objcMembers/@nonobjc control Objective-C runtime visibility — see
Interoperability with C,
Objective-C and C++ for the bridging this enables. @preconcurrency on an import relaxes Swift 6’s strict
concurrency checking for a module that predates Sendable auditing, without disabling it project-wide — see
Actors, Isolation and Sendable. @testable
import is how a test target reaches a module’s internal (but not private) declarations, covered again in
Testing.
@dynamicMemberLookup
struct Proxy { subscript(dynamicMember member: String) -> String { member } }
@propertyWrapper
struct Clamped {
var wrappedValue: Int
init(wrappedValue: Int) { self.wrappedValue = wrappedValue }
}
@resultBuilder
struct ArrayBuilder<T> {
static func buildBlock(_ items: T...) -> [T] { items }
}
@globalActor
actor SettingsActor {
static let shared = SettingsActor()
}
func apply(@autoclosure _ condition: () -> Bool, onFailure action: @escaping @Sendable () -> Void) { }
@dynamicMemberLookup, @propertyWrapper and @resultBuilder are covered in full in
Key Paths and Dynamic Member Lookup,
Properties and
Result Builders respectively; @globalActor defines a new
global actor (like the built-in @MainActor) usable to annotate other declarations, per
Actors, Isolation and Sendable. Type
attributes — @escaping (a closure parameter that may outlive the call), @autoclosure (a parameter written
as an expression, auto-wrapped in a closure), @Sendable (a closure safely shared across concurrency domains) and
@convention (a function value’s calling convention: swift, block, or c) — are covered where each is most
relevant: Closures for the first three,
Interoperability with C,
Objective-C and C++ for @convention.
@unknown default, Declaration Modifiers and Compiler Control
enum Direction { case north, south, east, west }
func describe(_ direction: Direction) -> String {
switch direction {
case .north: "N"
case .south: "S"
case .east, .west: "E/W"
@unknown default: "unrecognized direction added in a newer library version"
}
}
@unknown default marks a switch case that should only ever run if a non-frozen enum (typically from a
binary-distributed library) gains a new case in a future version the code wasn’t compiled against — unlike a
plain default, the compiler still warns if the switch doesn’t otherwise cover every case currently known, so
adding a genuinely new case to the enum today is caught at the call site rather than silently swallowed.
class Base {
final func cannotBeOverridden() { }
class func classMethod() { } // dynamically dispatched, overridable by subclasses
static func staticMethod() { } // like class func, but cannot itself be overridden
required init() { } // every subclass must provide this initializer
lazy var expensive: [Int] = computeOnce()
weak var delegate: AnyObject?
unowned let owner: Base
indirect enum Tree { case node(Tree, Tree) }
private(set) var readOnlyOutside = 0
dynamic func objcDispatched() { } // forces Objective-C message-send dispatch, e.g. for KVO/swizzling
nonisolated func notActorConfined() { }
mutating func mutatingOnStruct() { }
nonmutating func doesNotMutate() { }
}
Declaration modifiers each state a fact about how a declaration participates in inheritance, memory management,
concurrency isolation, or dispatch: final forbids overriding; static/class distinguish non-overridable vs.
overridable type-level members; override/required govern subclass obligations; lazy defers a stored
property’s initial value until first access; weak/unowned (see
Automatic Reference Counting); indirect
lets an enum case hold itself recursively; optional/dynamic matter for @objc protocol members and runtime
dispatch; nonisolated/mutating/nonmutating govern actor isolation and value-type self-mutation (see
Actors, Isolation and Sendable and
Methods and Subscripts).
#if os(macOS) || os(Linux)
import Foundation
#endif
#if swift(>=6.0) && compiler(>=6.0)
let usesTypedThrows = true
#endif
#if canImport(UIKit)
import UIKit
#endif
#if hasFeature(StrictConcurrency)
// code that only compiles under a specific upcoming-feature flag
#endif
#warning("this branch still uses the deprecated API")
#if DEBUG
#error("DEBUG builds must not ship")
#endif
func trace(file: String = #file, line: Int = #line, function: String = #function) {
print("\(function) at \(file):\(line)")
}
if #available(iOS 17, *) {
modernAPI()
} else if #unavailable(macOS 14) {
// fallback path for platforms this code doesn't support
}
Conditional compilation (#if/#elseif/#else/#endif) tests platform (os(), arch()), language/compiler
version (swift(), compiler()), module availability (canImport()), or an upcoming-feature flag
(hasFeature()), and is resolved entirely before type-checking — an unreachable branch’s code isn’t even
parsed against the current platform. #warning/#error emit a diagnostic (the latter halting compilation) at a
specific point in the source; #file/#line/#function (and #filePath/#column) capture the call site’s
source location when used as a default parameter value, which is how Swift Testing’s #expect reports failures
at the exact line that failed rather than inside its own implementation. #available/#unavailable are runtime
platform-version checks — unlike @available, which the compiler checks statically, these guard a branch of
code that runs conditionally depending on the actual OS version at execution time.
See Also
-
Closures —
@escaping,@autoclosureand@Sendablein the context of closures specifically. -
Properties —
@propertyWrapperand@Observablein full. -
Actors, Isolation and Sendable —
@MainActor,@globalActor,nonisolated, and@preconcurrencyin the concurrency-checking context they matter most in. -
Interoperability with C, Objective-C and C++ —
@objc,@convention, and bridging to Objective-C in full.
References
TSPL: Attributes; Declarations → Declaration Modifiers; Statements → Compiler Control Statements.