Initial commit
This commit is contained in:
693
skills/programming-swift/LanguageGuide/Extensions.md
Normal file
693
skills/programming-swift/LanguageGuide/Extensions.md
Normal file
@@ -0,0 +1,693 @@
|
||||
# Extensions
|
||||
|
||||
Add functionality to an existing type.
|
||||
|
||||
*Extensions* add new functionality to an existing
|
||||
class, structure, enumeration, or protocol type.
|
||||
This includes the ability to extend types
|
||||
for which you don't have access to the original source code
|
||||
(known as *retroactive modeling*).
|
||||
Extensions are similar to categories in Objective-C.
|
||||
(Unlike Objective-C categories, Swift extensions don't have names.)
|
||||
|
||||
Extensions in Swift can:
|
||||
|
||||
- Add computed instance properties and computed type properties
|
||||
- Define instance methods and type methods
|
||||
- Provide new initializers
|
||||
- Define subscripts
|
||||
- Define and use new nested types
|
||||
- Make an existing type conform to a protocol
|
||||
|
||||
In Swift,
|
||||
you can even extend a protocol to provide implementations of its requirements
|
||||
or add additional functionality that conforming types can take advantage of.
|
||||
For more details, see <doc:Protocols#Protocol-Extensions>.
|
||||
|
||||
> Note: Extensions can add new functionality to a type,
|
||||
> but they can't override existing functionality.
|
||||
|
||||
<!--
|
||||
- test: `extensionsCannotOverrideExistingBehavior`
|
||||
|
||||
```swifttest
|
||||
-> class C {
|
||||
var x = 0
|
||||
func foo() {}
|
||||
}
|
||||
-> extension C {
|
||||
override var x: Int {
|
||||
didSet {
|
||||
print("new x is \(x)")
|
||||
}
|
||||
}
|
||||
override func foo() {
|
||||
print("called overridden foo")
|
||||
}
|
||||
}
|
||||
!$ error: property does not override any property from its superclass
|
||||
!! override var x: Int {
|
||||
!! ~~~~~~~~ ^
|
||||
!$ error: ambiguous use of 'x'
|
||||
!! print("new x is \(x)")
|
||||
!! ^
|
||||
!$ note: found this candidate
|
||||
!! var x = 0
|
||||
!! ^
|
||||
!$ note: found this candidate
|
||||
!! override var x: Int {
|
||||
!! ^
|
||||
!$ error: invalid redeclaration of 'x'
|
||||
!! override var x: Int {
|
||||
!! ^
|
||||
!$ note: 'x' previously declared here
|
||||
!! var x = 0
|
||||
!! ^
|
||||
!$ error: method does not override any method from its superclass
|
||||
!! override func foo() {
|
||||
!! ~~~~~~~~ ^
|
||||
!$ error: invalid redeclaration of 'foo()'
|
||||
!! override func foo() {
|
||||
!! ^
|
||||
!$ note: 'foo()' previously declared here
|
||||
!! func foo() {}
|
||||
!! ^
|
||||
```
|
||||
-->
|
||||
|
||||
## Extension Syntax
|
||||
|
||||
Declare extensions with the `extension` keyword:
|
||||
|
||||
```swift
|
||||
extension SomeType {
|
||||
// new functionality to add to SomeType goes here
|
||||
}
|
||||
```
|
||||
|
||||
<!--
|
||||
- test: `extensionSyntax`
|
||||
|
||||
```swifttest
|
||||
>> struct SomeType {}
|
||||
-> extension SomeType {
|
||||
// new functionality to add to SomeType goes here
|
||||
}
|
||||
```
|
||||
-->
|
||||
|
||||
An extension can extend an existing type to make it adopt one or more protocols.
|
||||
To add protocol conformance,
|
||||
you write the protocol names
|
||||
the same way as you write them for a class or structure:
|
||||
|
||||
```swift
|
||||
extension SomeType: SomeProtocol, AnotherProtocol {
|
||||
// implementation of protocol requirements goes here
|
||||
}
|
||||
```
|
||||
|
||||
<!--
|
||||
- test: `extensionSyntax`
|
||||
|
||||
```swifttest
|
||||
>> protocol SomeProtocol {}
|
||||
>> protocol AnotherProtocol {}
|
||||
-> extension SomeType: SomeProtocol, AnotherProtocol {
|
||||
// implementation of protocol requirements goes here
|
||||
}
|
||||
```
|
||||
-->
|
||||
|
||||
Adding protocol conformance in this way is described in
|
||||
<doc:Protocols#Adding-Protocol-Conformance-with-an-Extension>.
|
||||
|
||||
An extension can be used to extend an existing generic type,
|
||||
as described in <doc:Generics#Extending-a-Generic-Type>.
|
||||
You can also extend a generic type to conditionally add functionality,
|
||||
as described in <doc:Generics#Extensions-with-a-Generic-Where-Clause>.
|
||||
|
||||
> Note: If you define an extension to add new functionality to an existing type,
|
||||
> the new functionality will be available on all existing instances of that type,
|
||||
> even if they were created before the extension was defined.
|
||||
|
||||
## Computed Properties
|
||||
|
||||
Extensions can add computed instance properties and computed type properties to existing types.
|
||||
This example adds five computed instance properties to Swift's built-in `Double` type,
|
||||
to provide basic support for working with distance units:
|
||||
|
||||
```swift
|
||||
extension Double {
|
||||
var km: Double { return self * 1_000.0 }
|
||||
var m: Double { return self }
|
||||
var cm: Double { return self / 100.0 }
|
||||
var mm: Double { return self / 1_000.0 }
|
||||
var ft: Double { return self / 3.28084 }
|
||||
}
|
||||
let oneInch = 25.4.mm
|
||||
print("One inch is \(oneInch) meters")
|
||||
// Prints "One inch is 0.0254 meters".
|
||||
let threeFeet = 3.ft
|
||||
print("Three feet is \(threeFeet) meters")
|
||||
// Prints "Three feet is 0.914399970739201 meters".
|
||||
```
|
||||
|
||||
<!--
|
||||
- test: `extensionsComputedProperties`
|
||||
|
||||
```swifttest
|
||||
-> extension Double {
|
||||
var km: Double { return self * 1_000.0 }
|
||||
var m: Double { return self }
|
||||
var cm: Double { return self / 100.0 }
|
||||
var mm: Double { return self / 1_000.0 }
|
||||
var ft: Double { return self / 3.28084 }
|
||||
}
|
||||
-> let oneInch = 25.4.mm
|
||||
-> print("One inch is \(oneInch) meters")
|
||||
<- One inch is 0.0254 meters
|
||||
-> let threeFeet = 3.ft
|
||||
-> print("Three feet is \(threeFeet) meters")
|
||||
<- Three feet is 0.914399970739201 meters
|
||||
```
|
||||
-->
|
||||
|
||||
These computed properties express that a `Double` value
|
||||
should be considered as a certain unit of length.
|
||||
Although they're implemented as computed properties,
|
||||
the names of these properties can be appended to
|
||||
a floating-point literal value with dot syntax,
|
||||
as a way to use that literal value to perform distance conversions.
|
||||
|
||||
In this example, a `Double` value of `1.0` is considered to represent “one meter”.
|
||||
This is why the `m` computed property returns `self` ---
|
||||
the expression `1.m` is considered to calculate a `Double` value of `1.0`.
|
||||
|
||||
Other units require some conversion to be expressed as a value measured in meters.
|
||||
One kilometer is the same as 1,000 meters,
|
||||
so the `km` computed property multiplies the value by `1_000.00`
|
||||
to convert into a number expressed in meters.
|
||||
Similarly, there are 3.28084 feet in a meter,
|
||||
and so the `ft` computed property divides the underlying `Double` value
|
||||
by `3.28084`, to convert it from feet to meters.
|
||||
|
||||
These properties are read-only computed properties,
|
||||
and so they're expressed without the `get` keyword, for brevity.
|
||||
Their return value is of type `Double`,
|
||||
and can be used within mathematical calculations wherever a `Double` is accepted:
|
||||
|
||||
```swift
|
||||
let aMarathon = 42.km + 195.m
|
||||
print("A marathon is \(aMarathon) meters long")
|
||||
// Prints "A marathon is 42195.0 meters long".
|
||||
```
|
||||
|
||||
<!--
|
||||
- test: `extensionsComputedProperties`
|
||||
|
||||
```swifttest
|
||||
-> let aMarathon = 42.km + 195.m
|
||||
-> print("A marathon is \(aMarathon) meters long")
|
||||
<- A marathon is 42195.0 meters long
|
||||
```
|
||||
-->
|
||||
|
||||
> Note: Extensions can add new computed properties, but they can't add stored properties,
|
||||
> or add property observers to existing properties.
|
||||
|
||||
<!--
|
||||
- test: `extensionsCannotAddStoredProperties`
|
||||
|
||||
```swifttest
|
||||
-> class C {}
|
||||
-> extension C { var x = 0 }
|
||||
!$ error: extensions must not contain stored properties
|
||||
!! extension C { var x = 0 }
|
||||
!! ^
|
||||
```
|
||||
-->
|
||||
|
||||
<!--
|
||||
TODO: change this example to something more advisable / less contentious.
|
||||
-->
|
||||
|
||||
## Initializers
|
||||
|
||||
Extensions can add new initializers to existing types.
|
||||
This enables you to extend other types to accept
|
||||
your own custom types as initializer parameters,
|
||||
or to provide additional initialization options
|
||||
that were not included as part of the type's original implementation.
|
||||
|
||||
Extensions can add new convenience initializers to a class,
|
||||
but they can't add new designated initializers or deinitializers to a class.
|
||||
Designated initializers and deinitializers
|
||||
must always be provided by the original class implementation.
|
||||
|
||||
If you use an extension to add an initializer to a value type that provides
|
||||
default values for all of its stored properties
|
||||
and doesn't define any custom initializers,
|
||||
you can call the default initializer and memberwise initializer for that value type
|
||||
from within your extension's initializer.
|
||||
This wouldn't be the case if you had written the initializer
|
||||
as part of the value type's original implementation,
|
||||
as described in <doc:Initialization#Initializer-Delegation-for-Value-Types>.
|
||||
|
||||
If you use an extension to add an initializer to a structure
|
||||
that was declared in another module,
|
||||
the new initializer can't access `self` until it calls
|
||||
an initializer from the defining module.
|
||||
|
||||
The example below defines a custom `Rect` structure to represent a geometric rectangle.
|
||||
The example also defines two supporting structures called `Size` and `Point`,
|
||||
both of which provide default values of `0.0` for all of their properties:
|
||||
|
||||
```swift
|
||||
struct Size {
|
||||
var width = 0.0, height = 0.0
|
||||
}
|
||||
struct Point {
|
||||
var x = 0.0, y = 0.0
|
||||
}
|
||||
struct Rect {
|
||||
var origin = Point()
|
||||
var size = Size()
|
||||
}
|
||||
```
|
||||
|
||||
<!--
|
||||
- test: `extensionsInitializers`
|
||||
|
||||
```swifttest
|
||||
-> struct Size {
|
||||
var width = 0.0, height = 0.0
|
||||
}
|
||||
-> struct Point {
|
||||
var x = 0.0, y = 0.0
|
||||
}
|
||||
-> struct Rect {
|
||||
var origin = Point()
|
||||
var size = Size()
|
||||
}
|
||||
```
|
||||
-->
|
||||
|
||||
Because the `Rect` structure provides default values for all of its properties,
|
||||
it receives a default initializer and a memberwise initializer automatically,
|
||||
as described in <doc:Initialization#Default-Initializers>.
|
||||
These initializers can be used to create new `Rect` instances:
|
||||
|
||||
```swift
|
||||
let defaultRect = Rect()
|
||||
let memberwiseRect = Rect(origin: Point(x: 2.0, y: 2.0),
|
||||
size: Size(width: 5.0, height: 5.0))
|
||||
```
|
||||
|
||||
<!--
|
||||
- test: `extensionsInitializers`
|
||||
|
||||
```swifttest
|
||||
-> let defaultRect = Rect()
|
||||
-> let memberwiseRect = Rect(origin: Point(x: 2.0, y: 2.0),
|
||||
size: Size(width: 5.0, height: 5.0))
|
||||
```
|
||||
-->
|
||||
|
||||
You can extend the `Rect` structure to provide an additional initializer
|
||||
that takes a specific center point and size:
|
||||
|
||||
```swift
|
||||
extension Rect {
|
||||
init(center: Point, size: Size) {
|
||||
let originX = center.x - (size.width / 2)
|
||||
let originY = center.y - (size.height / 2)
|
||||
self.init(origin: Point(x: originX, y: originY), size: size)
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
<!--
|
||||
- test: `extensionsInitializers`
|
||||
|
||||
```swifttest
|
||||
-> extension Rect {
|
||||
init(center: Point, size: Size) {
|
||||
let originX = center.x - (size.width / 2)
|
||||
let originY = center.y - (size.height / 2)
|
||||
self.init(origin: Point(x: originX, y: originY), size: size)
|
||||
}
|
||||
}
|
||||
```
|
||||
-->
|
||||
|
||||
This new initializer starts by calculating an appropriate origin point based on
|
||||
the provided `center` point and `size` value.
|
||||
The initializer then calls the structure's automatic memberwise initializer
|
||||
`init(origin:size:)`, which stores the new origin and size values
|
||||
in the appropriate properties:
|
||||
|
||||
```swift
|
||||
let centerRect = Rect(center: Point(x: 4.0, y: 4.0),
|
||||
size: Size(width: 3.0, height: 3.0))
|
||||
// centerRect's origin is (2.5, 2.5) and its size is (3.0, 3.0)
|
||||
```
|
||||
|
||||
<!--
|
||||
- test: `extensionsInitializers`
|
||||
|
||||
```swifttest
|
||||
-> let centerRect = Rect(center: Point(x: 4.0, y: 4.0),
|
||||
size: Size(width: 3.0, height: 3.0))
|
||||
/> centerRect's origin is (\(centerRect.origin.x), \(centerRect.origin.y)) and its size is (\(centerRect.size.width), \(centerRect.size.height))
|
||||
</ centerRect's origin is (2.5, 2.5) and its size is (3.0, 3.0)
|
||||
```
|
||||
-->
|
||||
|
||||
> Note: If you provide a new initializer with an extension,
|
||||
> you are still responsible for making sure that each instance is fully initialized
|
||||
> once the initializer completes.
|
||||
|
||||
## Methods
|
||||
|
||||
Extensions can add new instance methods and type methods to existing types.
|
||||
The following example adds a new instance method called `repetitions` to the `Int` type:
|
||||
|
||||
```swift
|
||||
extension Int {
|
||||
func repetitions(task: () -> Void) {
|
||||
for _ in 0..<self {
|
||||
task()
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
<!--
|
||||
- test: `extensionsInstanceMethods`
|
||||
|
||||
```swifttest
|
||||
-> extension Int {
|
||||
func repetitions(task: () -> Void) {
|
||||
for _ in 0..<self {
|
||||
task()
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
-->
|
||||
|
||||
The `repetitions(task:)` method takes a single argument of type `() -> Void`,
|
||||
which indicates a function that has no parameters and doesn't return a value.
|
||||
|
||||
After defining this extension,
|
||||
you can call the `repetitions(task:)` method on any integer
|
||||
to perform a task that many number of times:
|
||||
|
||||
```swift
|
||||
3.repetitions {
|
||||
print("Hello!")
|
||||
}
|
||||
// Hello!
|
||||
// Hello!
|
||||
// Hello!
|
||||
```
|
||||
|
||||
<!--
|
||||
- test: `extensionsInstanceMethods`
|
||||
|
||||
```swifttest
|
||||
-> 3.repetitions {
|
||||
print("Hello!")
|
||||
}
|
||||
</ Hello!
|
||||
</ Hello!
|
||||
</ Hello!
|
||||
```
|
||||
-->
|
||||
|
||||
### Mutating Instance Methods
|
||||
|
||||
Instance methods added with an extension can also modify (or *mutate*) the instance itself.
|
||||
Structure and enumeration methods that modify `self` or its properties
|
||||
must mark the instance method as `mutating`,
|
||||
just like mutating methods from an original implementation.
|
||||
|
||||
The example below adds a new mutating method called `square` to Swift's `Int` type,
|
||||
which squares the original value:
|
||||
|
||||
```swift
|
||||
extension Int {
|
||||
mutating func square() {
|
||||
self = self * self
|
||||
}
|
||||
}
|
||||
var someInt = 3
|
||||
someInt.square()
|
||||
// someInt is now 9
|
||||
```
|
||||
|
||||
<!--
|
||||
- test: `extensionsInstanceMethods`
|
||||
|
||||
```swifttest
|
||||
-> extension Int {
|
||||
mutating func square() {
|
||||
self = self * self
|
||||
}
|
||||
}
|
||||
-> var someInt = 3
|
||||
-> someInt.square()
|
||||
/> someInt is now \(someInt)
|
||||
</ someInt is now 9
|
||||
```
|
||||
-->
|
||||
|
||||
## Subscripts
|
||||
|
||||
Extensions can add new subscripts to an existing type.
|
||||
This example adds an integer subscript to Swift's built-in `Int` type.
|
||||
This subscript `[n]` returns the decimal digit `n` places in
|
||||
from the right of the number:
|
||||
|
||||
- `123456789[0]` returns `9`
|
||||
- `123456789[1]` returns `8`
|
||||
|
||||
…and so on:
|
||||
|
||||
```swift
|
||||
extension Int {
|
||||
subscript(digitIndex: Int) -> Int {
|
||||
var decimalBase = 1
|
||||
for _ in 0..<digitIndex {
|
||||
decimalBase *= 10
|
||||
}
|
||||
return (self / decimalBase) % 10
|
||||
}
|
||||
}
|
||||
746381295[0]
|
||||
// returns 5
|
||||
746381295[1]
|
||||
// returns 9
|
||||
746381295[2]
|
||||
// returns 2
|
||||
746381295[8]
|
||||
// returns 7
|
||||
```
|
||||
|
||||
<!--
|
||||
- test: `extensionsSubscripts`
|
||||
|
||||
```swifttest
|
||||
-> extension Int {
|
||||
subscript(digitIndex: Int) -> Int {
|
||||
var decimalBase = 1
|
||||
for _ in 0..<digitIndex {
|
||||
decimalBase *= 10
|
||||
}
|
||||
return (self / decimalBase) % 10
|
||||
}
|
||||
}
|
||||
>> let r0 =
|
||||
-> 746381295[0]
|
||||
/> returns \(r0)
|
||||
</ returns 5
|
||||
>> let r1 =
|
||||
-> 746381295[1]
|
||||
/> returns \(r1)
|
||||
</ returns 9
|
||||
>> let r2 =
|
||||
-> 746381295[2]
|
||||
/> returns \(r2)
|
||||
</ returns 2
|
||||
>> let r3 =
|
||||
-> 746381295[8]
|
||||
/> returns \(r3)
|
||||
</ returns 7
|
||||
```
|
||||
-->
|
||||
|
||||
<!--
|
||||
Rewrite the above to avoid bare expressions.
|
||||
Tracking bug is <rdar://problem/35301593>
|
||||
-->
|
||||
|
||||
<!--
|
||||
TODO: Replace the for loop above with an exponent,
|
||||
if/when integer exponents land in the stdlib.
|
||||
Darwin's pow() function is only for floating point.
|
||||
-->
|
||||
|
||||
If the `Int` value doesn't have enough digits for the requested index,
|
||||
the subscript implementation returns `0`,
|
||||
as if the number had been padded with zeros to the left:
|
||||
|
||||
```swift
|
||||
746381295[9]
|
||||
// returns 0, as if you had requested:
|
||||
0746381295[9]
|
||||
```
|
||||
|
||||
<!--
|
||||
- test: `extensionsSubscripts`
|
||||
|
||||
```swifttest
|
||||
>> let r4 =
|
||||
-> 746381295[9]
|
||||
/> returns \(r4), as if you had requested:
|
||||
</ returns 0, as if you had requested:
|
||||
>> let r5 =
|
||||
-> 0746381295[9]
|
||||
```
|
||||
-->
|
||||
|
||||
<!--
|
||||
TODO: provide an explanation of this example
|
||||
-->
|
||||
|
||||
<!--
|
||||
Rewrite the above to avoid bare expressions.
|
||||
Tracking bug is <rdar://problem/35301593>
|
||||
-->
|
||||
|
||||
## Nested Types
|
||||
|
||||
Extensions can add new nested types to existing classes, structures, and enumerations:
|
||||
|
||||
```swift
|
||||
extension Int {
|
||||
enum Kind {
|
||||
case negative, zero, positive
|
||||
}
|
||||
var kind: Kind {
|
||||
switch self {
|
||||
case 0:
|
||||
return .zero
|
||||
case let x where x > 0:
|
||||
return .positive
|
||||
default:
|
||||
return .negative
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
<!--
|
||||
- test: `extensionsNestedTypes`
|
||||
|
||||
```swifttest
|
||||
-> extension Int {
|
||||
enum Kind {
|
||||
case negative, zero, positive
|
||||
}
|
||||
var kind: Kind {
|
||||
switch self {
|
||||
case 0:
|
||||
return .zero
|
||||
case let x where x > 0:
|
||||
return .positive
|
||||
default:
|
||||
return .negative
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
-->
|
||||
|
||||
This example adds a new nested enumeration to `Int`.
|
||||
This enumeration, called `Kind`,
|
||||
expresses the kind of number that a particular integer represents.
|
||||
Specifically, it expresses whether the number is
|
||||
negative, zero, or positive.
|
||||
|
||||
This example also adds a new computed instance property to `Int`,
|
||||
called `kind`,
|
||||
which returns the appropriate `Kind` enumeration case for that integer.
|
||||
|
||||
The nested enumeration can now be used with any `Int` value:
|
||||
|
||||
```swift
|
||||
func printIntegerKinds(_ numbers: [Int]) {
|
||||
for number in numbers {
|
||||
switch number.kind {
|
||||
case .negative:
|
||||
print("- ", terminator: "")
|
||||
case .zero:
|
||||
print("0 ", terminator: "")
|
||||
case .positive:
|
||||
print("+ ", terminator: "")
|
||||
}
|
||||
}
|
||||
print("")
|
||||
}
|
||||
printIntegerKinds([3, 19, -27, 0, -6, 0, 7])
|
||||
// Prints "+ + - 0 - 0 + ".
|
||||
```
|
||||
|
||||
<!--
|
||||
- test: `extensionsNestedTypes`
|
||||
|
||||
```swifttest
|
||||
-> func printIntegerKinds(_ numbers: [Int]) {
|
||||
for number in numbers {
|
||||
switch number.kind {
|
||||
case .negative:
|
||||
print("- ", terminator: "")
|
||||
case .zero:
|
||||
print("0 ", terminator: "")
|
||||
case .positive:
|
||||
print("+ ", terminator: "")
|
||||
}
|
||||
}
|
||||
print("")
|
||||
}
|
||||
-> printIntegerKinds([3, 19, -27, 0, -6, 0, 7])
|
||||
<< + + - 0 - 0 +
|
||||
// Prints "+ + - 0 - 0 + ".
|
||||
```
|
||||
-->
|
||||
|
||||
<!--
|
||||
Workaround for rdar://26016325
|
||||
-->
|
||||
|
||||
This function, `printIntegerKinds(_:)`,
|
||||
takes an input array of `Int` values and iterates over those values in turn.
|
||||
For each integer in the array,
|
||||
the function considers the `kind` computed property for that integer,
|
||||
and prints an appropriate description.
|
||||
|
||||
> Note: `number.kind` is already known to be of type `Int.Kind`.
|
||||
> Because of this, all of the `Int.Kind` case values
|
||||
> can be written in shorthand form inside the `switch` statement,
|
||||
> such as `.negative` rather than `Int.Kind.negative`.
|
||||
|
||||
<!--
|
||||
This source file is part of the Swift.org open source project
|
||||
|
||||
Copyright (c) 2014 - 2022 Apple Inc. and the Swift project authors
|
||||
Licensed under Apache License v2.0 with Runtime Library Exception
|
||||
|
||||
See https://swift.org/LICENSE.txt for license information
|
||||
See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
|
||||
-->
|
||||
Reference in New Issue
Block a user