Declarations
Introduce types, operators, variables, and other names and constructs.
A declaration introduces a new name or construct into your program. For example, you use declarations to introduce functions and methods, to introduce variables and constants, and to define enumeration, structure, class, and protocol types. You can also use a declaration to extend the behavior of an existing named type and to import symbols into your program that are declared elsewhere.
In Swift, most declarations are also definitions in the sense that they're implemented or initialized at the same time they're declared. That said, because protocols don't implement their members, most protocol members are declarations only. For convenience and because the distinction isn't that important in Swift, the term declaration covers both declarations and definitions.
Grammar of a declaration:
declaration → import-declaration
declaration → constant-declaration
declaration → variable-declaration
declaration → typealias-declaration
declaration → function-declaration
declaration → enum-declaration
declaration → struct-declaration
declaration → class-declaration
declaration → actor-declaration
declaration → protocol-declaration
declaration → initializer-declaration
declaration → deinitializer-declaration
declaration → extension-declaration
declaration → subscript-declaration
declaration → macro-declaration
declaration → operator-declaration
declaration → precedence-group-declaration
Top-Level Code
The top-level code in a Swift source file consists of zero or more statements, declarations, and expressions. By default, variables, constants, and other named declarations that are declared at the top-level of a source file are accessible to code in every source file that's part of the same module. You can override this default behavior by marking the declaration with an access-level modifier, as described in doc:Declarations#Access-Control-Levels.
There are two kinds of top-level code: top-level declarations and executable top-level code. Top-level declarations consist of only declarations, and are allowed in all Swift source files. Executable top-level code contains statements and expressions, not just declarations, and is allowed only as the top-level entry point for the program.
The Swift code you compile to make an executable
can contain at most one of the following approaches
to mark the top-level entry point,
regardless of how the code is organized into files and modules:
a file that contains top-level executable code,
a main.swift file,
the main attribute,
the NSApplicationMain attribute,
or the UIApplicationMain attribute.
Grammar of a top-level declaration:
top-level-declaration → statements?
Code Blocks
A code block is used by a variety of declarations and control structures to group statements together. It has the following form:
{
<#statements#>
}
The statements inside a code block include declarations, expressions, and other kinds of statements and are executed in order of their appearance in source code.
Grammar of a code block:
code-block →
{statements?}
Import Declaration
An import declaration lets you access symbols
that are declared outside the current file.
The basic form imports the entire module;
it consists of the import keyword followed by a module name:
import <#module#>
Providing more detail limits which symbols are imported --- you can specify a specific submodule or a specific declaration within a module or submodule. When this detailed form is used, only the imported symbol (and not the module that declares it) is made available in the current scope.
import <#import kind#> <#module#>.<#symbol name#>
import <#module#>.<#submodule#>
Grammar of an import declaration:
import-declaration → attributes?
importimport-kind? import-pathimport-kind →
typealias|struct|class|enum|protocol|let|var|func
import-path → identifier | identifier.import-path
Constant Declaration
A constant declaration introduces a constant named value into your program.
Constant declarations are declared using the let keyword and have the following form:
let <#constant name#>: <#type#> = <#expression#>
A constant declaration defines an immutable binding between the constant name and the value of the initializer expression; after the value of a constant is set, it can't be changed. That said, if a constant is initialized with a class object, the object itself can change, but the binding between the constant name and the object it refers to can't.
When a constant is declared at global scope, it must be initialized with a value. When a constant declaration occurs in the context of a function or method, it can be initialized later, as long as it's guaranteed to have a value set before the first time its value is read. If the compiler can prove that the constant's value is never read, the constant isn't required to have a value set at all. This analysis is called definite initialization --- the compiler proves that a value is definitely set before being read.
Note: Definite initialization can't construct proofs that require domain knowledge, and its ability to track state across conditionals has a limit. If you can determine that constant always has a value set, but the compiler can't prove this is the case, try simplifying the code paths that set the value, or use a variable declaration instead.
When a constant declaration occurs in the context of a class or structure declaration, it's considered a constant property. Constant declarations aren't computed properties and therefore don't have getters or setters.
If the constant name of a constant declaration is a tuple pattern, the name of each item in the tuple is bound to the corresponding value in the initializer expression.
let (firstNumber, secondNumber) = (10, 42)
In this example,
firstNumber is a named constant for the value 10,
and secondNumber is a named constant for the value 42.
Both constants can now be used independently:
print("The first number is \(firstNumber).")
// Prints "The first number is 10."
print("The second number is \(secondNumber).")
// Prints "The second number is 42."
The type annotation (: type) is optional in a constant declaration
when the type of the constant name can be inferred,
as described in doc:Types#Type-Inference.
To declare a constant type property,
mark the declaration with the static declaration modifier.
A constant type property of a class is always implicitly final;
you can't mark it with the class or final declaration modifier
to allow or disallow overriding by subclasses.
Type properties are discussed in doc:Properties#Type-Properties.
For more information about constants and for guidance about when to use them, see doc:TheBasics#Constants-and-Variables and doc:Properties#Stored-Properties.
Grammar of a constant declaration:
constant-declaration → attributes? declaration-modifiers?
letpattern-initializer-listpattern-initializer-list → pattern-initializer | pattern-initializer
,pattern-initializer-list
pattern-initializer → pattern initializer?
initializer →=expression
Variable Declaration
A variable declaration introduces a variable named value into your program
and is declared using the var keyword.
Variable declarations have several forms that declare different kinds of named, mutable values, including stored and computed variables and properties, stored variable and property observers, and static variable properties. The appropriate form to use depends on the scope at which the variable is declared and the kind of variable you intend to declare.
Note: You can also declare properties in the context of a protocol declaration, as described in doc:Declarations#Protocol-Property-Declaration.
You can override a property in a subclass by marking the subclass's property declaration
with the override declaration modifier, as described in doc:Inheritance#Overriding.
Stored Variables and Stored Variable Properties
The following form declares a stored variable or stored variable property:
var <#variable name#>: <#type#> = <#expression#>
You define this form of a variable declaration at global scope, the local scope of a function, or in the context of a class or structure declaration. When a variable declaration of this form is declared at global scope or the local scope of a function, it's referred to as a stored variable. When it's declared in the context of a class or structure declaration, it's referred to as a stored variable property.
The initializer expression can't be present in a protocol declaration,
but in all other contexts, the initializer expression is optional.
That said, if no initializer expression is present,
the variable declaration must include an explicit type annotation (: type).
As with constant declarations, if a variable declaration omits the initializer expression, the variable must have a value set before the first time it is read. Also like constant declarations, if the variable name is a tuple pattern, the name of each item in the tuple is bound to the corresponding value in the initializer expression.
As their names suggest, the value of a stored variable or a stored variable property is stored in memory.
Computed Variables and Computed Properties
The following form declares a computed variable or computed property:
var <#variable name#>: <#type#> {
get {
<#statements#>
}
set(<#setter name#>) {
<#statements#>
}
}
You define this form of a variable declaration at global scope, the local scope of a function, or in the context of a class, structure, enumeration, or extension declaration. When a variable declaration of this form is declared at global scope or the local scope of a function, it's referred to as a computed variable. When it's declared in the context of a class, structure, or extension declaration, it's referred to as a computed property.
The getter is used to read the value, and the setter is used to write the value. The setter clause is optional, and when only a getter is needed, you can omit both clauses and simply return the requested value directly, as described in doc:Properties#Read-Only-Computed-Properties. But if you provide a setter clause, you must also provide a getter clause.
The setter name and enclosing parentheses is optional.
If you provide a setter name, it's used as the name of the parameter to the setter.
If you don't provide a setter name, the default parameter name to the setter is newValue,
as described in doc:Properties#Shorthand-Setter-Declaration.
Unlike stored named values and stored variable properties, the value of a computed named value or a computed property isn't stored in memory.
For more information and to see examples of computed properties, see doc:Properties#Computed-Properties.
Stored Variable Observers and Property Observers
You can also declare a stored variable or property with willSet and didSet observers.
A stored variable or property declared with observers has the following form:
var <#variable name#>: <#type#> = <#expression#> {
willSet(<#setter name#>) {
<#statements#>
}
didSet(<#setter name#>) {
<#statements#>
}
}
You define this form of a variable declaration at global scope, the local scope of a function, or in the context of a class or structure declaration. When a variable declaration of this form is declared at global scope or the local scope of a function, the observers are referred to as stored variable observers. When it's declared in the context of a class or structure declaration, the observers are referred to as property observers.
You can add property observers to any stored property. You can also add property observers to any inherited property (whether stored or computed) by overriding the property within a subclass, as described in doc:Inheritance#Overriding-Property-Observers.
The initializer expression is optional in the context of a class or structure declaration, but required elsewhere. The type annotation is optional when the type can be inferred from the initializer expression. This expression is evaluated the first time you read the property's value. If you overwrite the property's initial value without reading it, this expression is evaluated before the first time you write to the property.
The willSet and didSet observers provide a way to observe (and to respond appropriately)
when the value of a variable or property is being set.
The observers aren't called when the variable or property
is first initialized.
Instead, they're called only when the value is set outside of an initialization context.
A willSet observer is called just before the value of the variable or property
is set. The new value is passed to the willSet observer as a constant,
and therefore it can't be changed in the implementation of the willSet clause.
The didSet observer is called immediately after the new value is set. In contrast
to the willSet observer, the old value of the variable or property
is passed to the didSet observer in case you still need access to it. That said,
if you assign a value to a variable or property within its own didSet observer clause,
that new value that you assign will replace the one that was just set and passed to
the willSet observer.
The setter name and enclosing parentheses in the willSet and didSet clauses are optional.
If you provide setter names,
they're used as the parameter names to the willSet and didSet observers.
If you don't provide setter names,
the default parameter name to the willSet observer is newValue
and the default parameter name to the didSet observer is oldValue.
The didSet clause is optional when you provide a willSet clause.
Likewise, the willSet clause is optional when you provide a didSet clause.
If the body of the didSet observer refers to the old value,
the getter is called before the observer,
to make the old value available.
Otherwise, the new value is stored without calling the superclass's getter.
The example below shows a computed property that's defined by the superclass
and overridden by its subclasses to add an observer.
class Superclass {
private var xValue = 12
var x: Int {
get { print("Getter was called"); return xValue }
set { print("Setter was called"); xValue = newValue }
}
}
// This subclass doesn't refer to oldValue in its observer, so the
// superclass's getter is called only once to print the value.
class New: Superclass {
override var x: Int {
didSet { print("New value \(x)") }
}
}
let new = New()
new.x = 100
// Prints "Setter was called".
// Prints "Getter was called".
// Prints "New value 100".
// This subclass refers to oldValue in its observer, so the superclass's
// getter is called once before the setter, and again to print the value.
class NewAndOld: Superclass {
override var x: Int {
didSet { print("Old value \(oldValue) - new value \(x)") }
}
}
let newAndOld = NewAndOld()
newAndOld.x = 200
// Prints "Getter was called".
// Prints "Setter was called".
// Prints "Getter was called".
// Prints "Old value 12 - new value 200".
For more information and to see an example of how to use property observers, see doc:Properties#Property-Observers.
Type Variable Properties
To declare a type variable property,
mark the declaration with the static declaration modifier.
Classes can mark type computed properties with the class declaration modifier instead
to allow subclasses to override the superclass’s implementation.
Type properties are discussed in doc:Properties#Type-Properties.
Grammar of a variable declaration:
variable-declaration → variable-declaration-head pattern-initializer-list
variable-declaration → variable-declaration-head variable-name type-annotation code-block
variable-declaration → variable-declaration-head variable-name type-annotation getter-setter-block
variable-declaration → variable-declaration-head variable-name type-annotation getter-setter-keyword-block
variable-declaration → variable-declaration-head variable-name initializer willSet-didSet-block
variable-declaration → variable-declaration-head variable-name type-annotation initializer? willSet-didSet-blockvariable-declaration-head → attributes? declaration-modifiers?
var
variable-name → identifiergetter-setter-block → code-block
getter-setter-block →{getter-clause setter-clause?}
getter-setter-block →{setter-clause getter-clause}
getter-clause → attributes? mutation-modifier?getcode-block
setter-clause → attributes? mutation-modifier?setsetter-name? code-block
setter-name →(identifier)getter-setter-keyword-block →
{getter-keyword-clause setter-keyword-clause?}
getter-setter-keyword-block →{setter-keyword-clause getter-keyword-clause}
getter-keyword-clause → attributes? mutation-modifier?get
setter-keyword-clause → attributes? mutation-modifier?setwillSet-didSet-block →
{willSet-clause didSet-clause?}
willSet-didSet-block →{didSet-clause willSet-clause?}
willSet-clause → attributes?willSetsetter-name? code-block
didSet-clause → attributes?didSetsetter-name? code-block
Type Alias Declaration
A type alias declaration introduces a named alias of an existing type into your program.
Type alias declarations are declared using the typealias keyword and have the following form:
typealias <#name#> = <#existing type#>
After a type alias is declared, the aliased name can be used instead of the existing type everywhere in your program. The existing type can be a named type or a compound type. Type aliases don't create new types; they simply allow a name to refer to an existing type.
A type alias declaration can use generic parameters to give a name to an existing generic type. The type alias can provide concrete types for some or all of the generic parameters of the existing type. For example:
typealias StringDictionary<Value> = Dictionary<String, Value>
// The following dictionaries have the same type.
var dictionary1: StringDictionary<Int> = [:]
var dictionary2: Dictionary<String, Int> = [:]
When a type alias is declared with generic parameters, the constraints on those parameters must match exactly the constraints on the existing type's generic parameters. For example:
typealias DictionaryOfInts<Key: Hashable> = Dictionary<Key, Int>
Because the type alias and the existing type can be used interchangeably, the type alias can't introduce additional generic constraints.
A type alias can forward an existing type's generic parameters
by omitting all generic parameters from the declaration.
For example,
the Diccionario type alias declared here
has the same generic parameters and constraints as Dictionary.
typealias Diccionario = Dictionary
Inside a protocol declaration, a type alias can give a shorter and more convenient name to a type that's used frequently. For example:
protocol Sequence {
associatedtype Iterator: IteratorProtocol
typealias Element = Iterator.Element
}
func sum<T: Sequence>(_ sequence: T) -> Int where T.Element == Int {
// ...
}
Without this type alias,
the sum function would have to refer to the associated type
as T.Iterator.Element instead of T.Element.
See also doc:Declarations#Protocol-Associated-Type-Declaration.
Grammar of a type alias declaration:
typealias-declaration → attributes? access-level-modifier?
typealiastypealias-name generic-parameter-clause? typealias-assignment
typealias-name → identifier
typealias-assignment →=type
Function Declaration
A function declaration introduces a function or method into your program.
A function declared in the context of class, structure, enumeration, or protocol
is referred to as a method.
Function declarations are declared using the func keyword and have the following form:
func <#function name#>(<#parameters#>) -> <#return type#> {
<#statements#>
}
If the function has a return type of Void,
the return type can be omitted as follows:
func <#function name#>(<#parameters#>) {
<#statements#>
}
The type of each parameter must be included ---
it can't be inferred.
If you write inout in front of a parameter's type,
the parameter can be modified inside the scope of the function.
In-out parameters are discussed in detail
in doc:Declarations#In-Out-Parameters, below.
A function declaration whose statements
include only a single expression
is understood to return the value of that expression.
This implicit return syntax is considered
only when the expression's type and the function's return type
aren't Void
and aren't an enumeration like Never that doesn't have any cases.
Functions can return multiple values using a tuple type as the return type of the function.
A function definition can appear inside another function declaration. This kind of function is known as a nested function.
A nested function is nonescaping if it captures a value that's guaranteed to never escape --- such as an in-out parameter --- or passed as a nonescaping function argument. Otherwise, the nested function is an escaping function.
For a discussion of nested functions, see doc:Functions#Nested-Functions.
Parameter Names
Function parameters are a comma-separated list where each parameter has one of several forms. The order of arguments in a function call must match the order of parameters in the function's declaration. The simplest entry in a parameter list has the following form:
<#parameter name#>: <#parameter type#>
A parameter has a name, which is used within the function body, as well as an argument label, which is used when calling the function or method. By default, parameter names are also used as argument labels. For example:
func f(x: Int, y: Int) -> Int { return x + y }
f(x: 1, y: 2) // both x and y are labeled
You can override the default behavior for argument labels with one of the following forms:
<#argument label#> <#parameter name#>: <#parameter type#>
_ <#parameter name#>: <#parameter type#>
A name before the parameter name gives the parameter an explicit argument label, which can be different from the parameter name. The corresponding argument must use the given argument label in function or method calls.
An underscore (_) before a parameter name
suppresses the argument label.
The corresponding argument must have no label in function or method calls.
func repeatGreeting(_ greeting: String, count n: Int) { /* Greet n times */ }
repeatGreeting("Hello, world!", count: 2) // count is labeled, greeting is not
Parameter Modifiers
A parameter modifier changes how an argument is passed to the function.
<#argument label#> <#parameter name#>: <#parameter modifier#> <#parameter type#>
To use a parameter modifier,
write inout, borrowing, or consuming
before the argument's type.
func someFunction(a: inout A, b: consuming B, c: C) { ... }
In-Out Parameters
By default, function arguments in Swift are passed by value:
Any changes made within the function are not visible in the caller.
To make an in-out parameter instead,
you apply the inout parameter modifier.
func someFunction(a: inout Int) {
a += 1
}
When calling a function that includes in-out parameters,
the in-out argument must be prefixed with an ampersand (&)
to mark that the function call can change the argument's value.
var x = 7
someFunction(a: &x)
print(x) // Prints "8"
In-out parameters are passed as follows:
- When the function is called, the value of the argument is copied.
- In the body of the function, the copy is modified.
- When the function returns, the copy's value is assigned to the original argument.
This behavior is known as copy-in copy-out or call by value result. For example, when a computed property or a property with observers is passed as an in-out parameter, its getter is called as part of the function call and its setter is called as part of the function return.
As an optimization, when the argument is a value stored at a physical address in memory, the same memory location is used both inside and outside the function body. The optimized behavior is known as call by reference; it satisfies all of the requirements of the copy-in copy-out model while removing the overhead of copying. Write your code using the model given by copy-in copy-out, without depending on the call-by-reference optimization, so that it behaves correctly with or without the optimization.
Within a function, don't access a value that was passed as an in-out argument, even if the original value is available in the current scope. Accessing the original is a simultaneous access of the value, which violates memory exclusivity.
var someValue: Int
func someFunction(a: inout Int) {
a += someValue
}
// Error: This causes a runtime exclusivity violation
someFunction(a: &someValue)
For the same reason, you can't pass the same value to multiple in-out parameters.
var someValue: Int
func someFunction(a: inout Int, b: inout Int) {
a += b
b += 1
}
// Error: Cannot pass the same value to multiple in-out parameters
someFunction(a: &someValue, b: &someValue)
For more information about memory safety and memory exclusivity, see doc:MemorySafety.
A closure or nested function that captures an in-out parameter must be nonescaping. If you need to capture an in-out parameter without mutating it, use a capture list to explicitly capture the parameter immutably.
func someFunction(a: inout Int) -> () -> Int {
return { [a] in return a + 1 }
}
If you need to capture and mutate an in-out parameter, use an explicit local copy, such as in multithreaded code that ensures all mutation has finished before the function returns.
func multithreadedFunction(queue: DispatchQueue, x: inout Int) {
// Make a local copy and manually copy it back.
var localX = x
defer { x = localX }
// Operate on localX asynchronously, then wait before returning.
queue.async { someMutatingOperation(&localX) }
queue.sync {}
}
For more discussion and examples of in-out parameters, see doc:Functions#In-Out-Parameters.
Borrowing and Consuming Parameters
By default, Swift uses a set of rules
to automatically manage object lifetime across function calls,
copying values when required.
The default rules are designed to minimize overhead in most cases ---
if you want more specific control,
you can apply the borrowing or consuming parameter modifier.
In this case,
use copy to explicitly mark copy operations.
In addition,
values of a noncopyable type must be passed as either borrowing or consuming.
Regardless of whether you use the default rules, Swift guarantees that object lifetime and ownership are correctly managed in all cases. These parameter modifiers impact only the relative efficiency of particular usage patterns, not correctness.
The borrowing modifier indicates that the function
does not keep the parameter's value.
In this case, the caller maintains ownership of the object
and the responsibility for the object's lifetime.
Using borrowing minimizes overhead when the function
uses the object only transiently.
// `isLessThan` does not keep either argument
func isLessThan(lhs: borrowing A, rhs: borrowing A) -> Bool {
...
}
If the function needs to keep the parameter's value
for example, by storing it in a global variable ---
you use copy to explicitly copy that value.
// As above, but this `isLessThan` also wants to record the smallest value
func isLessThan(lhs: borrowing A, rhs: borrowing A) -> Bool {
if lhs < storedValue {
storedValue = copy lhs
} else if rhs < storedValue {
storedValue = copy rhs
}
return lhs < rhs
}
Conversely,
the consuming parameter modifier indicates
that the function takes ownership of the value,
accepting responsibility for either storing or destroying it
before the function returns.
// `store` keeps its argument, so mark it `consuming`
func store(a: consuming A) {
someGlobalVariable = a
}
Using consuming minimizes overhead when the caller no longer
needs to use the object after the function call.
// Usually, this is the last thing you do with a value
store(a: value)
If you keep using a copyable object after the function call, the compiler automatically makes a copy of that object before the function call.
// The compiler inserts an implicit copy here
store(a: someValue) // This function consumes someValue
print(someValue) // This uses the copy of someValue
Unlike inout, neither borrowing nor
consuming parameters require any special
notation when you call the function:
func someFunction(a: borrowing A, b: consuming B) { ... }
someFunction(a: someA, b: someB)
The explicit use of either borrowing or consuming
indicates your intention to more tightly control
the overhead of runtime ownership management.
Because copies can cause unexpected runtime ownership
operations,
parameters marked with either of these
modifiers cannot be copied unless you
use an explicit copy keyword:
func borrowingFunction1(a: borrowing A) {
// Error: Cannot implicitly copy a
// This assignment requires a copy because
// `a` is only borrowed from the caller.
someGlobalVariable = a
}
func borrowingFunction2(a: borrowing A) {
// OK: Explicit copying works
someGlobalVariable = copy a
}
func consumingFunction1(a: consuming A) {
// Error: Cannot implicitly copy a
// This assignment requires a copy because
// of the following `print`
someGlobalVariable = a
print(a)
}
func consumingFunction2(a: consuming A) {
// OK: Explicit copying works regardless
someGlobalVariable = copy a
print(a)
}
func consumingFunction3(a: consuming A) {
// OK: No copy needed here because this is the last use
someGlobalVariable = a
}
Special Kinds of Parameters
Parameters can be ignored, take a variable number of values, and provide default values using the following forms:
_ : <#parameter type#>
<#parameter name#>: <#parameter type#>...
<#parameter name#>: <#parameter type#> = <#default argument value#>
An underscore (_) parameter
is explicitly ignored and can't be accessed within the body of the function.
A parameter with a base type name followed immediately by three dots (...)
is understood as a variadic parameter.
A parameter that immediately follows a variadic parameter
must have an argument label.
A function can have multiple variadic parameters.
A variadic parameter is treated as an array that contains elements of the base type name.
For example, the variadic parameter Int... is treated as [Int].
For an example that uses a variadic parameter,
see doc:Functions#Variadic-Parameters.
A parameter with an equal sign (=) and an expression after its type
is understood to have a default value of the given expression.
The given expression is evaluated when the function is called.
If the parameter is omitted when calling the function,
the
…(truncated)