Skip to content

Commit b7e95c9

Browse files
committed
Examples 31-35
1 parent 0252c46 commit b7e95c9

File tree

5 files changed

+543
-131
lines changed

5 files changed

+543
-131
lines changed

contents/[31]protocols/index.md

Lines changed: 146 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -4,46 +4,167 @@ description: ""
44
order: 31
55
---
66

7+
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.
78

8-
foo
9+
```swift
10+
protocol Vehicle {
11+
12+
var name: String { get set }
13+
var mph: Double { get }
14+
15+
func drive()
16+
func estimatedTime(for _: Double) -> Double
17+
}
18+
```
19+
20+
See a `Car` structs conforming to the `Vehicle` protocol::
921

1022
```swift
11-
let x = 5
12-
let y = 5
13-
var z = 0
23+
struct Car: Vehicle {
24+
25+
var name: String
26+
var mph: Double
27+
28+
func drive() {
29+
print("Driving the \(name) at \(mph) mph.")
30+
}
31+
32+
func estimatedTime(for distance: Double) -> Double {
33+
distance / mph
34+
}
35+
}
1436

15-
z = x + y // addition
16-
print(z) // => 10
37+
```
1738

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:
2040

21-
z = x * y // multiplication
22-
print(z) // => 25
41+
```swift
2342

24-
z = x / y // division
25-
print(z) // => 1
43+
enum Airplane: Vehicle {
44+
case boeing(name: String, mph: Double)
45+
case airbus(name: String, mph: Double)
46+
47+
var name: String {
48+
get {
49+
switch self {
50+
case .boeing(let name, _), .airbus(let name, _):
51+
return name
52+
}
53+
}
54+
set {
55+
switch self {
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+
switch self {
66+
case .boeing(_, let mph), .airbus(_, let mph):
67+
return mph
68+
}
69+
}
70+
71+
private var type: String {
72+
switch self {
73+
case .airbus(_, _): "Airbus"
74+
case .boeing(_, _): "Boeing"
75+
}
76+
}
77+
78+
func drive() {
79+
print("Flying the \(type) \(name) at \(mph) mph.")
80+
}
81+
82+
func estimatedTime(for distance: Double) -> Double {
83+
distance / mph
84+
}
85+
}
86+
```
2687

2788

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`:
2990

30-
x += 2 // addition
31-
print(x) // => 12
91+
```swift
92+
var currentVehicle: Vehicle = Car(name: "Tesla", mph: 150)
93+
currentVehicle.drive()
94+
print(currentVehicle.estimatedTime(for: 6000))
3295

33-
x -= 2 // subtraction
34-
print(x) // => 10
96+
currentVehicle = Airplane.boeing(name: "777-9", mph: 500)
97+
currentVehicle.drive()
98+
print(currentVehicle.estimatedTime(for: 6000))
99+
```
100+
101+
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+
]
35107

36-
x *= 2 // multiplication
37-
print(x) // => 20
108+
let distanceToReach: Double = 6000
38109

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+
}
41114
```
42115

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+
func goToTheCheckpoint(using vehicle: Vehicle) {
120+
vehicle.drive()
121+
}
44122

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+
protocol Identifiable {
134+
var id: String { get }
135+
136+
init(id: String)
137+
}
138+
139+
struct User: 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+
protocol AgeRestricted {
154+
init?(age: Int)
155+
}
156+
157+
struct Adult: AgeRestricted {
158+
var age: Int
159+
160+
init?(age: Int) {
161+
guard age >= 18 else { return nil }
162+
self.age = age
163+
}
164+
}
165+
166+
let adult = Adult(age: 20)
167+
let minor = Adult(age: 16)
47168
```
48169

49-
baz
170+
If you pass something under 18, initialization fails and `nil` is returned.

contents/[32]generics/index.md

Lines changed: 53 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -4,46 +4,74 @@ description: ""
44
order: 32
55
---
66

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 `<>`.
78

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:
910

1011
```swift
11-
let x = 5
12-
let y = 5
13-
var z = 0
12+
func returnFirst<T>(_ array: [T]) -> T {
13+
array[0]
14+
}
1415

15-
z = x + y // addition
16-
print(z) // => 10
16+
let strings: [String] = ["apple", "banana", "cherry"]
17+
let firstString = returnFirst(strings)
18+
print(firstString)
1719

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+
```
2024

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.
2326

24-
z = x / y // division
25-
print(z) // => 1
27+
```swift
28+
func duplicate<T: Numeric>(_ array: [T]) -> [T] {
29+
array.map { $0 * 2 }
30+
}
2631

32+
let integers: [Int] = [4, 2, 6, 9]
33+
print(duplicate(integers))
2734

28-
var x = 10
35+
let doubles: [Double] = [3.14, 4.20, 6.9]
36+
print(duplicate(doubles))
2937

30-
x += 2 // addition
31-
print(x) // => 12
38+
let strings: [String] = ["a", "b", "c"]
39+
print(duplicate(strings)) // compiler error
40+
```
3241

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:
3543

36-
x *= 2 // multiplication
37-
print(x) // => 20
44+
```swift
45+
func duplicate<T: Numeric>(_ array: [T]) -> [T] {
46+
array.map { $0 * 2 }
47+
}
3848

39-
x /= 2 // division
40-
print(x) // => 10
49+
func duplicate<T>(_ array: [T]) -> [T] where T: Numeric {
50+
array.map { $0 * 2 }
51+
}
4152
```
4253

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.
4455

45-
```sh
46-
swift main.swift
56+
```swift
57+
func mapValues<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)
4774
```
4875

49-
baz
76+
You can go further and add constraints to multiple generic parameters too, even having specified constraints for each parameter.
77+

0 commit comments

Comments
 (0)