You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
A protocol is defined by the `protocol` keyword followed by the name, which conventionally starts with a capital letter. Protocol names usually describe a capability or a role.
7
8
8
-
foo
9
+
```swift
10
+
protocolVehicle {
11
+
12
+
var name: String { getset }
13
+
var mph: Double { get }
14
+
15
+
funcdrive()
16
+
funcestimatedTime(for_: Double) ->Double
17
+
}
18
+
```
19
+
20
+
See a `Car` structs conforming to the `Vehicle` protocol::
9
21
10
22
```swift
11
-
let x =5
12
-
let y =5
13
-
var z =0
23
+
structCar: Vehicle {
24
+
25
+
var name: String
26
+
var mph: Double
27
+
28
+
funcdrive() {
29
+
print("Driving the \(name) at \(mph) mph.")
30
+
}
31
+
32
+
funcestimatedTime(fordistance: Double) ->Double {
33
+
distance / mph
34
+
}
35
+
}
14
36
15
-
z = x + y // addition
16
-
print(z) // => 10
37
+
```
17
38
18
-
z = x - y // subtraction
19
-
print(z) // => 0
39
+
Just like structs, enums can conform to protocols too. This means you require all instances of an enum to provide certain properties and methods as defined by the protocol. The implementation is a bit different of course:
20
40
21
-
z = x * y // multiplication
22
-
print(z) // => 25
41
+
```swift
23
42
24
-
z = x / y // division
25
-
print(z) // => 1
43
+
enumAirplane: Vehicle {
44
+
caseboeing(name: String, mph: Double)
45
+
caseairbus(name: String, mph: Double)
46
+
47
+
var name: String {
48
+
get {
49
+
switchself {
50
+
case .boeing(let name, _), .airbus(let name, _):
51
+
return name
52
+
}
53
+
}
54
+
set {
55
+
switchself {
56
+
case .boeing(_, let mph):
57
+
self= .boeing(name: newValue, mph: mph)
58
+
case .airbus(_, let mph):
59
+
self= .airbus(name: newValue, mph: mph)
60
+
}
61
+
}
62
+
}
63
+
64
+
var mph: Double {
65
+
switchself {
66
+
case .boeing(_, let mph), .airbus(_, let mph):
67
+
return mph
68
+
}
69
+
}
70
+
71
+
privatevar type: String {
72
+
switchself {
73
+
case .airbus(_, _):"Airbus"
74
+
case .boeing(_, _):"Boeing"
75
+
}
76
+
}
77
+
78
+
funcdrive() {
79
+
print("Flying the \(type)\(name) at \(mph) mph.")
80
+
}
81
+
82
+
funcestimatedTime(fordistance: Double) ->Double {
83
+
distance / mph
84
+
}
85
+
}
86
+
```
26
87
27
88
28
-
var x =10
89
+
When instances are created from struct `Car` and `Airplane` which both conform to `Vehicle` protocol, you can use the protocol as a type, by explicitly marking the `currentVehicle` as `Vehicle`:
29
90
30
-
x +=2// addition
31
-
print(x) // => 12
91
+
```swift
92
+
var currentVehicle: Vehicle =Car(name: "Tesla", mph: 150)
By conforming to the same protocol, different types can be used interchangeably. In the snippet below, `vehicles` is a constant which holds an array of various types adapting `Vehicle`. Hence, these types can be stored together and processed in a uniform way:
102
+
103
+
```swift
104
+
let vehicles: [Vehicle] = [
105
+
// ...
106
+
]
35
107
36
-
x *=2// multiplication
37
-
print(x) // => 20
108
+
let distanceToReach: Double=6000
38
109
39
-
x /=2// division
40
-
print(x) // => 10
110
+
for vehicle in vehicles {
111
+
let time = vehicle.estimatedTime(for: distanceToReach)
112
+
print("Using a \(vehicle.name) it takes \(time) hours to travel \(distanceToReach) miles.")
113
+
}
41
114
```
42
115
43
-
bar
116
+
Thanks to protocols as types, you can create an array that holds different kinds of vehicles. We can also use a protocol type as function parameters:
117
+
118
+
```swift
119
+
funcgoToTheCheckpoint(usingvehicle: Vehicle) {
120
+
vehicle.drive()
121
+
}
44
122
45
-
```sh
46
-
swift main.swift
123
+
let car =Car(name: "Tesla", mph: 150)
124
+
goToTheCheckpoint(using: car)
125
+
126
+
let airplane = Airplane.boeing(name: "777-9", mph: 500)
127
+
goToTheCheckpoint(using: airplane)
128
+
```
129
+
130
+
Just like protocols can require properties or functions, they can also require initializers, asking conforming types to provide a specific way of initialization. To require an initializer, declare it like this:
131
+
132
+
```swift
133
+
protocolIdentifiable {
134
+
var id: String { get }
135
+
136
+
init(id: String)
137
+
}
138
+
139
+
structUser: Identifiable {
140
+
var id: String
141
+
142
+
init(id: String) {
143
+
self.id= id
144
+
}
145
+
}
146
+
147
+
let user =User(id: "12345")
148
+
```
149
+
150
+
What if you want to allow that initialization can fail? For example, maybe you only want to create an instance of `Adult` if it's `age` is above `18`. You can declare failable initializers with `init?` when initialization might fail:
151
+
152
+
```swift
153
+
protocolAgeRestricted {
154
+
init?(age: Int)
155
+
}
156
+
157
+
structAdult: AgeRestricted {
158
+
var age: Int
159
+
160
+
init?(age: Int) {
161
+
guard age >=18else { returnnil }
162
+
self.age= age
163
+
}
164
+
}
165
+
166
+
let adult =Adult(age: 20)
167
+
let minor =Adult(age: 16)
47
168
```
48
169
49
-
baz
170
+
If you pass something under 18, initialization fails and `nil` is returned.
Copy file name to clipboardExpand all lines: contents/[32]generics/index.md
+53-25Lines changed: 53 additions & 25 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -4,46 +4,74 @@ description: ""
4
4
order: 32
5
5
---
6
6
7
+
Generics functions are here to help us reuse code, avoid redundancy and even do it in a type-safe way. Instead of writing multiple functions, we can use a "generic placeholder" type, in this case, we call that `T` and we write it right after the function name inside `<>`.
7
8
8
-
foo
9
+
The main point is that these placeholders can stand for any type, based on how you use the function. The placeholder will be resolved—with the actual type—when you call the function:
9
10
10
11
```swift
11
-
let x =5
12
-
let y =5
13
-
var z =0
12
+
funcreturnFirst<T>(_array: [T]) -> T {
13
+
array[0]
14
+
}
14
15
15
-
z = x + y // addition
16
-
print(z) // => 10
16
+
let strings: [String] = ["apple", "banana", "cherry"]
17
+
let firstString =returnFirst(strings)
18
+
print(firstString)
17
19
18
-
z = x - y // subtraction
19
-
print(z) // => 0
20
+
let numbers: [Int] = [20, 10, 1]
21
+
let firstInt =returnFirst(numbers)
22
+
print(firstInt)
23
+
```
20
24
21
-
z = x * y // multiplication
22
-
print(z) // => 25
25
+
You can also apply constraints to generics functions. The function `duplicate` takes an array of generic type, but with one condition: it needs to conform to the `Numeric` protocol.
23
26
24
-
z = x / y // division
25
-
print(z) // => 1
27
+
```swift
28
+
funcduplicate<T: Numeric>(_array: [T]) -> [T] {
29
+
array.map { $0*2 }
30
+
}
26
31
32
+
let integers: [Int] = [4, 2, 6, 9]
33
+
print(duplicate(integers))
27
34
28
-
var x =10
35
+
let doubles: [Double] = [3.14, 4.20, 6.9]
36
+
print(duplicate(doubles))
29
37
30
-
x +=2// addition
31
-
print(x) // => 12
38
+
let strings: [String] = ["a", "b", "c"]
39
+
print(duplicate(strings)) // compiler error
40
+
```
32
41
33
-
x -=2// subtraction
34
-
print(x) // => 10
42
+
There's another way to do this, which you've seen before: using the where clause. You can also apply constraints by using the `where` keyword after the function:
35
43
36
-
x *=2// multiplication
37
-
print(x) // => 20
44
+
```swift
45
+
funcduplicate<T: Numeric>(_array: [T]) -> [T] {
46
+
array.map { $0*2 }
47
+
}
38
48
39
-
x /=2// division
40
-
print(x) // => 10
49
+
funcduplicate<T>(_array: [T]) -> [T] where T:Numeric {
50
+
array.map { $0*2 }
51
+
}
41
52
```
42
53
43
-
bar
54
+
You can declare as many different generic parameters as you need, just list them each in separate angle brackets (`<>`), separated by commas `(,)`. Angle brackets tell the compiler to expect a placeholder type, not a concrete one.
44
55
45
-
```sh
46
-
swift main.swift
56
+
```swift
57
+
funcmapValues<T, U>(
58
+
_array: [T],
59
+
transform: (T) -> U
60
+
) -> [U] {
61
+
var newArray: [U] = []
62
+
for item in array {
63
+
let newItem =transform(item)
64
+
newArray.append(newItem)
65
+
}
66
+
return newArray
67
+
}
68
+
69
+
let numbers: [Int] = [4, 2, 6, 9]
70
+
let strings: [String] =mapValues(numbers) { number in
71
+
String(number) +" items"
72
+
}
73
+
print(strings)
47
74
```
48
75
49
-
baz
76
+
You can go further and add constraints to multiple generic parameters too, even having specified constraints for each parameter.
0 commit comments