forked from jakarmy/swift-summary
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Contents.swift
576 lines (467 loc) · 16.9 KB
/
Contents.swift
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
// |=------------------------------------------------------=|
// Copyright (c) 2016 Juan Antonio Karmy.
// Licensed under MIT License
//
// See https://developer.apple.com/library/ios/documentation/Swift/Conceptual/Swift_Programming_Language/ for Swift Language Reference
//
// See Juan Antonio Karmy - http://karmy.co | http://twitter.com/jkarmy
//
// |=------------------------------------------------------=|
import UIKit
/*
===============
Protocol Syntax
===============
*/
protocol SomeProtocol {
// protocol definition goes here
}
protocol AnotherProtocol {
// protocol definition goes here
}
class SomeSuperclass {
}
//If a class has a superclass, list the superclass name before any protocols it adopts, followed by a comma.
class SomeClass: SomeSuperclass, SomeProtocol, AnotherProtocol {
// class definition goes here
}
/*
=====================
Property Requirements
=====================
*/
/* If a protocol requires a property to be gettable and settable, that property requirement cannot be
fulfilled by a constant stored property or a read-only computed property. If the protocol only requires a property to be gettable,
the requirement can be satisfied by any kind of property, and it is valid for the property to be also settable if this is useful for your own code.
*/
protocol SomeOtherProtocol {
var mustBeSettable: Int { get set }
var doesNotNeedToBeSettable: Int { get }
}
protocol FullyNamed {
var fullName: String { get }
}
// Here’s an example of a simple structure that adopts and conforms to the FullyNamed protocol.
struct Person: FullyNamed {
var fullName: String
}
let john = Person(fullName: "John Appleseed")
class Starship: FullyNamed {
var prefix: String?
var name: String
init(name: String, prefix: String? = nil) {
self.name = name
self.prefix = prefix
}
// Computed property.
var fullName: String {
return (prefix != nil ? prefix! + " " : "") + name
}
}
var ncc1701 = Starship(name: "Enterprise", prefix: "USS")
/*
===================
Method Requirements
===================
*/
protocol RandomNumberGenerator {
func random() -> Double
}
class LinearCongruentialGenerator: RandomNumberGenerator {
var lastRandom = 42.0
let m = 139968.0
let a = 3877.0
let c = 29573.0
func random() -> Double {
lastRandom = ((lastRandom * a + c) % m)
return lastRandom / m
}
}
var generator = LinearCongruentialGenerator()
print("Here's a random number: \(generator.random())")
// prints "Here's a random number: 0.37464991998171"
print("And another one: \(generator.random())")
// prints "And another one: 0.729023776863283"
/*
============================
Mutating Method Requirements
============================
*/
/*
If you define a protocol instance method requirement that is intended to mutate instances
of any type that adopts the protocol, mark the method with the mutating keyword as part
of the protocol’s definition. This enables structures and enumerations to adopt the protocol and satisfy that method requirement.
NOTE
If you mark a protocol instance method requirement as mutating, you do not need to
write the mutating keyword when writing an implementation of that method for a class.
The mutating keyword is only used by structures and enumerations.
*/
protocol Togglable {
mutating func toggle()
}
enum OnOffSwitch: Togglable {
case Off, On
mutating func toggle() {
switch self {
case Off:
self = On
case On:
self = Off
}
}
}
var lightSwitch = OnOffSwitch.Off
lightSwitch.toggle()
// lightSwitch is now equal to .On
/*
========================
Initializer Requirements
========================
*/
protocol SomeNewProtocol {
init(someParameter: Int)
}
/*
You can implement a protocol initializer requirement on a conforming class as either a designated initializer
or a convenience initializer. In both cases, you must mark the initializer implementation with the required modifier:
*/
class SomeNewClass: SomeNewProtocol {
required init(someParameter: Int) {
// initializer implementation goes here
}
}
/*
NOTE
You do not need to mark protocol initializer implementations with the required modifier on classes
that are marked with the final modifier, because final classes cannot be subclassed. For more on the final modifier, see Preventing Overrides.
*/
class SomeSuperClass {
init() {
// initializer implementation goes here
}
}
class SomeSubClass: SomeSuperClass, SomeProtocol {
// "required" from SomeProtocol conformance; "override" from SomeSuperClass
required override init() {
// initializer implementation goes here
}
}
/*
==================
Protocols as Types
==================
*/
/*
Because it is a type, you can use a protocol in many places where other types are allowed, including:
- As a parameter type or return type in a function, method, or initializer
- As the type of a constant, variable, or property
- As the type of items in an array, dictionary, or other container
NOTE
Because protocols are types, begin their names with a capital letter (such as FullyNamed and RandomNumberGenerator) to match the names of other types in Swift (such as Int, String, and Double).
*/
class Dice {
let sides: Int
let generator: RandomNumberGenerator
init(sides: Int, generator: RandomNumberGenerator) {
self.sides = sides
self.generator = generator
}
func roll() -> Int {
return Int(generator.random() * Double(sides)) + 1
}
}
var d6 = Dice(sides: 6, generator: LinearCongruentialGenerator())
for _ in 1...5 {
print("Random dice roll is \(d6.roll())")
}
// Random dice roll is 3
// Random dice roll is 5
// Random dice roll is 4
// Random dice roll is 5
// Random dice roll is 4
/*
==========
Delegation
==========
*/
protocol DiceGame {
var dice: Dice { get }
func play()
}
protocol DiceGameDelegate {
func gameDidStart(game: DiceGame)
func game(game: DiceGame, didStartNewTurnWithDiceRoll diceRoll: Int)
func gameDidEnd(game: DiceGame)
}
class SnakesAndLadders: DiceGame {
let finalSquare = 25
let dice = Dice(sides: 6, generator: LinearCongruentialGenerator())
var square = 0
var board: [Int]
init() {
board = [Int](count: finalSquare + 1, repeatedValue: 0)
board[03] = +08; board[06] = +11; board[09] = +09; board[10] = +02
board[14] = -10; board[19] = -11; board[22] = -02; board[24] = -08
}
var delegate: DiceGameDelegate?
func play() {
square = 0
delegate?.gameDidStart(self)
gameLoop: while square != finalSquare {
let diceRoll = dice.roll()
delegate?.game(self, didStartNewTurnWithDiceRoll: diceRoll)
switch square + diceRoll {
case finalSquare:
break gameLoop
case let newSquare where newSquare > finalSquare:
continue gameLoop
default:
square += diceRoll
square += board[square]
}
}
delegate?.gameDidEnd(self)
}
}
/*
Note that the delegate property is defined as an optional DiceGameDelegate,
because a delegate isn’t required in order to play the game. Because it is of an optional type,
the delegate property is automatically set to an initial value of nil. Thereafter, the game instantiator has the option to set the property to a suitable delegate.
*/
class DiceGameTracker: DiceGameDelegate {
var numberOfTurns = 0
func gameDidStart(game: DiceGame) {
numberOfTurns = 0
if game is SnakesAndLadders {
print("Started a new game of Snakes and Ladders")
}
print("The game is using a \(game.dice.sides)-sided dice")
}
func game(game: DiceGame, didStartNewTurnWithDiceRoll diceRoll: Int) {
++numberOfTurns
print("Rolled a \(diceRoll)")
}
func gameDidEnd(game: DiceGame) {
print("The game lasted for \(numberOfTurns) turns")
}
}
// In action:
let tracker = DiceGameTracker()
let game = SnakesAndLadders()
game.delegate = tracker
game.play()
/*
=============================================
Adding Protocol Conformance with an Extension
=============================================
*/
/*
You can extend an existing type to adopt and conform to a new protocol,
even if you do not have access to the source code for the existing type.
Extensions can add new properties, methods, and subscripts to an existing type,
and are therefore able to add any requirements that a protocol may demand
*/
protocol TextRepresentable {
var textualDescription: String { get }
}
extension Dice: TextRepresentable {
var textualDescription: String {
return "A \(sides)-sided dice"
}
}
let d12 = Dice(sides: 12, generator: LinearCongruentialGenerator())
print(d12.textualDescription)
// prints "A 12-sided dice"
/* Declaring Protocol Adoption with an Extension */
struct Hamster {
var name: String
var textualDescription: String {
return "A hamster named \(name)"
}
}
extension Hamster: TextRepresentable {}
// Protocols can also be added to collections
let simon = Hamster(name: "Simon")
let things: [TextRepresentable] = [d12, simon]
for thing in things {
print(thing.textualDescription)
}
/*
====================
Protocol Inheritance
====================
*/
protocol PrettyTextRepresentable: TextRepresentable {
var prettyTextualDescription: String { get }
}
extension SnakesAndLadders: TextRepresentable {
var textualDescription: String {
return "A game of Snakes and Ladders with \(finalSquare) squares"
}
}
extension SnakesAndLadders: PrettyTextRepresentable {
var prettyTextualDescription: String {
var output = textualDescription + ":\n"
for index in 1...finalSquare {
switch board[index] {
case let ladder where ladder > 0:
output += "▲ "
case let snake where snake < 0:
output += "▼ "
default:
output += "○ "
}
}
return output
}
}
/*
This example defines a new protocol, PrettyTextRepresentable, which inherits from TextRepresentable.
Anything that adopts PrettyTextRepresentable must satisfy all of the requirements enforced by TextRepresentable,
plus the additional requirements enforced by PrettyTextRepresentable.
*/
/*
====================
Class-Only Protocols
====================
*/
/*
You can limit protocol adoption to class types (and not structures or enumerations) by adding the class keyword
to a protocol’s inheritance list. The class keyword must always appear first in a protocol’s inheritance list, before any inherited protocols
*/
protocol SomeInheritedProtocol {}
protocol SomeClassOnlyProtocol: class, SomeInheritedProtocol {
// class-only protocol definition goes here
}
/*
====================
Protocol Composition
====================
*/
/*
It can be useful to require a type to conform to multiple protocols at once.
You can combine multiple protocols into a single requirement with a protocol composition.
Protocol compositions have the form protocol<SomeProtocol, AnotherProtocol>.
You can list as many protocols within the pair of angle brackets (<>) as you need, separated by commas.
*/
protocol Named {
var name: String { get }
}
protocol Aged {
var age: Int { get }
}
struct Personn: Named, Aged {
var name: String
var age: Int
}
func wishHappyBirthday(celebrator: protocol<Named, Aged>) {
print("Happy birthday \(celebrator.name) - you're \(celebrator.age)!")
}
let birthdayPerson = Personn(name: "Malcolm", age: 21)
wishHappyBirthday(birthdayPerson)
// prints "Happy birthday Malcolm - you're 21!"
/*
NOTE
Protocol compositions do not define a new, permanent protocol type.
Rather, they define a temporary local protocol that has the combined requirements of all protocols in the composition.
*/
/*
=================================
Checking for Protocol Conformance
=================================
*/
/*
You can use the is and as operators described in Type Casting to check for protocol conformance,
and to cast to a specific protocol. Checking for and casting to a protocol follows exactly the same syntax
as checking for and casting to a type:
• The is operator returns true if an instance conforms to a protocol and returns false if it does not.
• The as? version of the downcast operator returns an optional value of the protocol’s type, and this value is nil if the instance does not conform to that protocol.
• The as! version of the downcast operator forces the downcast to the protocol type and triggers a runtime error if the downcast does not succeed.
*/
/*
==============================
Optional Protocol Requirements
==============================
*/
/*
Optional requirements are prefixed by the optional modifier as part of the protocol’s definition.
When you use a method or property in an optional requirement, its type automatically becomes an optional.
For example, a method of type (Int) -> String becomes ((Int) -> String)?. Note that the entire function type is wrapped in the optional, not method’s the return value.
You check for an implementation of an optional method by writing a question mark after the name of the method when it is called, such as someOptionalMethod?(someArgument).
NOTE
Optional protocol requirements can only be specified if your protocol is marked with the @objc attribute.
This attribute indicates that the protocol should be exposed to Objective-C code and is described in
Using Swift with Cocoa and Objective-C (Swift 2.1). Even if you are not interoperating with Objective-C,
you need to mark your protocols with the @objc attribute if you want to specify optional requirements.
Note also that @objc protocols can be adopted only by classes that inherit from
Objective-C classes or other @objc classes. They can’t be adopted by structures or enumerations.
*/
@objc protocol CounterDataSource {
optional func incrementForCount(count: Int) -> Int
optional var fixedIncrement: Int { get }
}
class Counter {
var count = 0
var dataSource: CounterDataSource?
func increment() {
if let amount = dataSource?.incrementForCount?(count) {
count += amount
} else if let amount = dataSource?.fixedIncrement {
count += amount
}
}
}
/*
Because the call to incrementForCount(_:) can fail for either of these two reasons,
the call returns an optional Int value. This is true even though incrementForCount(_:)
is defined as returning a nonoptional Int value in the definition of CounterDataSource.
*/
/*
===================
Protocol Extensions
===================
*/
extension RandomNumberGenerator {
func randomBool() -> Bool {
return random() > 0.5
}
}
// By creating an extension on the protocol, all conforming types automatically gain this method implementation without any additional modification.
generator = LinearCongruentialGenerator()
print("Here's a random number: \(generator.random())")
// prints "Here's a random number: 0.37464991998171"
print("And here's a random Boolean: \(generator.randomBool())")
// prints "And here's a random Boolean: true"
/*
You can use protocol extensions to provide a default implementation to any method or property requirement of that protocol.
If a conforming type provides its own implementation of a required method or property,
that implementation will be used instead of the one provided by the extension.
NOTE
Protocol requirements with default implementations provided by extensions are distinct from optional protocol requirements.
Although conforming types don’t have to provide their own implementation of either,
requirements with default implementations can be called without optional chaining.
*/
/*
When you define a protocol extension, you can specify constraints that conforming types must satisfy
before the methods and properties of the extension are available. You write these constraints
after the name of the protocol you’re extending using a where clause
*/
extension CollectionType where Generator.Element: TextRepresentable {
var textualDescription: String {
let itemsAsText = self.map { $0.textualDescription }
return "[" + itemsAsText.joinWithSeparator(", ") + "]"
}
}
let murrayTheHamster = Hamster(name: "Murray")
let morganTheHamster = Hamster(name: "Morgan")
let mauriceTheHamster = Hamster(name: "Maurice")
let hamsters = [murrayTheHamster, morganTheHamster, mauriceTheHamster]
// Because Array conforms to CollectionType and the array’s elements conform to the TextRepresentable protocol,
// the array can use the textualDescription property to get a textual representation of its contents.
print(hamsters.textualDescription)
// prints "[A hamster named Murray, A hamster named Morgan, A hamster named Maurice]"
/*
NOTE
If a conforming type satisfies the requirements for multiple constrained extensions that provide implementations
for the same method or property, Swift will use the implementation corresponding to the most specialized constraints.
*/