Primary Expressions¶
Primary expressions are the most basic kind of expression. They can be used as expressions on their own, and they can be combined with other tokens to make prefix expressions, infix expressions, and postfix expressions.
Note
primary-expression → identifier generic-argument-clause? primary-expression → literal-expression primary-expression → self-expression primary-expression → superclass-expression primary-expression → conditional-expression primary-expression → closure-expression primary-expression → parenthesized-expression primary-expression → tuple-expression primary-expression → implicit-member-expression primary-expression → wildcard-expression primary-expression → macro-expansion-expression primary-expression → key-path-expression primary-expression → selector-expression primary-expression → key-path-string-expression
Literal Expression¶
A literal expression consists of either an ordinary literal (such as a string or a number), an array or dictionary literal, or a playground literal.
Note
Prior to Swift 5.9, the following special literals were recognized: #column, #dsohandle, #fileID, #filePath, #file, #function, and #line. These are now implemented as macros in the Swift standard library: column(), dsohandle(), fileID(), filePath(), file(), function(), and line().
An array literal is an ordered collection of values. It has the following form:
[<#value 1#>, <#value 2#>, <#...#>]
The last expression in the array can be followed by an optional comma. The value of an array literal has type [T], where T is the type of the expressions inside it. If there are expressions of multiple types, T is their closest common supertype. Empty array literals are written using an empty pair of square brackets and can be used to create an empty array of a specified type.
var emptyArray: [Double] = []
A dictionary literal is an unordered collection of key-value pairs. It has the following form:
[<#key 1#>: <#value 1#>, <#key 2#>: <#value 2#>, <#...#>]
The last expression in the dictionary can be followed by an optional comma. The value of a dictionary literal has type [Key: Value], where Key is the type of its key expressions and Value is the type of its value expressions. If there are expressions of multiple types, Key and Value are the closest common supertype for their respective values. An empty dictionary literal is written as a colon inside a pair of brackets ([:]) to distinguish it from an empty array literal. You can use an empty dictionary literal to create an empty dictionary literal of specified key and value types.
var emptyDictionary: [String: Double] = [:]
A playground literal is used by Xcode to create an interactive representation of a color, file, or image within the program editor. Playground literals in plain text outside of Xcode are represented using a special literal syntax.
For information on using playground literals in Xcode, see Add a color, file, or image literal in Xcode Help.
Note
literal-expression → literal literal-expression → array-literal | dictionary-literal | playground-literal
array-literal → [ array-literal-items? ,? ]
array-literal-items → array-literal-item | array-literal-item , array-literal-items
array-literal-item → expression
dictionary-literal → [ dictionary-literal-items ,? ] | [ : ]
dictionary-literal-items → dictionary-literal-item | dictionary-literal-item , dictionary-literal-items
dictionary-literal-item → expression : expression
playground-literal → #colorLiteral ( red : expression , green : expression , blue : expression , alpha : expression )
playground-literal → #fileLiteral ( resourceName : expression )
playground-literal → #imageLiteral ( resourceName : expression )
Self Expression¶
The self expression is an explicit reference to the current type or instance of the type in which it occurs. It has the following forms:
self
self.<#member name#>
self[<#subscript index#>]
self(<#initializer arguments#>)
self.init(<#initializer arguments#>)
In an initializer, subscript, or instance method, self refers to the current instance of the type in which it occurs. In a type method, self refers to the current type in which it occurs.
The self expression is used to specify scope when accessing members, providing disambiguation when there’s another variable of the same name in scope, such as a function parameter. For example:
class SomeClass {
var greeting: String
init(greeting: String) {
self.greeting = greeting
}
}
In a mutating method of a value type, you can assign a new instance of that value type to self. For example:
struct Point {
var x = 0.0, y = 0.0
mutating func moveBy(x deltaX: Double, y deltaY: Double) {
self = Point(x: x + deltaX, y: y + deltaY)
}
}
Note
self-expression → self | self-method-expression | self-subscript-expression | self-initializer-expression
self-method-expression → self . identifier
self-subscript-expression → self [ function-call-argument-list ]
self-initializer-expression → self . init
Superclass Expression¶
A superclass expression lets a class interact with its superclass. It has one of the following forms:
super.<#member name#>
super[<#subscript index#>]
super.init(<#initializer arguments#>)
The first form is used to access a member of the superclass. The second form is used to access the superclass’s subscript implementation. The third form is used to access an initializer of the superclass.
Subclasses can use a superclass expression in their implementation of members, subscripting, and initializers to make use of the implementation in their superclass.
Note
superclass-expression → superclass-method-expression | superclass-subscript-expression | superclass-initializer-expression
superclass-method-expression → super . identifier
superclass-subscript-expression → super [ function-call-argument-list ]
superclass-initializer-expression → super . init
Conditional Expression¶
A conditional expression evaluates to one of several given values based on the value of a condition. It has one the following forms:
if <#condition 1#> {
<#expression used if condition 1 is true#>
} else if <#condition 2#> {
<#expression used if condition 2 is true#>
} else {
<#expression used if both conditions are false#>
}
switch <#expression#> {
case <#pattern 1#>:
<#expression 1#>
case <#pattern 2#> where <#condition#>:
<#expression 2#>
default:
<#expression 3#>
}
A conditional expression has the same behavior and syntax as an if statement or a switch statement, except for the differences that the paragraphs below describe.
A conditional expression appears only in the following contexts:
As the value assigned to a variable.
As the initial value in a variable or constant declaration.
As the error thrown by a
throwexpression.As the value returned by a function, closure, or property getter.
As the value inside a branch of a conditional expression.
The branches of a conditional expression are exhaustive, ensuring that the expression always produces a value regardless of the condition. This means each if branch needs a corresponding else branch.
Each branch contains either a single expression, which is used as the value for the conditional expression when that branch’s conditional is true, a throw statement, or a call to a function that never returns.
Each branch must produce a value of the same type. Because type checking of each branch is independent, you sometimes need to specify the value’s type explicitly, like when branches include different kinds of literals, or when a branch’s value is nil. When you need to provide this information, add a type annotation to the variable that the result is assigned to, or add an as cast to the branches’ values.
let number: Double = if someCondition { 10 } else { 12.34 }
let number = if someCondition { 10 as Double } else { 12.34 }
Inside a result builder, conditional expressions can appear only as the initial value of a variable or constant. This behavior means when you write if or switch in a result builder — outside of a variable or constant declaration — that code is understood as a branch statement and one of the result builder’s methods transforms that code.
Don’t put a conditional expression in a try expression, even if one of the branches of a conditional expression is throwing.
Note
conditional-expression → if-expression | switch-expression
if-expression → if condition-list { statement } if-expression-tail
if-expression-tail → else if-expression
if-expression-tail → else { statement }
switch-expression → switch expression { switch-expression-cases }
switch-expression-cases → switch-expression-case switch-expression-cases?
switch-expression-case → case-label statement
switch-expression-case → default-label statement
Closure Expression¶
A closure expression creates a closure, also known as a lambda or an anonymous function in other programming languages. Like a function declaration, a closure contains statements, and it captures constants and variables from its enclosing scope. It has the following form:
{ (<#parameters#>) -> <#return type#> in
<#statements#>
}
The parameters have the same form as the parameters in a function declaration, as described in Function Declaration.
Writing throws or async in a closure expression explicitly marks a closure as throwing or asynchronous.
{ (<#parameters#>) async throws -> <#return type#> in
<#statements#>
}
If the body of a closure includes a throws statement or a try expression that isn’t nested inside of a do statement with exhaustive error handling, the closure is understood to be throwing. If a throwing closure throws errors of only a single type, the closure is understood as throwing that error type; otherwise, it’s understood as throwing any Error. Likewise, if the body includes an await expression, it’s understood to be asynchronous.
There are several special forms that allow closures to be written more concisely:
A closure can omit the types of its parameters, its return type, or both. If you omit the parameter names and both types, omit the
inkeyword before the statements. If the omitted types can’t be inferred, a compile-time error is raised.A closure may omit names for its parameters. Its parameters are then implicitly named
$followed by their position:$0,$1,$2, and so on.A closure that consists of only a single expression is understood to return the value of that expression. The contents of this expression are also considered when performing type inference on the surrounding expression.
The following closure expressions are equivalent:
myFunction { (x: Int, y: Int) -> Int in
return x + y
}
myFunction { x, y in
return x + y
}
myFunction { return $0 + $1 }
myFunction { $0 + $1 }
For information about passing a closure as an argument to a function, see Function Call Expression.
Closure expressions can be used without being stored in a variable or constant, such as when you immediately use a closure as part of a function call. The closure expressions passed to myFunction in code above are examples of this kind of immediate use. As a result, whether a closure expression is escaping or nonescaping depends on the surrounding context of the expression. A closure expression is nonescaping if it’s called immediately or passed as a nonescaping function argument. Otherwise, the closure expression is escaping.
For more information about escaping closures, see Escaping Closures.
Capture Lists¶
By default, a closure expression captures constants and variables from its surrounding scope with strong references to those values. You can use a capture list to explicitly control how values are captured in a closure.
A capture list is written as a comma-separated list of expressions surrounded by square brackets, before the list of parameters. If you use a capture list, you must also use the in keyword, even if you omit the parameter names, parameter types, and return type. The last expression in the capture list can be followed by an optional comma.
The entries in the capture list are initialized when the closure is created. For each entry in the capture list, a constant is initialized to the value of the constant or variable that has the same name in the surrounding scope. For example in the code below, a is included in the capture list but b is not, which gives them different behavior.
var a = 0
var b = 0
let closure = { [a] in
print(a, b)
}
a = 10
b = 10
closure()
// Prints "0 10".
There are two different things named a, the variable in the surrounding scope and the constant in the closure’s scope, but only one variable named b. The a in the inner scope is initialized with the value of the a in the outer scope when the closure is created, but their values aren’t connected in any special way. This means that a change to the value of a in the outer scope doesn’t affect the value of a in the inner scope, nor does a change to a inside the closure affect the value of a outside the closure. In contrast, there’s only one variable named b — the b in the outer scope — so changes from inside or outside the closure are visible in both places.
This distinction isn’t visible when the captured variable’s type has reference semantics. For example, there are two things named x in the code below, a variable in the outer scope and a constant in the inner scope, but they both refer to the same object because of reference semantics.
class SimpleClass {
var value: Int = 0
}
var x = SimpleClass()
var y = SimpleClass()
let closure = { [x] in
print(x.value, y.value)
}
x.value = 10
y.value = 10
closure()
// Prints "10 10".
If the type of the expression’s value is a class, you can mark the expression in a capture list with weak or unowned to capture a weak or unowned reference to the expression’s value.
myFunction { print(self.title) } // implicit strong capture
myFunction { [self] in print(self.title) } // explicit strong capture
myFunction { [weak self] in print(self!.title) } // weak capture
myFunction { [unowned self] in print(self.title) } // unowned capture
You can also bind an arbitrary expression to a named value in a capture list. The expression is evaluated when the closure is created, and the value is captured with the specified strength. For example:
// Weak capture of "self.parent" as "parent"
myFunction { [weak parent = self.parent] in print(parent!.title) }
For more information and examples of closure expressions, see Closure Expressions. For more information and examples of capture lists, see Resolving Strong Reference Cycles for Closures.
Note
closure-expression → { attributes? closure-signature? statements? }
closure-signature → capture-list? closure-parameter-clause async? throws-clause? function-result? in
closure-signature → capture-list in
closure-parameter-clause → ( ) | ( closure-parameter-list ,? ) | identifier-list
closure-parameter-list → closure-parameter | closure-parameter , closure-parameter-list
closure-parameter → closure-parameter-name type-annotation?
closure-parameter → closure-parameter-name type-annotation ...
closure-parameter-name → identifier
capture-list → [ capture-list-items ,? ]
capture-list-items → capture-list-item | capture-list-item , capture-list-items
capture-list-item → capture-specifier? identifier
capture-list-item → capture-specifier? identifier = expression
capture-list-item → capture-specifier? self-expression
capture-specifier → weak | unowned | unowned(safe) | unowned(unsafe)
Implicit Member Expression¶
An implicit member expression is an abbreviated way to access a member of a type, such as an enumeration case or a type method, in a context where type inference can determine the implied type. It has the following form:
.<#member name#>
For example:
var x = MyEnumeration.someValue
x = .anotherValue
If the inferred type is an optional, you can also use a member of the non-optional type in an implicit member expression.
var someOptional: MyEnumeration? = .someValue
Implicit member expressions can be followed by a postfix operator or other postfix syntax listed in Postfix Expressions. This is called a chained implicit member expression. Although it’s common for all of the chained postfix expressions to have the same type, the only requirement is that the whole chained implicit member expression needs to be convertible to the type implied by its context. Specifically, if the implied type is an optional you can use a value of the non-optional type, and if the implied type is a class type you can use a value of one of its subclasses. For example:
class SomeClass {
static var shared = SomeClass()
static var sharedSubclass = SomeSubclass()
var a = AnotherClass()
}
class SomeSubclass: SomeClass { }
class AnotherClass {
static var s = SomeClass()
func f() -> SomeClass { return AnotherClass.s }
}
let x: SomeClass = .shared.a.f()
let y: SomeClass? = .shared
let z: SomeClass = .sharedSubclass
In the code above, the type of x matches the type implied by its context exactly, the type of y is convertible from SomeClass to SomeClass?, and the type of z is convertible from SomeSubclass to SomeClass.
Note
implicit-member-expression → . identifier
implicit-member-expression → . identifier . postfix-expression
Parenthesized Expression¶
A parenthesized expression consists of an expression surrounded by parentheses. You can use parentheses to specify the precedence of operations by explicitly grouping expressions. Grouping parentheses don’t change an expression’s type — for example, the type of (1) is simply Int.
Note
parenthesized-expression → ( expression )
Tuple Expression¶
A tuple expression consists of a comma-separated list of expressions surrounded by parentheses. Each expression can have an optional identifier before it, separated by a colon (:). It has the following form:
(<#identifier 1#>: <#expression 1#>, <#identifier 2#>: <#expression 2#>, <#...#>)
Each identifier in a tuple expression must be unique within the scope of the tuple expression. In a nested tuple expression, identifiers at the same level of nesting must be unique. For example, (a: 10, a: 20) is invalid because the label a appears twice at the same level. However, (a: 10, b: (a: 1, x: 2)) is valid — although a appears twice, it appears once in the outer tuple and once in the inner tuple.
A tuple expression can contain zero expressions, or it can contain two or more expressions. A single expression inside parentheses is a parenthesized expression.
Note
Both an empty tuple expression and an empty tuple type are written () in Swift. Because Void is a type alias for (), you can use it to write an empty tuple type. However, like all type aliases, Void is always a type — you can’t use it to write an empty tuple expression.
The last expression in a tuple can be followed by an optional comma.
Note
tuple-expression → ( ) | ( tuple-element , tuple-element-list ,? )
tuple-element-list → tuple-element | tuple-element , tuple-element-list
tuple-element → expression | identifier : expression
Wildcard Expression¶
A wildcard expression is used to explicitly ignore a value during an assignment. For example, in the following assignment 10 is assigned to x and 20 is ignored:
(x, _) = (10, 20)
// x is 10, and 20 is ignored
Note
wildcard-expression → _
Macro-Expansion Expression¶
A macro-expansion expression consists of a macro name followed by a comma-separated list of the macro’s arguments in parentheses. The macro is expanded at compile time. Macro-expansion expressions have the following form:
<#macro name#>(<#macro argument 1#>, <#macro argument 2#>)
A macro-expansion expression omits the parentheses after the macro’s name if the macro doesn’t take any arguments.
A macro-expansion expression can appear as the default value for a parameter. When used as the default value of a function or method parameter, macros are evaluated using the source code location of the call site, not the location where they appear in a function definition. However, when a default value is a larger expression that contains a macro in addition to other code, those macros are evaluated where they appear in the function definition.
func f(a: Int = #line, b: Int = (#line), c: Int = 100 + #line) {
print(a, b, c)
}
f() // Prints "4 1 101"
In the function above, the default value for a is a single macro expression, so that macro is evaluated using the source code location where f(a:b:c:) is called. In contrast, the values for b and c are expressions that contain a macro — the macros in those expressions are evaluated using the source code location where f(a:b:c:) is defined.
When you use a macro as a default value, it’s type checked without expanding the macro, to check the following requirements:
The macro’s access level is the same as or less restrictive than the function that uses it.
The macro either takes no arguments, or its arguments are literals without string interpolation.
The macro’s return type matches the parameter’s type.
You use macro expressions to call freestanding macros. To call an attached macro, use the custom attribute syntax described in Attributes. Both freestanding and attached macros expand as follows:
Swift parses the source code to produce an abstract syntax tree (AST).
The macro implementation receives AST nodes as its input and performs the transformations needed by that macro.
The transformed AST nodes that the macro implementation produced are added to the original AST.
The expansion of each macro is independent and self-contained. However, as a performance optimization, Swift might start an external process that implements the macro and reuse the same process to expand multiple macros. When you implement a macro, that code must not depend on what macros your code previously expanded, or on any other external state like the current time.
For nested macros and attached macros that have multiple roles, the expansion process repeats. Nested macro-expansion expressions expand from the outside in. For example, in the code below outerMacro(_:) expands first and the unexpanded call to innerMacro(_:) appears in the abstract syntax tree that outerMacro(_:) receives as its input.
#outerMacro(12, #innerMacro(34), "some text")
An attached macro that has multiple roles expands once for each role. Each expansion receives the same, original, AST as its input. Swift forms the overall expansion by collecting all of the generated AST nodes and putting them in their corresponding places in the AST.
For an overview of macros in Swift, see Macros.
Note
macro-expansion-expression → # identifier generic-argument-clause? function-call-argument-clause? trailing-closures?
Key-Path Expression¶
A key-path expression refers to a property or subscript of a type. You use key-path expressions in dynamic programming tasks, such as key-value observing. They have the following form:
\<#type name#>.<#path#>
The type name is the name of a concrete type, including any generic parameters, such as String, [Int], or Set<Int>.
The path consists of property names, subscripts, optional-chaining expressions, and forced unwrapping expressions. Each of these key-path components can be repeated as many times as needed, in any order.
At compile time, a key-path expression is replaced by an instance of the KeyPath class.
To access a value using a key path, pass the key path to the subscript(keyPath:) subscript, which is available on all types. For example:
struct SomeStructure {
var someValue: Int
}
let s = SomeStructure(someValue: 12)
let pathToProperty = \SomeStructure.someValue
let value = s[keyPath: pathToProperty]
// value is 12
The type name can be omitted in contexts where type inference can determine the implied type. The following code uses \.someProperty instead of \SomeClass.someProperty:
class SomeClass: NSObject {
@objc dynamic var someProperty: Int
init(someProperty: Int) {
self.someProperty = someProperty
}
}
let c = SomeClass(someProperty: 10)
c.observe(\.someProperty) { object, change in
// ...
}
The path can refer to self to create the identity key path (\.self). The identity key path refers to a whole instance, so you can use it to access and change all of the data stored in a variable in a single step. For example:
var compoundValue = (a: 1, b: 2)
// Equivalent to compoundValue = (a: 10, b: 20)
compoundValue[keyPath: \.self] = (a: 10, b: 20)
The path can contain multiple property names, separated by periods, to refer to a property of a property’s value. This code uses the key path expression \OuterStructure.outer.someValue to access the someValue property of the OuterStructure type’s outer property:
struct OuterStructure {
var outer: SomeStructure
init(someValue: Int) {
self.outer = SomeStructure(someValue: someValue)
}
}
let nested = OuterStructure(someValue: 24)
let nestedKeyPath = \OuterStructure.outer.someValue
let nestedValue = nested[keyPath: nestedKeyPath]
// nestedValue is 24
The path can include subscripts using brackets, as long as the subscript’s parameter type conforms to the Hashable protocol. This example uses a subscript in a key path to access the second element of an array:
let greetings = ["hello", "hola", "bonjour", "안녕"]
let myGreeting = greetings[keyPath: \[String].[1]]
// myGreeting is 'hola'
The value used in a subscript can be a named value or a literal. Values are captured in key paths using value semantics. The following code uses the variable index in both a key-path expression and in a closure to access the third element of the greetings array. When index is modified, the key-path expression still references the third element, while the closure uses the new index.
var index = 2
let path = \[String].[index]
let fn: ([String]) -> String = { strings in strings[index] }
print(greetings[keyPath: path])
// Prints "bonjour".
print(fn(greetings))
// Prints "bonjour".
// Setting 'index' to a new value doesn't affect 'path'
index += 1
print(greetings[keyPath: path])
// Prints "bonjour".
// Because 'fn' closes over 'index', it uses the new value
print(fn(greetings))
// Prints "안녕".
The path can use optional chaining and forced unwrapping. This code uses optional chaining in a key path to access a property of an optional string:
let firstGreeting: String? = greetings.first
print(firstGreeting?.count as Any)
// Prints "Optional(5)".
// Do the same thing using a key path.
let count = greetings[keyPath: \[String].first?.count]
print(count as Any)
// Prints "Optional(5)".
You can mix and match components of key paths to access values that are deeply nested within a type. The following code accesses different values and properties of a dictionary of arrays by using key-path expressions that combine these components.
let interestingNumbers = ["prime": [2, 3, 5, 7, 11, 13, 17],
"triangular": [1, 3, 6, 10, 15, 21, 28],
"hexagonal": [1, 6, 15, 28, 45, 66, 91]]
print(interestingNumbers[keyPath: \[String: [Int]].["prime"]] as Any)
// Prints "Optional([2, 3, 5, 7, 11, 13, 17])".
print(interestingNumbers[keyPath: \[String: [Int]].["prime"]![0]])
// Prints "2".
print(interestingNumbers[keyPath: \[String: [Int]].["hexagonal"]!.count])
// Prints "7".
print(interestingNumbers[keyPath: \[String: [Int]].["hexagonal"]!.count.bitWidth])
// Prints "64".
You can use a key path expression in contexts where you would normally provide a function or closure. Specifically, you can use a key path expression whose root type is SomeType and whose path produces a value of type Value, instead of a function or closure of type (SomeType) -> Value.
struct Task {
var description: String
var completed: Bool
}
var toDoList = [
Task(description: "Practice ping-pong.", completed: false),
Task(description: "Buy a pirate costume.", completed: true),
Task(description: "Visit Boston in the Fall.", completed: false),
]
// Both approaches below are equivalent.
let descriptions = toDoList.filter(\.completed).map(\.description)
let descriptions2 = toDoList.filter { $0.completed }.map { $0.description }
Any side effects of a key path expression are evaluated only at the point where the expression is evaluated. For example, if you make a function call inside a subscript in a key path expression, the function is called only once as part of evaluating the expression, not every time the key path is used.
func makeIndex() -> Int {
print("Made an index")
return 0
}
// The line below calls makeIndex().
let taskKeyPath = \[Task][makeIndex()]
// Prints "Made an index".
// Using taskKeyPath doesn't call makeIndex() again.
let someTask = toDoList[keyPath: taskKeyPath]
For more information about using key paths in code that interacts with Objective-C APIs, see Using Objective-C Runtime Features in Swift. For information about key-value coding and key-value observing, see Key-Value Coding Programming Guide and Key-Value Observing Programming Guide.
Note
key-path-expression → \ type? . key-path-components
key-path-components → key-path-component | key-path-component . key-path-components
key-path-component → identifier key-path-postfixes? | key-path-postfixes
key-path-postfixes → key-path-postfix key-path-postfixes?
key-path-postfix → ? | ! | self | [ function-call-argument-list ]
Selector Expression¶
A selector expression lets you access the selector used to refer to a method or to a property’s getter or setter in Objective-C. It has the following form:
#selector(<#method name#>)
#selector(getter: <#property name#>)
#selector(setter: <#property name#>)
The method name and property name must be a reference to a method or a property that’s available in the Objective-C runtime. The value of a selector expression is an instance of the Selector type. For example:
class SomeClass: NSObject {
@objc let property: String
@objc(doSomethingWithInt:)
func doSomething(_ x: Int) { }
init(property: String) {
self.property = property
}
}
let selectorForMethod = #selector(SomeClass.doSomething(_:))
let selectorForPropertyGetter = #selector(getter: SomeClass.property)
When creating a selector for a property’s getter, the property name can be a reference to a variable or constant property. In contrast, when creating a selector for a property’s setter, the property name must be a reference to a variable property only.
The method name can contain parentheses for grouping, as well the as operator to disambiguate between methods that share a name but have different type signatures. For example:
extension SomeClass {
@objc(doSomethingWithString:)
func doSomething(_ x: String) { }
}
let anotherSelector = #selector(SomeClass.doSomething(_:) as (SomeClass) -> (String) -> Void)
Because a selector is created at compile time, not at runtime, the compiler can check that a method or property exists and that they’re exposed to the Objective-C runtime.
Note
Although the method name and the property name are expressions, they’re never evaluated.
For more information about using selectors in Swift code that interacts with Objective-C APIs, see Using Objective-C Runtime Features in Swift.
Note
selector-expression → #selector ( expression )
selector-expression → #selector ( getter: expression )
selector-expression → #selector ( setter: expression )
Key-Path String Expression¶
A key-path string expression lets you access the string used to refer to a property in Objective-C, for use in key-value coding and key-value observing APIs. It has the following form:
#keyPath(<#property name#>)
The property name must be a reference to a property that’s available in the Objective-C runtime. At compile time, the key-path string expression is replaced by a string literal. For example:
class SomeClass: NSObject {
@objc var someProperty: Int
init(someProperty: Int) {
self.someProperty = someProperty
}
}
let c = SomeClass(someProperty: 12)
let keyPath = #keyPath(SomeClass.someProperty)
if let value = c.value(forKey: keyPath) {
print(value)
}
// Prints "12".
When you use a key-path string expression within a class, you can refer to a property of that class by writing just the property name, without the class name.
extension SomeClass {
func getSomeKeyPath() -> String {
return #keyPath(someProperty)
}
}
print(keyPath == c.getSomeKeyPath())
// Prints "true".
Because the key path string is created at compile time, not at runtime, the compiler can check that the property exists and that the property is exposed to the Objective-C runtime.
For more information about using key paths in Swift code that interacts with Objective-C APIs, see Using Objective-C Runtime Features in Swift. For information about key-value coding and key-value observing, see Key-Value Coding Programming Guide and Key-Value Observing Programming Guide.
Note
Although the property name is an expression, it’s never evaluated.
Note
key-path-string-expression → #keyPath ( expression )