├── 10. Properties.playground
├── contents.xcplayground
├── section-1.swift
└── timeline.xctimeline
├── 11. Methods.playground
├── contents.xcplayground
├── section-1.swift
└── timeline.xctimeline
├── 12. Subscripts.playground
├── contents.xcplayground
├── section-1.swift
└── timeline.xctimeline
├── 13. Inheritance.playground
├── contents.xcplayground
├── section-1.swift
└── timeline.xctimeline
├── 14a. Initialization.playground
├── contents.xcplayground
├── section-1.swift
└── timeline.xctimeline
├── 14b. Initializer Chaining.playground
├── contents.xcplayground
├── section-1.swift
└── timeline.xctimeline
├── 15. Deinitialization.playground
├── contents.xcplayground
├── section-1.swift
└── timeline.xctimeline
├── 16. ARC.playground
├── contents.xcplayground
├── section-1.swift
└── timeline.xctimeline
├── 17. Optional Chaining.playground
├── contents.xcplayground
├── section-1.swift
└── timeline.xctimeline
├── 18. Type Casting.playground
├── contents.xcplayground
├── section-1.swift
└── timeline.xctimeline
├── 19. Nested Types.playground
├── contents.xcplayground
├── section-1.swift
└── timeline.xctimeline
├── 1a. The Basics.playground
├── contents.xcplayground
├── section-1.swift
└── timeline.xctimeline
├── 1b. Type alliases.playground
├── contents.xcplayground
├── section-1.swift
└── timeline.xctimeline
├── 1c. Tuples.playground
├── contents.xcplayground
├── section-1.swift
└── timeline.xctimeline
├── 1d. Optionals.playground
├── contents.xcplayground
├── section-1.swift
└── timeline.xctimeline
├── 1e. Assertions.playground
├── contents.xcplayground
├── section-1.swift
└── timeline.xctimeline
├── 2. Basic operations.playground
├── contents.xcplayground
├── section-1.swift
└── timeline.xctimeline
├── 20. Extensions.playground
├── contents.xcplayground
├── section-1.swift
└── timeline.xctimeline
├── 21. Protocols.playground
├── contents.xcplayground
├── section-1.swift
└── timeline.xctimeline
├── 22. Generics.playground
├── contents.xcplayground
├── section-1.swift
└── timeline.xctimeline
├── 23. Advanced Operators.playground
├── contents.xcplayground
├── section-1.swift
└── timeline.xctimeline
├── 3. Strings and Characters.playground
├── contents.xcplayground
├── section-1.swift
└── timeline.xctimeline
├── 4a. Arrays.playground
├── contents.xcplayground
├── section-1.swift
└── timeline.xctimeline
├── 4b. Dictionaries.playground
├── contents.xcplayground
├── section-1.swift
└── timeline.xctimeline
├── 5. Control Flow.playground
├── contents.xcplayground
├── section-1.swift
└── timeline.xctimeline
├── 6. Functions.playground
├── contents.xcplayground
├── section-1.swift
└── timeline.xctimeline
├── 7. Closures.playground
├── contents.xcplayground
├── section-1.swift
└── timeline.xctimeline
├── 8. Enumerations.playground
├── contents.xcplayground
├── section-1.swift
└── timeline.xctimeline
├── 9. Classes and Structures.playground
├── contents.xcplayground
├── section-1.swift
└── timeline.xctimeline
├── 99. Not The End.playground
├── contents.xcplayground
├── section-1.swift
└── timeline.xctimeline
└── README.md
/10. Properties.playground/contents.xcplayground:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/10. Properties.playground/section-1.swift:
--------------------------------------------------------------------------------
1 | // ------------------------------------------------------------------------------------------------
2 | // Things to know:
3 | //
4 | // * Properties store values in classes, structures and enumerations.
5 | // ------------------------------------------------------------------------------------------------
6 |
7 | // Here's a structure with a couple simple stored properties:
8 | struct FixedLengthRange
9 | {
10 | var firstValue: Int
11 | let length: Int
12 | }
13 |
14 | // Structures must have all of their properties initialized upon instantiation.
15 | //
16 | // This won't compile since the struct includes properties that havn't been initialized with
17 | // default values:
18 | //
19 | // var anotherRangeOfThreeItems = FixedLengthRange()
20 | //
21 | // In order to instantiate a FixedLengthRange struct, we must use the memberwise initializer. Note
22 | // that this will initialize a constant property and a variable property inside the struct:
23 | var rangeOfThreeItems = FixedLengthRange(firstValue: 0, length: 3)
24 | rangeOfThreeItems.firstValue = 6
25 |
26 | // ------------------------------------------------------------------------------------------------
27 | // Lazy Stored Properties
28 | //
29 | // A Lazy Stored Property is a value that is not calculated until its first use.
30 | //
31 | // They are declared using the "lazy" attribute and may not be constant.
32 | //
33 | // Global and local variables are all lazy, except that they don't need the lazy attribute.
34 | //
35 | // Here, we'll define a couple classes to showcase Lazy Stored Properties. In this example, let's
36 | // assume that DataImporter is a time-consuming process, and as such, we will want to use a lazy
37 | // stored property whenever we use it. This way, if we end up never using it, we never pay the
38 | // penalty of instantiating it.
39 | class DataImporter
40 | {
41 | var filename = "data.txt"
42 | }
43 |
44 | class DataManager
45 | {
46 | lazy var importer = DataImporter()
47 | var data = [String]()
48 | }
49 |
50 | // Now let's instantiate the data manager and add some simple data to the class:
51 | let manager = DataManager()
52 | manager.data.append("Some data")
53 | manager.data.append("Some more data")
54 |
55 | // Notice how we haven't used the importer yet, so it is nil:
56 | manager
57 |
58 | // So now let's access it:
59 | manager.importer.filename
60 |
61 | // And now we see the importer was created:
62 | manager
63 |
64 | // ------------------------------------------------------------------------------------------------
65 | // Computed Properties
66 | //
67 | // Computed properties don't store data, but rather use getters and setters for accessing values
68 | // that are computed up on request.
69 | //
70 | // Computed Properties are available for global as well as local variables.
71 | //
72 | // We'll start with a few structures that we'll use to show how computed properties work.
73 | struct Point
74 | {
75 | var x = 0.0, y = 0.0
76 | }
77 | struct Size
78 | {
79 | var width = 0.0, height = 0.0
80 | }
81 |
82 | // The following structure includes a computed property with a Point type named 'center'. Notice
83 | // that 'center' is variable (not constant) which is a requirement of computed properties.
84 | //
85 | // Every computed property must have a getter, but does not need a setter. If we provide a setter,
86 | // we need to know the new value being assigned to the computed property. We've called this
87 | // value 'newCenter'. Note that this value does not have a type annotation because the computed
88 | // property already knows that it is a Point type. Providing a type annotation would be an error.
89 | struct Rect
90 | {
91 | var origin = Point()
92 | var size = Size()
93 | var center: Point
94 | {
95 | get
96 | {
97 | let centerX = origin.x + (size.width / 2)
98 | let centerY = origin.y + (size.height / 2)
99 | return Point(x: centerX, y: centerY)
100 | }
101 | set(newCenter)
102 | {
103 | origin.x = newCenter.x - (size.width / 2)
104 | origin.y = newCenter.y - (size.height / 2)
105 | }
106 | }
107 | }
108 |
109 | // Here, we'll create a square from our Rect structure
110 | var square = Rect(origin: Point(x: 0.0, y: 0.0), size: Size(width: 10.0, height: 10.0))
111 |
112 | // We can now get the center point, computed from the Rect's origin and size. Being a computed
113 | // property, we can treat it just like any other peroperty.
114 | let initialSquareCenter = square.center
115 |
116 | // Since we provided a setter, we can also set the center point as if it is a stored property.
117 | // This will effectively update the Rect's origin and size based on the specified center point.
118 | square.center = Point(x: 15, y: 15)
119 |
120 | // We can see that the origin has been updated from (0, 0) to (10, 10):
121 | square.origin
122 |
123 | // Shorthand Setter Declaration
124 | //
125 | // The computed property's setter from the Rect structure provided a parameter on the setter named
126 | // 'newCenter'. If we don't specify this parameter, Swift will automatically generate an input
127 | // value named 'newValue' for us.
128 | //
129 | // Here, AlternativeRect is the same declaration as Rect above, except that the setter uses
130 | // Swift's default setter value, 'newValue':
131 | struct AlternativeRect
132 | {
133 | var origin = Point()
134 | var size = Size()
135 | var center: Point
136 | {
137 | get
138 | {
139 | let centerX = origin.x + (size.width / 2)
140 | let centerY = origin.y + (size.height / 2)
141 | return Point(x: centerX, y: centerY)
142 | }
143 | set
144 | {
145 | origin.x = newValue.x - (size.width / 2)
146 | origin.y = newValue.y - (size.height / 2)
147 | }
148 | }
149 | }
150 |
151 | // We can also have a read-only computed property by simply omitting the setter:
152 | struct Cube
153 | {
154 | var width = 0.0, height = 0.0, depth = 0.0
155 | var volume: Double
156 | {
157 | get
158 | {
159 | return width * height * depth
160 | }
161 | }
162 | }
163 |
164 | // Alternatively, Swift allows us to shorten the syntax of a read-only computed property by
165 | // omitting the get{} construct and inserting the code for the getter directly into the property
166 | // declaration:
167 | struct AnotherCube
168 | {
169 | var width = 0.0, height = 0.0, depth = 0.0
170 | var volume: Double
171 | {
172 | return width * height * depth
173 | }
174 | }
175 |
176 | // Let's declare our structure and read the 'volume' property
177 | var cube = AnotherCube(width: 4, height: 5, depth: 2)
178 | cube.volume
179 |
180 | // Since the 'volume' property is read-only, if we tried to assign a value to it, it would
181 | // would generate a compiler error.
182 | //
183 | // The following line of code will not compile:
184 | //
185 | // cube.volume = 8.0
186 |
187 | // ------------------------------------------------------------------------------------------------
188 | // Property Observers
189 | //
190 | // Property observers allow us to respond to changes in a property's value. We can declare an
191 | // observer that contains our code that is run just before a property is set (optionally allowing
192 | // us to alter the value being set) and an observer that is run just after a property has been
193 | // modified.
194 | //
195 | // Property observers are available for global as well as local variables.
196 | //
197 | // These observers are not called when a property is first initialized, but only when it changes.
198 | //
199 | // Similar to setters, each 'willSet' and 'didSet' will receive a parameter that represents (in
200 | // the case of 'willSet') the value about to be assigned to the property and (in the case of
201 | // 'didSet') the value that was previously stored in the property prior to modification.
202 | class StepCounter
203 | {
204 | var totalSteps: Int = 0
205 | {
206 | willSet(newTotalSteps)
207 | {
208 | "About to step to \(newTotalSteps)"
209 | }
210 | didSet(oldTotalSteps)
211 | {
212 | "Just stepped from \(oldTotalSteps)"
213 | }
214 | }
215 | }
216 |
217 | // Let's create an instance of StepCounter so we can try out our observer
218 | let stepCounter = StepCounter()
219 |
220 | // The following will first call 'willSet' on the 'totalSteps' property, followed by a change to
221 | // the value itself, followed by a call to 'didSet'
222 | stepCounter.totalSteps = 200;
223 |
224 | // Similar to setters, we can shorten our observers by omitting the parameter list for each. When
225 | // we co this, Swift automatically provides parameters named 'newValue' and 'oldValue'
226 | class StepCounterShorter
227 | {
228 | var totalSteps: Int = 0
229 | {
230 | willSet{ "About to step to \(newValue)" }
231 | didSet { "Just stepped from \(oldValue)" }
232 | }
233 | }
234 |
235 | // We can also override the value being set by modifying the property itself within the 'didSet'
236 | // observer. This only works in the 'didSet'. If you attempt to modify the property in 'willSet'
237 | // you will receive a compiler warning.
238 | //
239 | // Let's try wrapping our value to the range of 0...255
240 | class StepCounterShorterWithModify
241 | {
242 | var totalSteps: Int = 0
243 | {
244 | willSet{ "About to step to \(newValue)" }
245 | didSet { totalSteps = totalSteps & 0xff }
246 | }
247 | }
248 | var stepper = StepCounterShorterWithModify()
249 | stepper.totalSteps = 345
250 | stepper.totalSteps // This reports totalSteps is now set to 89
251 |
252 | // ------------------------------------------------------------------------------------------------
253 | // Type Properties
254 | //
255 | // Until now, we've been working with Instance Properties and Instance Methods, which are
256 | // associated to an instance of a class, structure or enumeration. Each instance gets its own copy
257 | // of the property or method.
258 | //
259 | // Type properties are properties that are attached to the class, structure or enumeration's type
260 | // itself and not any specific instance. All instances of a type will share the same Type Property.
261 | //
262 | // These are similar to 'static' properties in C-like languages.
263 | //
264 | // For Value types (structs, enums) we use the 'static' keyword. For Class types with the 'class'
265 | // keyword.
266 | //
267 | // Type properties can be in the form of stored properties or computed properties. As with normal
268 | // stored or computed properties, all the same rules apply except that stored type properties must
269 | // always have a default value. This exception is necessary because the initializer (which we'll
270 | // learn about later) is associated to a specific instance and as such, cannot be reliable for
271 | // initializing type properties.
272 | //
273 | // Here is a class with a couple of type properties
274 | struct SomeStructure
275 | {
276 | static var storedTypeProperty = "some value"
277 |
278 | // Here's a read-only computed type property using the short-hand read-only syntax
279 | static var computedTypeProperty: Int { return 4 }
280 | }
281 |
282 | // Similarly, here's an enumeration with a couple of type properties
283 | enum SomeEnum
284 | {
285 | static let storedTypeProperty = "some value"
286 |
287 | static var computedTypeProperty: Int { return 4 }
288 | }
289 |
290 | // Classes are a little different in that they cannot contain stored type properties, but may
291 | // only contain computed type properties
292 | class SomeClass
293 | {
294 | // The following line won't compile because classes aren't allowed stored type properties
295 | //
296 | // class var storedTypeProperty = "some value"
297 |
298 | // This is read-only, but you can also do read/write
299 | class var computedTypeProperty: Int { return 4 }
300 | }
301 |
302 |
--------------------------------------------------------------------------------
/10. Properties.playground/timeline.xctimeline:
--------------------------------------------------------------------------------
1 |
2 |
4 |
5 |
7 |
8 |
9 |
10 |
--------------------------------------------------------------------------------
/11. Methods.playground/contents.xcplayground:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/11. Methods.playground/section-1.swift:
--------------------------------------------------------------------------------
1 | // ------------------------------------------------------------------------------------------------
2 | // Things to know:
3 | //
4 | // * Methods can be in the form of Instance Methods, which apply to a given instance of a class
5 | // struct or enumeration and Type Methods, which apply to the type itself, like static methods
6 | // in C-like languages.
7 | // ------------------------------------------------------------------------------------------------
8 |
9 | // Instance Methods
10 | //
11 | // Instance methods work on instances of a class, structure or enumeration. In order to call an
12 | // instance method, you must first instantiate the class, structure or enumeration and then place
13 | // the call on that instance.
14 | //
15 | // Here, we'll create a class with a single instance method:
16 | class SomeClass
17 | {
18 | func doSomething()
19 | {
20 | // ...
21 | }
22 | }
23 |
24 | // Since this should be pretty clear, let's jump into parameters for methods with internal and
25 | // external names.
26 | //
27 | // The defaults for external names are different for methods than they are for global functions.
28 | //
29 | // For methods, the default behavior is that the caller must always specify all but the first
30 | // external parameter name when calling the method. Member authors need not specify the external
31 | // names for those parameters as the default is to treat all parameters as if they had the "#"
32 | // specifier, which creates an external parameter name that mirrors the local parameter name.
33 | //
34 | // To override this default-external-names-for-second-and-beyond-parameters, specify an "_" as the
35 | // external parameter name for all but the first parameter.
36 | //
37 | // If you want the caller to also use external name for the first parameter, be sure to add your
38 | // own '#' symbol to the local name or specify the external name explicitly.
39 | //
40 | // Here's a class that exercises the various combinations of internal and external name usages:
41 | class Counter
42 | {
43 | var count = 0;
44 |
45 | // No parameters
46 | func increment()
47 | {
48 | count++
49 | }
50 |
51 | // One parameter, no external parameter name needed by caller
52 | func incrementBy(amount: Int)
53 | {
54 | count += amount
55 | }
56 |
57 | // One parameter, overriding default behavior to requre caller to use external parameter name
58 | // on first (and only) parameter
59 | func addValueTo(value amount: Int)
60 | {
61 | count += amount
62 | }
63 |
64 | // Two parameters. Since no external names are specified, default behavior is implied: Caller
65 | // need not specify the first parameter's external name, but must specify all others:
66 | func addTwiceWithExternalImplied(first: Int, second: Int)
67 | {
68 | count += first
69 | count += second
70 | }
71 |
72 | // Two parameters. Using explicit external parameter names on all parameters to force caller
73 | // to use them for all parameters, including the first.
74 | func addTwiceWithExternalSpecified(a first: Int, b second: Int)
75 | {
76 | count += first
77 | count += second
78 | }
79 |
80 | // Two parameters. Using the external parameter shorthand ("#") to force caller to use
81 | // external parameter name on first parameter and defaulting to shared local/external names
82 | // for the rest.
83 | func addTwiceWithExternalSpecified2(#first: Int, second: Int)
84 | {
85 | count += first
86 | count += second
87 | }
88 |
89 | // Two parameters. Disabling all external names
90 | func addTwiceWithExternalSpecified3(first: Int, _ second: Int)
91 | {
92 | count += first
93 | count += second
94 | }
95 | }
96 |
97 | // Now let's see how we call each of those functions
98 | var counter = Counter()
99 | counter.increment()
100 | counter.incrementBy(4)
101 | counter.addValueTo(value: 4)
102 | counter.addTwiceWithExternalImplied(50, second: 4)
103 | counter.addTwiceWithExternalSpecified(a: 50, b: 4)
104 | counter.addTwiceWithExternalSpecified2(first: 10, second: 10)
105 | counter.addTwiceWithExternalSpecified3(10, 10)
106 | counter.count
107 |
108 | // The 'self' property refers to the current instance of a class, structure or enumeration. For
109 | // C++ developers, think of 'self' as 'this'.
110 | class Point
111 | {
112 | var x: Int = 10
113 |
114 | func setX(x: Int)
115 | {
116 | // Using self to disambiguate from the local parameter
117 | self.x = x
118 | }
119 | }
120 |
121 | // ------------------------------------------------------------------------------------------------
122 | // Mutation
123 | //
124 | // Instance methods cannot by default modify properties of structures or enumerations. To enable
125 | // this, mark them as 'mutating':
126 | struct Point2
127 | {
128 | var x: Int = 10
129 |
130 | // Note the need for the keyword 'mutating'
131 | mutating func setX(x: Int)
132 | {
133 | self.x = x
134 | }
135 | }
136 |
137 | // We'll create a constant Point2...
138 | let fixedPoint = Point2(x: 3)
139 |
140 | // Because 'fixedPoint' is constant, we are not allowed to call mutating memthods:
141 | //
142 | // The following line won't compile:
143 | //
144 | // fixedPoint.setX(4)
145 |
146 | // If you're working with a structure or enumeration (not a class), uou can assign to 'self'
147 | // directly
148 | struct Point3
149 | {
150 | var x = 0
151 |
152 | // This does not work with classes
153 | mutating func replaceMe(newX: Int)
154 | {
155 | self = Point3(x: 3)
156 | }
157 | }
158 |
159 | // Assigning to 'self' in an enumeration is used to change to a different member of the same
160 | // enumeration:
161 | enum TriStateSwitch
162 | {
163 | case Off, Low, High
164 | mutating func next()
165 | {
166 | switch self
167 | {
168 | case Off:
169 | self = Low
170 | case Low:
171 | self = High
172 | case High:
173 | self = Off
174 | }
175 | }
176 | }
177 |
178 | // ------------------------------------------------------------------------------------------------
179 | // Type Methods
180 | //
181 | // Type methods are like C++'s static methods.
182 | //
183 | // They can only access Type members.
184 | struct LevelTracker
185 | {
186 | var currentLevel = 1
187 | static var highestUnlockedLevel = 1
188 |
189 | static func unlockedLevel(level: Int)
190 | {
191 | if level > highestUnlockedLevel
192 | {
193 | highestUnlockedLevel = level
194 | }
195 | }
196 | static func levelIsUnlocked(level: Int) -> Bool
197 | {
198 | return level <= highestUnlockedLevel
199 | }
200 | mutating func advanceToLevel(level: Int) -> Bool
201 | {
202 | if LevelTracker.levelIsUnlocked(level)
203 | {
204 | currentLevel = level
205 | return true
206 | }
207 | else
208 | {
209 | return false
210 | }
211 | }
212 | }
213 |
214 | // To call a type method, use the type name, not the instance name:
215 | LevelTracker.levelIsUnlocked(3)
216 |
217 | // If we attempt to use an instance to call a type method, we'll get an error
218 | var levelTracker = LevelTracker()
219 |
220 | // The following line will not compile:
221 | //
222 | // levelTracker.levelIsUnlocked(3)
223 |
224 | // For classes, type methods use the 'class' keyword rather than the 'static' keyword:
225 | class SomeOtherClass
226 | {
227 | class func isGreaterThan100(value: Int) -> Bool
228 | {
229 | return value > 100
230 | }
231 | }
232 |
233 | // We call class type methods with the type name just as we do for structures and enumerations:
234 | SomeOtherClass.isGreaterThan100(105)
235 |
--------------------------------------------------------------------------------
/11. Methods.playground/timeline.xctimeline:
--------------------------------------------------------------------------------
1 |
2 |
4 |
5 |
7 |
8 |
9 |
10 |
--------------------------------------------------------------------------------
/12. Subscripts.playground/contents.xcplayground:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/12. Subscripts.playground/section-1.swift:
--------------------------------------------------------------------------------
1 | // ------------------------------------------------------------------------------------------------
2 | // Things to know:
3 | //
4 | // * Subscripts allow you to declare functionality for instances to make use of the subscript
5 | // operator ( [] ).
6 | //
7 | // * Subscripts are available for classes, structures and enumerations.
8 | //
9 | // * Subscripts are declared much like getters and setters for properties.
10 | // ------------------------------------------------------------------------------------------------
11 |
12 | // Subscripts are declared like getters and setters, and follow the same syntax rules for
13 | // read-only and read-write variants. They also can employ the same syntactic simplifications
14 | // available for getters and setters.
15 | //
16 | // Here's a structure that utilizes a subscript so that we can see the syntax of the declaration.
17 | struct TimesTable
18 | {
19 | let multiplier: Int
20 |
21 | // Read-only subscript using simplified getter syntax
22 | subscript(index: Int) -> Int
23 | {
24 | return multiplier * index
25 | }
26 |
27 | // Overloaded subscript for type Double, also read-only using the simplified getter syntax
28 | subscript(index: Double) -> Int
29 | {
30 | return multiplier * Int(index)
31 | }
32 | }
33 |
34 | // We can now make use of our newly created subscripts
35 | let threeTimesTable = TimesTable(multiplier: 3)
36 | threeTimesTable[3]
37 | threeTimesTable[4.0]
38 |
39 | // Subscripts can take any parameter type and variadic parameters, but cannot use inout or default
40 | // parameters.
41 | //
42 | // Here's a more complex example:
43 | struct Matrix
44 | {
45 | let rows: Int
46 | let columns: Int
47 | var grid: [Double]
48 |
49 | init (rows: Int, columns: Int)
50 | {
51 | self.rows = rows
52 | self.columns = columns
53 | grid = Array(count: rows * columns, repeatedValue: 0.0)
54 | }
55 |
56 | func indexIsValidForRow(row: Int, column: Int) -> Bool
57 | {
58 | return row >= 0 && row < rows && column >= 0 && column < columns
59 | }
60 |
61 | // Subscript with getter & setter as well as dual-parameter subscript
62 | subscript(row: Int, column: Int) -> Double
63 | {
64 | get
65 | {
66 | assert(indexIsValidForRow(row, column: column), "Index out of range")
67 | return grid[row * columns + column]
68 | }
69 | set
70 | {
71 | assert(indexIsValidForRow(row, column: column), "Index out of range")
72 | grid[row * columns + column] = newValue
73 | }
74 | }
75 | }
76 |
77 | // We'll create a standard 4x4 identity matrix
78 | var matrix = Matrix(rows: 4, columns: 4)
79 | matrix[0,0] = 1
80 | matrix[1,1] = 1
81 | matrix[2,2] = 1
82 | matrix[3,3] = 1
83 |
--------------------------------------------------------------------------------
/12. Subscripts.playground/timeline.xctimeline:
--------------------------------------------------------------------------------
1 |
2 |
4 |
5 |
7 |
8 |
9 |
10 |
--------------------------------------------------------------------------------
/13. Inheritance.playground/contents.xcplayground:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/13. Inheritance.playground/section-1.swift:
--------------------------------------------------------------------------------
1 | // ------------------------------------------------------------------------------------------------
2 | // Things to know:
3 | //
4 | // * There is no default base class for Swift objects. Any class that doesn't derive from
5 | // another class is a base class.
6 | // ------------------------------------------------------------------------------------------------
7 |
8 | // Let's start with a simple base class:
9 | class Vehicle
10 | {
11 | var numberOfWheels: Int
12 | var maxPassengers: Int
13 |
14 | func description() -> String
15 | {
16 | return "\(numberOfWheels) wheels; up to \(maxPassengers) passengers"
17 | }
18 |
19 | // Must initialize any properties that do not have a default value
20 | init()
21 | {
22 | numberOfWheels = 0
23 | maxPassengers = 1
24 | }
25 | }
26 |
27 | // ------------------------------------------------------------------------------------------------
28 | // Subclasses
29 | //
30 | // Now let's subclass the Vehicle to create a two-wheeled vehicle called a Bicycle
31 | class Bicycle: Vehicle
32 | {
33 | // We'll make this a 2-wheeled vehicle
34 | override init()
35 | {
36 | super.init()
37 | numberOfWheels = 2
38 | }
39 | }
40 |
41 | // We can call a member from the superclass
42 | let bicycle = Bicycle()
43 | bicycle.description()
44 |
45 | // Subclasses can also be subclassed
46 | class Tandem: Bicycle
47 | {
48 | // This bicycle has 2 passengers
49 | override init()
50 | {
51 | super.init()
52 | maxPassengers = 2
53 | }
54 | }
55 |
56 | // Here, we'll create a car that includes a new description by overriding the superclass' instance
57 | // method
58 | class Car: Vehicle
59 | {
60 | // Adding a new property
61 | var speed: Double = 0.0
62 |
63 | // New initialization, starting with super.init()
64 | override init()
65 | {
66 | super.init()
67 | maxPassengers = 5
68 | numberOfWheels = 4
69 | }
70 |
71 | // Using the override keyword to specify that we're overriding a function up the inheritance
72 | // chain. It is a compilation error to leave out 'override' if a method exists up the chain.
73 | override func description() -> String
74 | {
75 | // We start with the default implementation of description then tack our stuff onto it
76 | return super.description() + "; " + "traveling at \(speed) mph"
77 | }
78 | }
79 |
80 | // Here, we'll check our description to see that it does indeed give us something different from
81 | // the superclass' default description:
82 | let car = Car()
83 | car.speed = 55
84 | car.description()
85 |
86 | // ------------------------------------------------------------------------------------------------
87 | // Overriding Properties
88 | //
89 | // We can override property getters and setters. This applies to any property, including stored and
90 | // computed properties
91 | //
92 | // When we do this, our overridden property must include the name of the property as well as the
93 | // property's type.
94 | class SpeedLimitedCar: Car
95 | {
96 | // Make sure to specify the name and type
97 | override var speed: Double
98 | {
99 | get
100 | {
101 | return super.speed
102 | }
103 | // We need a setter since the super's property is read/write
104 | //
105 | // However, if the super was read-only, we wouldn't need this setter unless we wanted to
106 | // add write capability.
107 | set
108 | {
109 | super.speed = min(newValue, 40.0)
110 | }
111 | }
112 | }
113 |
114 | // We can see our override in action
115 | var speedLimitedCar = SpeedLimitedCar()
116 | speedLimitedCar.speed = 60
117 | speedLimitedCar.speed
118 |
119 | // We can also override property observers
120 | class AutomaticCar: Car
121 | {
122 | var gear = 1
123 | override var speed: Double
124 | {
125 | // Set the gear based on changes to speed
126 | didSet { gear = Int(speed / 10.0) + 1 }
127 |
128 | // Since we're overriding the property observer, we're not allowed to override the
129 | // getter/setter.
130 | //
131 | // The following lines will not compile:
132 | //
133 | // get { return super.speed }
134 | // set { super.speed = newValue }
135 | }
136 | }
137 |
138 | // Here is our overridden observers in action
139 | var automaticCar = AutomaticCar()
140 | automaticCar.speed = 35.0
141 | automaticCar.gear
142 |
143 | // ------------------------------------------------------------------------------------------------
144 | // Preenting Overrides
145 | //
146 | // We can prevent a subclass from overriding a particular method or property using the 'final'
147 | // keyword.
148 | //
149 | // final can be applied to: class, var, func, class methods and subscripts
150 | //
151 | // Here, we'll prevent an entire class from being subclassed by applying the . Because of this,
152 | // the finals inside the class are not needed, but are present for descriptive purposes. These
153 | // additional finals may not compile in the future, but they do today:
154 | final class AnotherAutomaticCar: Car
155 | {
156 | // This variable cannot be overridden
157 | final var gear = 1
158 |
159 | // We can even prevent overridden functions from being further refined
160 | final override var speed: Double
161 | {
162 | didSet { gear = Int(speed / 10.0) + 1 }
163 | }
164 | }
165 |
--------------------------------------------------------------------------------
/13. Inheritance.playground/timeline.xctimeline:
--------------------------------------------------------------------------------
1 |
2 |
4 |
5 |
7 |
8 |
9 |
10 |
--------------------------------------------------------------------------------
/14a. Initialization.playground/contents.xcplayground:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/14a. Initialization.playground/section-1.swift:
--------------------------------------------------------------------------------
1 | // ------------------------------------------------------------------------------------------------
2 | // Things to know:
3 | //
4 | // * Swift provides an initializer (which partially resembles a function) to ensure that every
5 | // property in a class, structure or enumeration is fully initialized.
6 | //
7 | // * Optionals do not need to be initialized as they are automatically initialized to nil if no
8 | // default value is provided.
9 | // ------------------------------------------------------------------------------------------------
10 |
11 | // Here's a basic use of an initializer.
12 | //
13 | // The Fahrenheit class will create a 'temperature' property that does not have a default value.
14 | // Because there is no default value, the property must be initialized in the initializer.
15 | //
16 | // Without the initializer, we would get an error whenever we tried to instantiate the Fahrenheit
17 | // class.
18 | struct Fahrenheit
19 | {
20 | var temperature: Double
21 |
22 | init()
23 | {
24 | temperature = 32.0
25 | }
26 | }
27 |
28 | // Since the class can fully initialize itself, we can safely instantiate it with no error:
29 | var f = Fahrenheit()
30 |
31 | // Since the temperature is always defined as "32.0", it is cleaner and preferred to use a default
32 | // value instead of setting it inside the initializer:
33 | struct AnotherFahrenheit
34 | {
35 | var temperature: Double = 32.0
36 | }
37 |
38 | // Initializers can also include parameters. Here's an example of using a parameter to initialize
39 | // the class's temperature to a given value.
40 | //
41 | // The following class contains two initializers:
42 | struct Celsius
43 | {
44 | var temperatureInCelsius: Double = 0.0
45 |
46 | // Initialize our temperature from Fahrenheit
47 | init(fromFahrenheit fahrenheit: Double)
48 | {
49 | temperatureInCelsius = (fahrenheit - 32.0) / 1.8
50 | }
51 |
52 | // Initialize our temperature from Kelvin
53 | init(kelvin: Double)
54 | {
55 | temperatureInCelsius = kelvin - 273.15
56 | }
57 | }
58 |
59 | // Now let's try our new initializers
60 | let boilingPotOfWater = Celsius(fromFahrenheit: 212.0)
61 | let freezingPointOfWater = Celsius(kelvin: 273.15)
62 |
63 | // External parameter names are automatically generated for the initializer. In order to opt-out
64 | // of this automated convenience, you can specify "_" for the extnenal name explicitly.
65 | //
66 | // Here's a class that defines two initializers - one that makes use of the automatic external
67 | // name generation and one that opts out:
68 | struct Color
69 | {
70 | let red = 0.0, green = 0.0, blue = 0.0
71 |
72 | // This initializer will make use of automatically generated exernal names
73 | init(red: Double, green: Double, blue: Double)
74 | {
75 | self.red = red
76 | self.green = green
77 | self.blue = blue
78 | }
79 |
80 | // This initializer opts out by explicitly declaring external names with "_"
81 | init(_ red: Double, _ blue: Double)
82 | {
83 | self.red = red
84 | self.green = 0
85 | self.blue = blue
86 | }
87 | }
88 |
89 | // Here, we can see our efforts pay off:
90 | let magenta = Color(red: 1.0, green: 0.0, blue: 1.0)
91 | let purple = Color(1.0, 0.5)
92 |
93 | // Optional properties do not need to be initialized:
94 | class SurveyQuestion
95 | {
96 | var text: String
97 |
98 | // Response is optional, and is automatically initialized to nil
99 | var response: String?
100 |
101 | init(text: String)
102 | {
103 | // We only need to initialize 'text'
104 | self.text = text
105 | }
106 |
107 | func ask() -> String
108 | {
109 | return text
110 | }
111 | }
112 |
113 | // Constants need initialization as well. In the following example, our constant has a default
114 | // value. However, if we initialize the class with the init(text: String) initializer, we can
115 | // modify the default value to use the one passed in:
116 | class SurveyQuestion2
117 | {
118 | // Default value of "No question"
119 | let text: String = "No question"
120 |
121 | var response: String?
122 |
123 | init(text: String)
124 | {
125 | // Initializing the constant, 'text', even though it has a default value, we can modify
126 | // that default value here
127 | self.text = text
128 | }
129 |
130 | init()
131 | {
132 | // We do nothing here and let the default value for 'text' define its value
133 | }
134 |
135 | func ask() -> String
136 | {
137 | return text
138 | }
139 | }
140 |
141 | // Here, we initialize the class with a blank initializer (calling init()) to let text's default
142 | // value initialize the stored value
143 | let noQuestion = SurveyQuestion2()
144 |
145 | // Here, we'll us an aalternat initializer to specify a different value for 'text'
146 | let beetsQuestion = SurveyQuestion2(text: "Do you like beets?")
147 |
148 | // ------------------------------------------------------------------------------------------------
149 | // Default Initializer
150 | //
151 | // If all properties have default values (including optionals defaulting to nil) AND you do not
152 | // create your own initlializer AND there is no superclass, Swift will create a default
153 | // initializer (with no parameters) for you. This initializer sets all properties to their
154 | // default values.
155 | //
156 | // If you create your own initializer, Swift will not create the default initializer. If you want
157 | // your custom initializers as well as the default initializer, then put your initializers in an
158 | // extension.
159 | class ShoppingListItem
160 | {
161 | var name: String?
162 | var quantity = 1
163 | var purchased = false
164 |
165 | // No init(...) initializer
166 | }
167 |
168 | // ------------------------------------------------------------------------------------------------
169 | // Memberwise Initializers for Structure Types
170 | //
171 | // Similar to the default initializer for classes, structures that do not implement an initializer
172 | // but have default values for their stored properties will get a default memberwise initializer.
173 | //
174 | // As with classes, if you want your custom initializers as well as the default memberwise
175 | // initializer, then put your initializers in an extension.
176 | struct Size
177 | {
178 | var width = 0.0
179 | var height = 0.0
180 | }
181 |
182 | // Here, we call the default memberwise initializer that Swift created for us
183 | let twoByTwo = Size(width: 2.0, height: 2.0)
184 |
185 | // ------------------------------------------------------------------------------------------------
186 | // Initializer Delegatgion for Value Types
187 | //
188 | // Sometimes, it's convenient to have multiple initializers that call other initializers to do
189 | // some of the heavy lifting.
190 | //
191 | // In the following example, we have a class 'Rect' that has multiple initializers. One that
192 | // initializes our rect given an origin and size, while another that calls upon the origin/size
193 | // initializer from calculations based on the a center point.
194 | //
195 | // This concept is further extended in "Initializer Chaining", covered later.
196 | struct Point
197 | {
198 | var x = 0.0
199 | var y = 0.0
200 | }
201 |
202 | struct Rect
203 | {
204 | var origin = Point()
205 | var size = Size()
206 |
207 | // We create a basic initializer to use the default values Since we define other initializers,
208 | // the system won't create this for us, so we need to define it ourselves. Since we're using
209 | // the defaults, it is an empty closure.
210 | init() {}
211 |
212 | // Init from origin/size
213 | init(origin: Point, size: Size)
214 | {
215 | self.origin = origin
216 | self.size = size
217 | }
218 |
219 | // Init from center/size - note how we use the init(origin:size) to perform actual
220 | // initialization
221 | init(center: Point, size: Size)
222 | {
223 | let originX = center.x - size.width / 2
224 | let originY = center.y - size.height / 2
225 | self.init(origin: Point(x: originX, y: originY), size: size)
226 | }
227 | }
228 |
229 | // Here, we call the three initializers:
230 | let basicRect = Rect()
231 | let originRect = Rect(origin: Point(x: 2.0, y: 2.0), size: Size(width: 5.0, height: 5.0))
232 | let centerRect = Rect(center: Point(x: 4.0, y: 4.0), size: Size(width: 3.0, height: 3.0))
233 |
--------------------------------------------------------------------------------
/14a. Initialization.playground/timeline.xctimeline:
--------------------------------------------------------------------------------
1 |
2 |
4 |
5 |
7 |
8 |
9 |
10 |
--------------------------------------------------------------------------------
/14b. Initializer Chaining.playground/contents.xcplayground:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/14b. Initializer Chaining.playground/section-1.swift:
--------------------------------------------------------------------------------
1 | // ------------------------------------------------------------------------------------------------
2 | // Things to know:
3 | //
4 | // * Initializer Chaining refers to the way in which initialization takes place along the class
5 | // hierarchy.
6 | //
7 | // * Designated Initializers are those that are responsible for ensuring that the instance of a
8 | // class, structure or enumeration is properly initialized. These are the initializers you've
9 | // seen so far. Designated initializers are also resonsible for calling the superclass'
10 | // initializer.
11 | //
12 | // * Convenience Initializers are initializers that are provided for specialized initialization
13 | // and must call one of the designated initializers within a class in order to fulfill the
14 | // requirement of properly initializing a class.
15 | // ------------------------------------------------------------------------------------------------
16 |
17 | // Designated Initializers are the normal initializers ("init(...)") functions that you've seen
18 | // so far. If the system creates an initializer for you, it will be a designated initializer.
19 | //
20 | // Convenience Initializers allow you to offer different code-paths through initialization, which
21 | // call upon the designated initializers to do the heavy lifting. They use the keyword
22 | // "convenience". They are actually different than designated initializers because Swift uses the
23 | // difference (designated vs. convenience) to perform two-phase initialization, which we'll cover
24 | // shortly.
25 | //
26 | // Here's what a simple case looks like with a designated and convenience initializer:
27 | class Food
28 | {
29 | var name: String
30 |
31 | // Designated initializer - required as the class does not have a default value for 'name'.
32 | // There can be more than one of these, but the fewer the better, usually, design-wise.
33 | init(name: String)
34 | {
35 | self.name = name
36 | }
37 |
38 | // Here, we'll use a convenience initializer to initialize 'name' to an unnamed Food
39 | convenience init()
40 | {
41 | // Must call the designated in same class
42 | self.init(name: "[unnamed]")
43 | }
44 | }
45 |
46 | // Here we make use of our two initializers
47 | let namedMeat = Food(name: "Bacon")
48 | let mysteryMeat = Food()
49 |
50 | // ------------------------------------------------------------------------------------------------
51 | // Two-Phase Initialization
52 | //
53 | // Two-phase initialization is a new concept enforced by Swift. Think of it like this:
54 | //
55 | // Phase 1: Subclasses MUST FIRST initialize any stored properties that are part of their subclass.
56 | // They must do this before they are allowed to cusomize a super's stored property.
57 | //
58 | // Subclasses MUST THEN call the super's initializer, which will repeat this process
59 | // until the top-most superclass (the base class) is reached.
60 | //
61 | // At this point we can assume that the class is fully initialized (but not necessarily
62 | // customized by the subclasses that may need to alter the initialization results.
63 | //
64 | // Phase 2: In the base class' initializer, we can customize its properties before it returns to
65 | // the caller (a subclass.) That subclass is then allowed to write to any/all stored
66 | // properties defined by itself or the superclass chain. This continues until you get the
67 | // the bottom-most subclass.
68 | //
69 | // A note about convenience initializers:
70 | //
71 | // Convenience initializers must always call into a designated initializer from the current class.
72 | // A designated initializer for a subclass must then into a super's iniitializer, which may also
73 | // be a convenience initializer, because that convenience initializer will be required to
74 | // eventually call a designated initializer before going higher up the chain.
75 | //
76 | // Let's derive a class from Food so we can see this in action:
77 | class RecipeIngredient: Food
78 | {
79 | var quantity: Int
80 |
81 | // This is a designated initializer (because it has no 'convenience' keyword)
82 | init(name: String, quantity: Int)
83 | {
84 | // We must initialize our new stored properties first (this is Phase 1)
85 | self.quantity = quantity
86 |
87 | // Next, we must call super's initializer (still, Phase 1)
88 | super.init(name: name)
89 |
90 | // Only after the super's initializer is called, can we customize any properties that
91 | // originated from someplace up the class hierarchy chain.
92 | self.name = "Ingredient: " + name
93 | }
94 |
95 | // Here, we'll create a convenience initializer that simply provides a default quantity
96 | // value of 1. Note that in order for this to compile, we are required to call a designated
97 | // initializer.
98 | convenience override init(name: String)
99 | {
100 | self.init(name: name, quantity: 1)
101 | }
102 | }
103 |
104 | // Now we can call our various initializers to see them in action:
105 | let oneMysteryItem = RecipeIngredient()
106 | let oneBacon = RecipeIngredient(name: "Bacon")
107 | let sixEggs = RecipeIngredient(name: "Eggs", quantity: 6)
108 |
109 | // ------------------------------------------------------------------------------------------------
110 | // Inheriting a full set of the super's initializers
111 | //
112 | // In the following class, we don't specify any initializer, but that's OK because we have default
113 | // values for the stored property (purchased).
114 | //
115 | // Not providing any initializer at all allows us to gain a full set of the super's initializers.
116 | class ShoppingListItem: RecipeIngredient
117 | {
118 | var purchased = false
119 | var description: String
120 | {
121 | var output = "\(quantity) x \(name)"
122 | output += purchased ? " ✔" : " ✘"
123 | return output
124 | }
125 | }
126 |
127 | // Let's initialize our new ShoppingListItem using the super's initializer
128 | let lotsOfCheese = ShoppingListItem(name: "cheese", quantity: 99)
129 |
130 | // Here, we can create an array of ShippingListItems
131 | var breakfastList = [
132 | ShoppingListItem(),
133 | ShoppingListItem(name: "Bacon"),
134 | ShoppingListItem(name: "Eggs", quantity: 6),
135 | ]
136 |
137 | // ------------------------------------------------------------------------------------------------
138 | // For individual properties, we can set their default value with a closure or function instead
139 | // of just a literal value.
140 | //
141 | // In the example below, we use a closure to calculate the default value for 'estimatedPi'. Note
142 | // the parenthesis at the end of the closure. Without them, we would be asking to set 'estimatedPI'
143 | // to a closure type rather than to the result of the closure. Also, the parenthesis tell it to
144 | // execute the closure immediately.
145 | //
146 | // Also note that these closures used for default values for properties are not allowed to access
147 | // any of the other properties of the class because it's assumed that they have not yet been
148 | // initialized.
149 | class ClassWithPI
150 | {
151 | let estimatedPI: Double =
152 | {
153 | let constant1 = 22.0
154 | let constant2 = 7.0
155 |
156 | // Must return the type specified by the property
157 | return constant1 / constant2
158 | }()
159 | }
160 |
161 | // Here's a more pracitcal example. Note that since the closure is required to return the type
162 | // of the property it is initializing, it must create a temporary of the same type which is
163 | // initialized and then returned.
164 | struct CheckerBoard
165 | {
166 | let boardColors: [Bool] =
167 | {
168 | var temporaryBoard = [Bool]()
169 | var isBlack = false
170 | for i in 1...10
171 | {
172 | for j in 1...10
173 | {
174 | temporaryBoard.append(isBlack)
175 | isBlack = !isBlack
176 | }
177 |
178 | isBlack = !isBlack
179 | }
180 |
181 | // Return the temporary in order to set 'boardColors'
182 | return temporaryBoard
183 | }()
184 |
185 | func squareIsBlackAtRow(row: Int, column: Int) -> Bool
186 | {
187 | return boardColors[(row * 10) + column]
188 | }
189 | }
190 |
191 | // We can now check our work
192 | var board = CheckerBoard()
193 | board.squareIsBlackAtRow(1, column: 1) // Should be false
194 | board.squareIsBlackAtRow(1, column: 2) // Should be true
195 |
--------------------------------------------------------------------------------
/14b. Initializer Chaining.playground/timeline.xctimeline:
--------------------------------------------------------------------------------
1 |
2 |
4 |
5 |
7 |
8 |
9 |
10 |
--------------------------------------------------------------------------------
/15. Deinitialization.playground/contents.xcplayground:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/15. Deinitialization.playground/section-1.swift:
--------------------------------------------------------------------------------
1 | // ------------------------------------------------------------------------------------------------
2 | // Things to know:
3 | //
4 | // * Deinitializers are called automatically before a class instance is
5 | // deallocated,so you can access all properties in the deinitializer.
6 | //
7 | // * You cannot call them directly.
8 | //
9 | // * The superclass' deinitializer is called before the subclass'.
10 | //
11 | // * Swift uses ARC to manage memory, so deinitializers shouldn't always
12 | // be necessary. However, you might open a file and need to close it
13 | // when a class is deallocated.
14 | // ------------------------------------------------------------------------------------------------
15 |
16 | // Let's create a couple classes to work with...
17 | struct Bank
18 | {
19 | static var coinsInBank = 10_000
20 | static func vendCoins(var numberOfCoinsToVend: Int) -> Int
21 | {
22 | numberOfCoinsToVend = min(numberOfCoinsToVend, coinsInBank)
23 | coinsInBank -= numberOfCoinsToVend
24 | return numberOfCoinsToVend
25 | }
26 |
27 | static func receiveCoins(coins: Int)
28 | {
29 | coinsInBank += coins
30 | }
31 | }
32 |
33 | class Player
34 | {
35 | var coinsInPurse: Int
36 |
37 | init(coins: Int)
38 | {
39 | coinsInPurse = Bank.vendCoins(coins)
40 | }
41 |
42 | func winCoins(coins: Int)
43 | {
44 | coinsInPurse += coins
45 | }
46 |
47 | deinit
48 | {
49 | Bank.receiveCoins(coinsInPurse)
50 | }
51 | }
52 |
53 | // Let's exercise the Player class a bit and create a new player with 100
54 | // coins in his purse (pulled from the Bank.)
55 | var playerOne: Player? = Player(coins: 100)
56 | playerOne!.coinsInPurse
57 | Bank.coinsInBank
58 |
59 | // The Player now wins 2000 coins!
60 | playerOne!.winCoins(2_000)
61 | playerOne!.coinsInPurse
62 | Bank.coinsInBank
63 |
64 | // When we cause playerOne to be deallocated, the deinitializer is called
65 | playerOne = nil
66 |
67 | // This should print 12000 coins, but the playgrounds don't appear to do
68 | // this correctly. If you put this code into a project and compile/run
69 | // it (with minor changes to print variables using println) then you
70 | // will see that the bank does indeed have 12000 coins.
71 | Bank.coinsInBank
72 |
--------------------------------------------------------------------------------
/15. Deinitialization.playground/timeline.xctimeline:
--------------------------------------------------------------------------------
1 |
2 |
4 |
5 |
7 |
8 |
9 |
10 |
--------------------------------------------------------------------------------
/16. ARC.playground/contents.xcplayground:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/16. ARC.playground/timeline.xctimeline:
--------------------------------------------------------------------------------
1 |
2 |
4 |
5 |
7 |
8 |
9 |
10 |
--------------------------------------------------------------------------------
/17. Optional Chaining.playground/contents.xcplayground:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/17. Optional Chaining.playground/section-1.swift:
--------------------------------------------------------------------------------
1 | // ------------------------------------------------------------------------------------------------
2 | // Things to know:
3 | //
4 | // * Optional Chaining is the process of safely referencing a series of optionals (which contain
5 | // optionals, which contain optionals, etc.) without having to perform the optional unwrapping
6 | // checks at each step along the way.
7 | // ------------------------------------------------------------------------------------------------
8 |
9 | // Consider a case of forced unwrapping like "someOptional!.someProperty". We already know that
10 | // this is only safe if we know that the optional will never be nil. For times that we don't know
11 | // this, we must check the optional before we can reference it. This can become cumbersome for
12 | // situations where many optionals are chained together as properties of properties. Let's create
13 | // a series of optionals to show this:
14 | class Artist
15 | {
16 | let name: String
17 | init(name: String) { self.name = name }
18 | }
19 |
20 | class Song
21 | {
22 | let name: String
23 | var artist: Artist?
24 | init(name: String, artist: Artist?)
25 | {
26 | self.name = name
27 | self.artist = artist
28 | }
29 | }
30 |
31 | class MusicPreferences
32 | {
33 | var favoriteSong: Song?
34 | init(favoriteSong: Song?) { self.favoriteSong = favoriteSong }
35 | }
36 |
37 | class Person
38 | {
39 | let name: String
40 | var musicPreferences: MusicPreferences?
41 | init (name: String, musicPreferences: MusicPreferences?)
42 | {
43 | self.name = name
44 | self.musicPreferences = musicPreferences
45 | }
46 | }
47 |
48 | // Here, we'll create a working chain:
49 | var someArtist: Artist? = Artist(name: "Somebody with talent")
50 | var favSong: Song? = Song(name: "Something with a beat", artist: someArtist)
51 | var musicPrefs: MusicPreferences? = MusicPreferences(favoriteSong: favSong)
52 | var person: Person? = Person(name: "Bill", musicPreferences: musicPrefs)
53 |
54 | // We can access this chain with forced unwrapping. In this controlled environment, this will work
55 | // but it's probably not advisable in a real world situation unless you're sure that each member
56 | // of the chain is sure to never be nil.
57 | person!.musicPreferences!.favoriteSong!.artist!
58 |
59 | // Let's break the chain, removing the user's music preferences:
60 | if var p = person
61 | {
62 | p.musicPreferences = nil
63 | }
64 |
65 | // With a broken chain, if we try to reference the arist like before, we will get a runtime error.
66 | //
67 | // The following line will compile, but will crash:
68 | //
69 | // person!.musicPreferences!.favoriteSong!.artist!
70 | //
71 | // The solusion here is to use optional chaining, which replaces the forced unwrapping "!" with
72 | // a "?" character. If at any point along this chain, any optional is nil, evaluation is aborted
73 | // and the entire result becomes nil.
74 | //
75 | // Let's see this entire chain, one step at a time working backwards. This let's us see exactly
76 | // where the optional chain becomes a valid value:
77 | person?.musicPreferences?.favoriteSong?.artist
78 | person?.musicPreferences?.favoriteSong
79 | person?.musicPreferences
80 | person
81 |
82 | // Optional chaining can be mixed with non-optionals as well as forced unwrapping and other
83 | // contexts (subcripts, function calls, return values, etc.) We won't bother to create the
84 | // series of classes and instances for the following example, but it should serve as a valid
85 | // example of how to use optional chaining syntax in various situations. Given the proper context
86 | // this line of code would compile and run as expected.
87 | //
88 | // person?.musicPreferences?[2].getFavoriteSong()?.artist?.name
89 | //
90 | // This line could be read as: optional person's second optional music preference's favorite song's
91 | // optional artist's name.
92 |
--------------------------------------------------------------------------------
/17. Optional Chaining.playground/timeline.xctimeline:
--------------------------------------------------------------------------------
1 |
2 |
4 |
5 |
7 |
8 |
9 |
10 |
--------------------------------------------------------------------------------
/18. Type Casting.playground/contents.xcplayground:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/18. Type Casting.playground/section-1.swift:
--------------------------------------------------------------------------------
1 | // ------------------------------------------------------------------------------------------------
2 | // Things to know:
3 | //
4 | // * Type casting allows us to check the type of an instance and/or treat that instance as a
5 | // different type from somewhere else in its own hieararchy.
6 | //
7 | // * Type casting also allows us to determine if a type conforms to a protocol.
8 | //
9 | // * Additionally we can create
10 | // ------------------------------------------------------------------------------------------------
11 |
12 | // Let's start by creating a few types to work with:
13 | class MediaItem
14 | {
15 | var name: String
16 | init(name: String) { self.name = name }
17 | }
18 |
19 | class Movie: MediaItem
20 | {
21 | var director: String
22 | init(name: String, director: String)
23 | {
24 | self.director = director
25 | super.init(name: name)
26 | }
27 | }
28 |
29 | class Song: MediaItem
30 | {
31 | var artist: String
32 | init(name: String, artist: String)
33 | {
34 | self.artist = artist
35 | super.init(name: name)
36 | }
37 | }
38 |
39 | // We'll create a library of Movies and Songs. Note that Swift will infer the type of the array to
40 | // be MediaItem[].
41 | let library =
42 | [
43 | Movie(name: "Casablanca", director: "Michael Curtiz"),
44 | Song(name: "Blue Suede Shoes", artist: "Elvis Presley"),
45 | Movie(name: "Citizen Kane", director: "Orson Welles"),
46 | Song(name: "The One And Only", artist: "Chesney Hawkes"),
47 | Song(name: "Never Gunna Give You Up", artist: "Rick Astley")
48 | ]
49 |
50 | // ------------------------------------------------------------------------------------------------
51 | // Checking type
52 | //
53 | // We can check the type of an object very simply with the 'is' operator, also known as the Type
54 | // Check Operator.
55 | library[0] is Movie // true
56 | library[0] is Song // false
57 |
58 | // Let's see this in action. Lets loop through the library and count up the movies and songs:
59 | var movieCount = 0
60 | var songCount = 0
61 | for item in library
62 | {
63 | if item is Movie { ++movieCount }
64 | else if item is Song { ++songCount }
65 | }
66 |
67 | // Our final Movie and Song counts:
68 | movieCount
69 | songCount
70 |
71 | // ------------------------------------------------------------------------------------------------
72 | // Downcasting
73 | //
74 | // As with other languages, downcasting refers to casting an object to a subclass within its
75 | // hierarchy. This is done with the Type Cast Operator, "as".
76 | //
77 | // Because not all objects can be downcasted, the result of a downcast may be nil. Because of this,
78 | // we have two options for downcasting. We can downcast to an optional using the "as?" operator
79 | // or downcast with forced unwrapping of the optional with the standard "as" operator. As with
80 | // forced unwrapping, only do this if you know for certain that the downcast will succeed.
81 | //
82 | // Let's see this in action using optional binding and the "as?" operator. Remember that our
83 | // library has type MediaItem[], so if we cast an element of that array to either a Movie or Song,
84 | // will will be downcasting the instance.
85 | for item in library
86 | {
87 | if let movie = item as? Movie
88 | {
89 | "Movie: '\(movie.name)' was directed by \(movie.director)"
90 | }
91 | else if let song = item as? Song
92 | {
93 | "Song: '\(song.name)' was performed by \(song.artist)"
94 | }
95 | }
96 |
97 | // ------------------------------------------------------------------------------------------------
98 | // Type Casting for Any and AnyObject
99 | //
100 | // * AnyObject allows us to store an instance to any class type.
101 | //
102 | // * Any allows us to store a reference to any type at all, excluding functino types.
103 | //
104 | // We should strive to use Any and AnyObject only when we must (like when an API function returns
105 | // arrays of values of any object type.) Otherwise, it's important that we use explicit types to
106 | // conform to Swift's type safety.
107 | //
108 | // Let's see AnyObject in action. We'll define an array of type AnyObject[] and populate it with
109 | // some movies:
110 | let someObjects: [AnyObject] =
111 | [
112 | Movie(name: "2001: A Space Odyssey", director: "Stanley Kubrick"),
113 | Movie(name: "Moon", director: "Duncan Jones"),
114 | Movie(name: "Alien", director: "Ridley Scott"),
115 | ]
116 |
117 | // Here, we know that someObjects[] only contains Movie instances, so we'll use our forced version
118 | // of the typecast operator, "as". Note, however, that if somebody modifies the code later and adds
119 | // an instance of a non-Movie type (which they can do), we'll crash. This is why it's important
120 | // to limit our use of AnyObject and Any to only those cases where we absolutely need it.
121 | //
122 | // Let's see how we would use the someObjects array:
123 | for object: AnyObject in someObjects
124 | {
125 | let movie = object as Movie
126 | "Movie: '\(movie.name)' was directed by \(movie.director)"
127 | }
128 |
129 | // Alternatively, we can downcast the array itself rather than each item:
130 | var someMovies = someObjects as [Movie]
131 | for movie in someMovies
132 | {
133 | "Movie: '\(movie.name)' was directed by \(movie.director)"
134 | }
135 |
136 | // Finally, we can avoid the additional local variable and performt he downcast right inside
137 | // the loop structure:
138 | for movie in someObjects as [Movie]
139 | {
140 | "Movie: '\(movie.name)' was directed by \(movie.director)"
141 | }
142 |
143 | // Any allows us store references to any type at all (not including functions), which can include
144 | // integers, floating points, strings or objects.
145 | //
146 | // Let's see this in action. We'll create an array of type Any[] and fill it with random bits and
147 | // pieces of stuff:
148 | var things = [Any]()
149 |
150 | things.append(0)
151 | things.append(0.0)
152 | things.append(42)
153 | things.append(3.14159)
154 | things.append("Hello")
155 | things.append((3.0, 5.0))
156 | things.append(Movie(name: "Ghostbusters", director: "Ivan Reitman"))
157 |
158 | // We can now use a switch statement with a mix of "is" and "as" operators to parse through the
159 | // things array and get information from it.
160 | //
161 | // We'll use a switch statement to check each type (and even include a few where clauses to get a
162 | // bit more specific about the value inside the type.) Note that when we do this, we use the forced
163 | // version of the 'as' operator, which is safe in the context of a 'case' because a matched case
164 | // means that it's guaranteed to be a valid value of the given type.
165 | for thing in things
166 | {
167 | switch thing
168 | {
169 | case 0 as Int:
170 | "zero as an Int"
171 | case 0 as Double:
172 | "zero as a Double"
173 | case let someInt as Int:
174 | "an integer value of \(someInt)"
175 | case let someDouble as Double where someDouble > 0:
176 | "a positive Double value of \(someDouble)"
177 | case is Double:
178 | "some other double that I don't want to print"
179 | case let someString as String:
180 | "a string value of \(someString)"
181 | case let (x, y) as (Double, Double):
182 | "a Tuple used to store an X/Y floating point coordinate: \(x), \(y)"
183 | case let movie as Movie:
184 | "A movie called '\(movie.name)'"
185 | default:
186 | "Something else"
187 | }
188 | }
189 |
--------------------------------------------------------------------------------
/18. Type Casting.playground/timeline.xctimeline:
--------------------------------------------------------------------------------
1 |
2 |
4 |
5 |
7 |
8 |
9 |
10 |
--------------------------------------------------------------------------------
/19. Nested Types.playground/contents.xcplayground:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/19. Nested Types.playground/section-1.swift:
--------------------------------------------------------------------------------
1 | // ------------------------------------------------------------------------------------------------
2 | // Things to know:
3 | //
4 | // * Nested types are utility classes and structures that are declared within other classes,
5 | // structures and enumerations.
6 | // ------------------------------------------------------------------------------------------------
7 |
8 | // Let's take a look at how we might define a Blackjack Card using nested types.
9 | //
10 | // Each card has a suit (Spades, Hearts, Diamonds, Clubs) and a rank (Ace, King, Queen, etc.) This
11 | // is a natural use of nested types because the suit and rank enumerations only apply to a
12 | // Blackjack card and so they are scoped to within that that type.
13 | //
14 | // Additionally, since the rank of an Ace can be valued at either an 11 or a 1, we can create
15 | // a nested structure within the Rank enumeration that allows us to multiple values for a given
16 | // rank.
17 | //
18 | // Let's see how this might be implemented:
19 | struct BlackjackCard
20 | {
21 | // Nested Suit enumeration
22 | enum Suit: Character
23 | {
24 | case Spades = "♠", Hearts = "♡", Diamonds = "♢", Clubs = "♣"
25 | }
26 |
27 | // Nested Rank enumeration
28 | enum Rank: Int
29 | {
30 | case Two = 2, Three, Four, Five, Six, Seven, Eight, Nine, Ten
31 | case Jack, Queen, King, Ace
32 |
33 | // A rank can possibly have two values (for the Ace), so we'll use this structure
34 | // to contain those two values. We could just as well use a Tuple, but we're showcasing
35 | // nested types.
36 | //
37 | // Note that since all Ranks have a value but only some ranks have a second value, we'll
38 | // define the first value as an Int and the second value as an Int?
39 | struct Values
40 | {
41 | let first: Int
42 | let second: Int?
43 | }
44 |
45 | // Here we have a computed property to return the values of a rank. Note that the only
46 | // Rank to return multiple values is the Ace. However, we also use this to provide the
47 | // proper value for Jack/King/Queen, which are all equivalent to 10.
48 | var values: Values
49 | {
50 | switch self
51 | {
52 | case .Ace:
53 | return Values(first: 1, second: 11)
54 | case .Jack, .Queen, .King:
55 | return Values(first: 10, second: nil)
56 | default:
57 | return Values(first: self.rawValue, second: nil)
58 | }
59 | }
60 | }
61 |
62 | // BlackjackCard properties and methods
63 | let rank: Rank
64 | let suit: Suit
65 |
66 | var description: String
67 | {
68 | var output = "A \(suit.rawValue) with a value of \(rank.values.first)"
69 | if let second = rank.values.second
70 | {
71 | output += " or \(second)"
72 | }
73 | return output
74 | }
75 | }
76 |
77 | // Let's initialize a BlackJack card for the Ace of Spades. Note that BlackjackCard doesn't define
78 | // any initializers, so we're able to use the default memberwise initializer.
79 | //
80 | // Also note that since the initializer knows thet type of each member being initialized (both of
81 | // which are enumerations) we can use the shorthand method (.Something) for each member's initial
82 | // value.
83 | let theAceOfSpades = BlackjackCard(rank: .Ace, suit: .Spades)
84 | theAceOfSpades.description
85 |
86 | // To access the nested type, we can drill down into the type using type names:
87 | let heartsSymbol = String( BlackjackCard.Suit.Hearts.rawValue )
88 |
--------------------------------------------------------------------------------
/19. Nested Types.playground/timeline.xctimeline:
--------------------------------------------------------------------------------
1 |
2 |
4 |
5 |
7 |
8 |
9 |
10 |
--------------------------------------------------------------------------------
/1a. The Basics.playground/contents.xcplayground:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/1a. The Basics.playground/section-1.swift:
--------------------------------------------------------------------------------
1 | // ------------------------------------------------------------------------------------------------
2 | // Things to know:
3 | //
4 | // * Swift is Apple's new programming language for iOS and OSX. If you know C or Objective-C, then
5 | // these playgrounds should serve as a solid primer for making the switch to Swift.
6 | //
7 | // * Some experience programming in a C-like langauge is expected. If not, then I'm sorry but
8 | // you're just not the target audience.
9 | // ------------------------------------------------------------------------------------------------
10 |
11 | // ------------------------------------------------------------------------------------------------
12 | // Constants & Variables - These are known as "Stored Values" in Swift
13 |
14 | // Use the 'let' keyword to define a constant
15 | let maximumNumberOfLoginAttempts = 10
16 |
17 | // Use the 'var' keyword to define a variable
18 | //
19 | // Tip: only use variables when you need a stored value that changes. Otherwise, prefer constants.
20 | var currentLoginAttempt = 0
21 |
22 | // Constants cannot change. This line wouldn't compile:
23 | // maximumNumberOfLoginAttempts = 9
24 |
25 | // Variables can change:
26 | currentLoginAttempt += 1
27 |
28 | // You also can't redeclare a variable or constant once it has been declared. These lines
29 | // won't compile:
30 | // let maximumNumberOfLoginAttempts = 10
31 | // var currentLoginAttempt = "Some string which is not an Int"
32 |
33 | // You can combine them on a single line with a comma
34 | let a = 10, b = 20, c = 30
35 | var x = 0.0, y = 0.0, z = 0.0
36 |
37 | // Specifying the type with type annotations
38 | //
39 | // The built-in types in Swift are: Int, Double, Float, Bool, String, Array, Dictionary
40 | // There are variations of these (like UInt16), but those are the basic types. Note that all of
41 | // these types are capitalized.
42 | //
43 | // Because of inference (assuming 42 is an Int an "some text" is a String), type annotations are
44 | // usually pretty rare in Swift
45 | //
46 | // Here's how you specify the type. If we didn't specify the type as Double, tye type would have
47 | // been inferred to be Int.
48 | var SomeDouble: Double = 4
49 |
50 | // Constant & Variable names cannot contain any mathematical symbols, arrows private-use (or
51 | // invalid) Unicode code points or line-and-box drawing characters. Nor can they begin with a
52 | // number. Otherwise, it's open season for naming your variables! (yes, really!)
53 | //
54 | // Here are some oddly named, but perfectly valid, constants:
55 | let π = 3.14159
56 | let 你好 = "你好世界"
57 | let 🐶🐮 = "dogcow"
58 |
59 | // You can print a value using println
60 | let fiveHundred = 500
61 | println("The current value of fiveHundred is: \(fiveHundred)")
62 |
63 | // Since we're using Playgrounds, we'll just put the raw string on the line which is an expression
64 | // that evaluates to itself, printing the result in the right-hand pane in the playground, like so:
65 | "The current value of fiveHundred is: \(fiveHundred)"
66 |
67 | // ------------------------------------------------------------------------------------------------
68 | // A note about variable names
69 | //
70 | // As with most languages, you cannot give an identifier (such as a variable or constant name,
71 | // class name, etc.) the same name as a keyword. For example, you can't define a constant named
72 | // "let":
73 | //
74 | // The following line of code will not compile:
75 | //
76 | // let let = 0
77 | //
78 | // However, sometimes it would be convenient to do so and Swift provides a means to enable this
79 | // by surrounding the identifier with backticks (`). Here's an example:
80 | let `let` = 42.0
81 |
82 | // We can now use `let` like any normal variable:
83 | x = `let`
84 |
85 | // This works for any keyword:
86 | let `class` = "class"
87 | let `do` = "do"
88 | let `for` = "for"
89 |
90 | // Additionally, it's important to know that this works on non-colliding identifier names:
91 | let `myConstant` = 123.456
92 |
93 | // Also note that `myConstant` and myConstant refer to the same constant:
94 | myConstant
95 |
96 | // ------------------------------------------------------------------------------------------------
97 | // Comments
98 | //
99 | // You've probably already figured this out, but anything after the "//" is a comment. There's more
100 | // to comments, though:
101 |
102 | /* This is a comment
103 | that spans multiple lines */
104 |
105 | // The multi-line comments are handy because they can nest, which allows you to safely comment out
106 | // blocks of code, even if they have multi-line comments in them:
107 |
108 | /*
109 | // Some variable
110 | var someVar = 10
111 |
112 | /* A function
113 | *
114 | * This is a common way to comment functions, but it makes it difficult to comment out these
115 | * blocks.
116 | */
117 | func doSomething()
118 | {
119 | return
120 | }
121 | */
122 |
123 | // ------------------------------------------------------------------------------------------------
124 | // Semicolons
125 | //
126 | // Semicolons on the end of a line are optional, but the preferred style for Swift is to not use
127 | // them to terminate lines.
128 | var foo1 = 0
129 | var foo2 = 0; // optional semicolon
130 |
131 | // However, if you want to put two lines of code on one line, you'll need the semicolon to separate
132 | // them.
133 | foo1 = 1; foo2 = 2
134 |
135 | // ------------------------------------------------------------------------------------------------
136 | // Integers
137 | //
138 | // There are multiple types of integers. Signed and unsigned with sizes of 8, 16, 32 and 64 bits.
139 | // Here are a couple samples:
140 | let meaningOfLife: UInt8 = 42 // Unsigned 8-bit integer
141 | let randomNumber: Int32 = -34 // Signed 32-bit integer
142 |
143 | // There is also Int and UInt, the defaults. These will default to the size of the current
144 | // platform's native word size. On 32-bit platforms, Int will be equivalent to Int32/UInt32. On
145 | // 64-bit platforms it is equivalent to Int64/UInt64.
146 | //
147 | // Similarly, there is
148 | //
149 | // Tip: For code interoperability, prefer Int over its counterparts.
150 | let tirePressurePSI = 52
151 |
152 | // To find the bounds of any integer, try ".min" or ".max"
153 | UInt8.min
154 | UInt8.max
155 | Int32.min
156 | Int32.max
157 |
158 | // ------------------------------------------------------------------------------------------------
159 | // Floating point numbers
160 | //
161 | // Double is a 64-bit floating point numbers and Float is a 32-bit floating point number
162 | let pi: Double = 3.14159
163 | let pie: Float = 100 // ... becase it's 100% delicious!
164 |
165 | // ------------------------------------------------------------------------------------------------
166 | // Type Safety and Type Inference
167 | //
168 | // Swift is a strongly typed language, and as such, every stored value MUST have a type and can
169 | // only be used where that specific type is expected.
170 | //
171 | // Integer literals are inferred to be Int
172 | let someInt = 1234
173 |
174 | // Floating point literals are always inferred to be Double
175 | let someDouble = 1234.56
176 |
177 | // If you want a Float instead, you must use type annotation
178 | let someFloat: Float = 1234.56
179 |
180 | // String literals are inferred to be String type
181 | let someString = "This will be a String"
182 |
183 | // Here's a bool
184 | let someBool = true
185 |
186 | // These lines won't compile because we are specifying a type that doesn't match the given value
187 | // let someBool: Bool = 19
188 | // let someInteger: Int = "45"
189 | // let someOtherInt: Int = 45.6
190 |
191 | // ------------------------------------------------------------------------------------------------
192 | // Numeric literals
193 | //
194 | // You can specify numbers in a few interesting ways
195 | let decimalInteger = 17
196 | let binaryInteger = 0b10001 // 17 in binary notation
197 | let octalInteger = 0o21 // ...also 17 (Octal, baby!)
198 | let hexInteger = 0x11 // ...and 17 in Hexidecimal
199 |
200 | // Floating point numbers can be specified in a few different ways as well. Here are a few raw
201 | // examples (not assigned to variables):
202 | 1.25e2 // Scientific notation
203 | 1.25e-2
204 | 0xFp2 // Break this down into "0xF", "p", "2". Read as 15 (0xF) to the power of (p) 2, which is 60
205 | 0xFp-2
206 | 0xC.3p0
207 |
208 | // We can pad our literals as well:
209 | 000123.456 // Zero padding
210 | 0__123.456 // Underscores are just ignored
211 |
212 | // Numeric type conversion
213 |
214 | // A number that won't fit in the given type will not compile
215 | // let cannotBeNegative: UInt8 = -1
216 | // let tooBig: Int8 = Int8.max + 1
217 |
218 | // Since the default type for numeric values is Int, you need to specify a different type
219 | let simpleInt = 2_000 // Int
220 | let twoThousand: UInt16 = 2_000 // Specified as UInt16
221 | let one: UInt8 = 1 // Specified as UInt8
222 |
223 | // This will infer a UInt16 based on the types of both operands
224 | let twoThousandAndOne = twoThousand + UInt16(one)
225 |
226 | // Conversions between integer and floating point types must be made explicit
227 | let three = 3 // Inferred to be Int
228 | let pointOneFourOneFiveNine = 0.14159 // Inferred to be Double
229 | let doublePi = Double(three) + pointOneFourOneFiveNine // Explicit conversion of Int to Double
230 |
231 | // The inverse is also true - conversion from floating point to integer must be explicit
232 | //
233 | // Conversions to integer types from floating point simply truncate the fractional part. So
234 | // doublePi becomes 3 and -doublePi becomes -3
235 | let integerPi = Int(doublePi)
236 | let negativePi = Int(-doublePi)
237 |
238 | // Literal numerics work a little differently since the literal values don't have an explicit
239 | // type assigned to them. Their type is only inferred at the point they are evaluated.
240 | let someValue = 3 + 0.14159
241 |
--------------------------------------------------------------------------------
/1a. The Basics.playground/timeline.xctimeline:
--------------------------------------------------------------------------------
1 |
2 |
4 |
5 |
7 |
8 |
10 |
11 |
12 |
13 |
--------------------------------------------------------------------------------
/1b. Type alliases.playground/contents.xcplayground:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/1b. Type alliases.playground/section-1.swift:
--------------------------------------------------------------------------------
1 | // ------------------------------------------------------------------------------------------------
2 | // Things to know:
3 | //
4 | // * Type Aliases allow you to provide a different name for types,
5 | // similar to 'typedef' in C.
6 | // ------------------------------------------------------------------------------------------------
7 |
8 | // Create an alias for UInt16 called "AudioSample"
9 | typealias AudioSample = UInt16
10 |
11 | // This actually calls UInt16.min
12 | var maxAmplituedFound = AudioSample.min
13 |
14 | // We can also typealias custom types
15 | struct MySimpleStruct
16 | {
17 | static let a = 99
18 | }
19 |
20 | typealias MyAliasedName = MySimpleStruct
21 | MyAliasedName.a
22 |
23 |
--------------------------------------------------------------------------------
/1b. Type alliases.playground/timeline.xctimeline:
--------------------------------------------------------------------------------
1 |
2 |
4 |
5 |
7 |
8 |
9 |
10 |
--------------------------------------------------------------------------------
/1c. Tuples.playground/contents.xcplayground:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/1c. Tuples.playground/section-1.swift:
--------------------------------------------------------------------------------
1 | // ------------------------------------------------------------------------------------------------
2 | // Things to know:
3 | //
4 | // * Tuples are groups of values combined into a single, compound value
5 | // ------------------------------------------------------------------------------------------------
6 |
7 | // Defining a Tuple - use parenthesis around the comma-delimited list of values
8 | //
9 | // This Tuple doesn't specify types, so it relies on inference
10 | let httpError404 = (404, "Not found")
11 |
12 | // We can also specify the type in order to avoid inferrence
13 | let someOtherTuple = (Double(100), Bool(false))
14 |
15 | // Decomposing typles looks like this
16 | let (statusCode, statusMessage) = httpError404
17 | statusCode
18 | statusMessage
19 |
20 | // We can also decompose into variables instead of constants, but you probably figured that out
21 | var (varStatusCode, varStatusMessage) = httpError404
22 | varStatusCode
23 | varStatusMessage
24 |
25 | // You can also access them with the dot operator followed by their index:
26 | httpError404.0
27 | httpError404.1
28 |
29 | // Alternatively, you can name the elements of a Tuple
30 | let namedTuple = (statusCode: 404, message: "Not found")
31 |
32 | // When you name the elements you effectively assign names to their indices, so the dot operator
33 | // works with names or integers:
34 | namedTuple.statusCode == namedTuple.0
35 | namedTuple.message == namedTuple.1
36 |
37 |
--------------------------------------------------------------------------------
/1c. Tuples.playground/timeline.xctimeline:
--------------------------------------------------------------------------------
1 |
2 |
4 |
5 |
6 |
7 |
--------------------------------------------------------------------------------
/1d. Optionals.playground/contents.xcplayground:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/1d. Optionals.playground/section-1.swift:
--------------------------------------------------------------------------------
1 | // ------------------------------------------------------------------------------------------------
2 | // Things to know:
3 | //
4 | // * An optional value is a stored value that can either hold a value or "no value at all"
5 | //
6 | // * This is similar to assigning a stored value to "nil" but Optionals work with any type of
7 | // stored value, including Int, Bool, etc.
8 | // ------------------------------------------------------------------------------------------------
9 |
10 | // An optional declaration adds a "?" immediately after the explicit type. The following line
11 | // defines a value 'someOptional' that can either hold an Int or no value at all. In this case
12 | // we set an optional Int value to .None (similar to nil)
13 | let someOptional: Int? = .None
14 |
15 | // Let's try to convert a String to an Int
16 | //
17 | // Using the String's toInt() method, we'll try to convert a string to a numeric value. Since not
18 | // all strings can be converted to an Integer, the toInt() returns an optional, "Int?". This way
19 | // we can recognize failed conversions without having to trap exceptions or use other arcane
20 | // methods to recognize the failure.
21 | //
22 | // Here's an optional in action
23 | let notNumber = "abc"
24 | let failedConversion = notNumber.toInt()
25 |
26 | // Notice how failedConversion is 'nil', even though it's an Int
27 | failedConversion
28 |
29 | // Let's carry on with a successful conversion
30 | let possibleNumber = "123"
31 | var optionalConvertedNumber = possibleNumber.toInt()
32 |
33 | // This one worked
34 | optionalConvertedNumber
35 |
36 | // If we assign it to a constant, the type of that constant will be an Optional Int (Int?)
37 | let unwrapped = optionalConvertedNumber // 'unwrapped' is another optional
38 |
39 | // ------------------------------------------------------------------------------------------------
40 | // Alternate syntax for Optionals
41 | //
42 | // The use of a "?" for the syntax of an optional is syntactic sugar for the use of the Generic
43 | // Optional type defined in Swift's standard library. We haven't gotten into Generics yet, but
44 | // let's not let that stop us from learning this little detail.
45 | //
46 | // These two lines are of equivalent types:
47 | let optionalA: String? = .None
48 | let optionalB: Optional = .None
49 |
50 | // ------------------------------------------------------------------------------------------------
51 | // Unwrapping
52 | //
53 | // The difference between Int and Int? is important. Optionals essentially "wrap" their contents
54 | // which means that Int and Int? are two different types (with the latter being wrapped in an
55 | // optional.
56 | //
57 | // We can't explicity convert to an Int because that's not the same as an Int?. The following
58 | // line won't compile:
59 | //
60 | // let unwrappedInt: Int = optionalConvertedNumber
61 |
62 | // One way to do this is to "force unwrap" the value using the "!" symbol, like this:
63 | let unwrappedInt = optionalConvertedNumber!
64 |
65 | // Implicit unwrapping isn't very safe because if the optional doesn't hold a value, it will
66 | // generate a runtime error. To verify that is's safe, you can check the optional with an if
67 | // statement.
68 | if optionalConvertedNumber != .None
69 | {
70 | // It's now safe to force-unwrap because we KNOW it has a value
71 | let anotherUnwrappedInt = optionalConvertedNumber!
72 | }
73 | else
74 | {
75 | // The optional holds "no value"
76 | "Nothing to see here, go away"
77 | }
78 |
79 | // ------------------------------------------------------------------------------------------------
80 | // Optional Binding
81 | //
82 | // We can conditionally store the unwrapped value to a stored value if the optional holds a value.
83 | //
84 | // In the following block, we'll optionally bind the Int value to a constant named 'intValue'
85 | if let intValue = optionalConvertedNumber
86 | {
87 | // No need to use the "!" suffix as intValue is not optional
88 | intValue
89 |
90 | // In fact, since 'intValue' is an Int (not an Int?) we can't use the force-unwrap. This line
91 | // of code won't compile:
92 | // intValue!
93 | }
94 | else
95 | {
96 | // Note that 'intValue' doesn't exist in this "else" scope
97 | "No value"
98 | }
99 |
100 | // We can still use optional binding to bind to another optional value, if we do so explicitly
101 | // by specifying the type of the stored value that we're binding to.
102 | if let optionalIntValue:Int? = optionalConvertedNumber
103 | {
104 | // 'optionalIntValue' is still an optional, but it's known to be safe. We can still check
105 | // it here, though, because it's still an optional. If it weren't optional, this if statement
106 | // wouldn't compile:
107 | if optionalIntValue != .None
108 | {
109 | // 'optionalIntValue' is optional, so we still use the force-unwrap here:
110 | "intValue is optional, but has the value \(optionalIntValue!)"
111 | }
112 | }
113 |
114 | // Setting an optional to 'nil' sets it to be contain "no value"
115 | optionalConvertedNumber = nil
116 |
117 | // Now if we check it, we see that it holds no value:
118 | if optionalConvertedNumber != .None
119 | {
120 | "optionalConvertedNumber holds a value (\(optionalConvertedNumber))! (this should not happen)"
121 | }
122 | else
123 | {
124 | "optionalConvertedNumber holds no value"
125 | }
126 |
127 | // We can also try optional binding on an empty optional
128 | if let optionalIntValue = optionalConvertedNumber
129 | {
130 | "optionalIntValue holds a value (\(optionalIntValue))! (this should not happen)"
131 | }
132 | else
133 | {
134 | "optionalIntValue holds no value"
135 | }
136 |
137 | // Because of the introduction of optionals, you can no longer use nil for non-optional
138 | // variables or constants.
139 | //
140 | // The following line will not compile
141 | //
142 | // var failure: String = nil // Won't compile
143 |
144 | // The following line will compile, because the String is optional
145 | var noString: String? = nil
146 |
147 | // If we create an optional without an initializer, it is automatically initialized to nil. In
148 | // future sections, we'll learn that all values must be initialized before they are used. Because
149 | // of this behavior, this variable is considered initialized, even though it holds no value:
150 | var emptyOptional: String?
151 |
152 | // Another way to implicitly unwrap an optional is during declaration of a new stored value
153 | //
154 | // Here, we create an optional that we are pretty sure will always hold a value, so we give Swift
155 | // permission to automatically unwrap it whenever it needs to.
156 | //
157 | // Note the type, "String!"
158 | var assumedString: String! = "An implicitly unwrapped optional string"
159 |
160 | // Although assumedString is still an optional (and can be treated as one), it will also
161 | // automatically unwrap if we try to use it like a normal String.
162 | //
163 | // Note that we perform no unwrapping in order to assign the value to a normal String
164 | let copyOfAssumedString: String = assumedString
165 |
166 | // Since assumedString is still an optional, we can still set it to nil. This is dangerous to do
167 | // because we assumed it is always going to hold a value (and we gave permission to automatically
168 | // unwrap it.) So by doing this, we are taking a risk:
169 | assumedString = nil
170 |
171 | // BE SURE that your implicitly unwrapped optionals actually hold values!
172 | //
173 | // The following line will compile, but will generate a runtime error because of the automatic
174 | // unwrapping.
175 | //
176 | // let errorString: String = assumedString
177 |
178 | // Like any other optional, we can still check if it holds a value:
179 | if assumedString != nil
180 | {
181 | "We have a value"
182 | }
183 | else
184 | {
185 | "No value"
186 | }
187 |
--------------------------------------------------------------------------------
/1d. Optionals.playground/timeline.xctimeline:
--------------------------------------------------------------------------------
1 |
2 |
4 |
5 |
7 |
8 |
9 |
10 |
--------------------------------------------------------------------------------
/1e. Assertions.playground/contents.xcplayground:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/1e. Assertions.playground/section-1.swift:
--------------------------------------------------------------------------------
1 | // ------------------------------------------------------------------------------------------------
2 | // Things to know:
3 | //
4 | // * Assertions only trigger in debug mode and not in published builds
5 | //
6 | // * Assertions cause your app to terminate on the line and the debugger jumps to the line
7 | // ------------------------------------------------------------------------------------------------
8 |
9 | // Let's start with a value...
10 | let age = 3
11 |
12 | // You can assert with a message
13 | assert(age >= 0, "A person's age cannot be negative")
14 |
15 | // You can assert without the message
16 | assert(age >= 0)
17 |
18 |
--------------------------------------------------------------------------------
/1e. Assertions.playground/timeline.xctimeline:
--------------------------------------------------------------------------------
1 |
2 |
4 |
5 |
7 |
8 |
9 |
10 |
--------------------------------------------------------------------------------
/2. Basic operations.playground/contents.xcplayground:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/2. Basic operations.playground/section-1.swift:
--------------------------------------------------------------------------------
1 | // ------------------------------------------------------------------------------------------------
2 | // Things to know:
3 | //
4 | // * Assuming knowledge of C here, so a lot will be left out that is the the same, such as
5 | // "let i = 1 + 2"
6 | //
7 | // * Unary operators work on a single target. Of them, you have prefix operators such as !b or
8 | // postfix operators such as i++.
9 | //
10 | // * Binary operators work on two targets and are infix operators because they go between two
11 | // values, such as a + b
12 | //
13 | // * Ternary operators work on three targets. There is only one ternary: a ? b : c.
14 | // ------------------------------------------------------------------------------------------------
15 |
16 | // We have our standard assignment operator (=). Note that the assignment operator in Swift does
17 | // not actually return a value, so the statment "if (x = y) {}" will not work. This helps prevent
18 | // accidental assignments during conditionals.
19 | var a = 10.0
20 | var b = 3.0
21 |
22 | // Assignment can also take multiple values (for Tuples):
23 | let (x, y) = (5, 6)
24 | x
25 | y
26 |
27 | // Aside from the standard mathematics operators (+, -, /, *), there is also the remainder operator
28 | // (%) which is not to be confused with modulo. They work differently because of the way remainders
29 | // are calculated for negative numbers and can be used with floating point values.
30 | var c = a / b // Floatng point result
31 | var d = a % b // Floating point remainder
32 |
33 | // ------------------------------------------------------------------------------------------------
34 | // Range operators
35 | //
36 | // The range operator with two dots means up to but NOT including the final value.
37 | //
38 | // This is called the "Half-Closed Range Operator"
39 | for i in 1..<10
40 | {
41 | i // prints 1 through 9
42 | }
43 |
44 | // The range operator with three dots is inclusive with last value like
45 | //
46 | // This is called the "Closed Range Operator"
47 | for i in 1...10
48 | {
49 | i // prints 1 through 10
50 | }
51 |
52 | // ------------------------------------------------------------------------------------------------
53 | // Unary, Binary and Ternary operators
54 | //
55 | // Unary prefix operators appear before their taget. Here we increment a then negate it:
56 | ++a
57 | a = -a
58 |
59 | // You can also use the uniary + operator, though it doesn't do anything
60 | a = +a
61 |
62 | // We have the compound assigment operator
63 | a += 10
64 |
65 | // The logical NOT
66 | var truefalse = true
67 | truefalse = !truefalse
68 |
69 | // Unary postfix operators appear after their target: i++
70 | a--
71 | a
72 |
73 | // Binary operators are infix because they appear between to targets
74 | a + b
75 |
76 | // String concatenation uses the + operator:
77 | "hello, " + "world"
78 |
79 | // To add characters, convert them to a string
80 | let dog: Character = "🐶"
81 | let cow: Character = "🐮"
82 | let dogCow = String(dog) + String(cow)
83 |
84 | // Ternary operators work on three targets:
85 | truefalse ? a : b
86 |
87 | // ------------------------------------------------------------------------------------------------
88 | // Identity operators
89 | //
90 | // We can test if the object reference refers to the same instance of an object (as opposed to two
91 | // objects that are "equivalent" based on some compare logic.) We do this with the === operator:
92 | class myclass {}
93 | var c1 = myclass()
94 | var c2 = myclass()
95 | c1 === c2
96 | c1 === c1
97 |
98 | // String comparisons are case sensitive
99 | "abc" == "abc"
100 | "abc" == "ABC"
101 |
102 | // ------------------------------------------------------------------------------------------------
103 | // Logical operators
104 | //
105 | // Comparisons use the logical operators with AND, OR and NOT
106 | if (true && false) || !(false && true)
107 | {
108 | "true"
109 | }
110 |
--------------------------------------------------------------------------------
/2. Basic operations.playground/timeline.xctimeline:
--------------------------------------------------------------------------------
1 |
2 |
4 |
5 |
7 |
8 |
9 |
10 |
--------------------------------------------------------------------------------
/20. Extensions.playground/contents.xcplayground:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/20. Extensions.playground/section-1.swift:
--------------------------------------------------------------------------------
1 | // ------------------------------------------------------------------------------------------------
2 | // Things to know:
3 | //
4 | // * Similar to Objective-C categories, extensions allow you to add functionality to an existing
5 | // type (class, struct, enumeration.)
6 | //
7 | // * You do not need access to the original source code to extend a type.
8 | //
9 | // * Extensions can be applied to built-in types as well, including String, Int, Double, etc.
10 | //
11 | // * With extensions, you can:
12 | //
13 | // o Add computed properties (including static)
14 | // o Define instance methods and type methods
15 | // o Provide new convenience initializers
16 | // o Define subscripts
17 | // o Define and use new nested types
18 | // o Make an existing type conform to a protocol
19 | //
20 | // * Extensions do not support adding stored properties or property observers to a type.
21 | //
22 | // * Extensions apply all instances of a type, even if they were created before the extension was
23 | // defined.
24 | // ------------------------------------------------------------------------------------------------
25 |
26 | // Let's take a look at how extensions are declared. Note that, unlike Objective-C categories,
27 | // extensions are not named:
28 | extension Int
29 | {
30 | // ... code here
31 | }
32 |
33 | // ------------------------------------------------------------------------------------------------
34 | // Computed properties
35 | //
36 | // Computed properties are a poweful use of extensions. Below, we'll add native conversion from
37 | // various measurements (Km, mm, feet, etc.) to the Double class.
38 | extension Double
39 | {
40 | var kmToMeters: Double { return self * 1_000.0 }
41 | var cmToMeters: Double { return self / 100.0 }
42 | var mmToMeters: Double { return self / 1_000.0 }
43 | var inchToMeters: Double { return self / 39.3701 }
44 | var ftToMeters: Double { return self / 3.28084 }
45 | }
46 |
47 | // We can call upon Double's new computed property to convert inches to meters
48 | let oneInchInMeters = 1.inchToMeters
49 |
50 | // Similarly, we'll convert three feet to meters
51 | let threeFeetInMeters = 3.ftToMeters
52 |
53 | // ------------------------------------------------------------------------------------------------
54 | // Initializers
55 | //
56 | // Extensions can be used to add new convenience initializers to a class, but they cannot add
57 | // new designated initializers.
58 | //
59 | // Let's see this in action:
60 | struct Size
61 | {
62 | var width = 0.0
63 | var height = 0.0
64 | }
65 | struct Point
66 | {
67 | var x = 0.0
68 | var y = 0.0
69 | }
70 | struct Rect
71 | {
72 | var origin = Point()
73 | var size = Size()
74 | }
75 |
76 | // Since we didn't provide any initializers, we can use Swift's default memberwise initializer for
77 | // the Rect.
78 | var memberwiseRect = Rect(origin: Point(x: 2.0, y: 2.0), size: Size(width: 5.0, height: 5.0))
79 |
80 | // Let's extend Rect to add a new convenience initializer. Note that we're still responsible for
81 | // ensuring that the instance is fully initialized.
82 | extension Rect
83 | {
84 | init (center: Point, size: Size)
85 | {
86 | let originX = center.x - (size.width / 2)
87 | let originY = center.y - (size.height / 2)
88 | self.init(origin: Point(x: originX, y: originY), size: size)
89 | }
90 | }
91 |
92 | // Let's try out our new initializer:
93 | let centerRect = Rect(center: Point(x: 4.0, y: 4.0), size: Size(width: 3.0, height: 3.0))
94 |
95 | // Remember that if a class has an initializer, Swift will not provide the default memberwise
96 | // initializer. However, since we added an initializer via an Extension, we still have access
97 | // to Swift's memberwise initializer:
98 | var anotherRect = Rect(origin: Point(x: 1.0, y: 1.0), size: Size(width: 3.0, height: 2.0))
99 |
100 | // ------------------------------------------------------------------------------------------------
101 | // Methods
102 | //
103 | // As you might expect, we can add methods to an existing type as well. Here's a clever little
104 | // extention to perform a task (a closure) multiple times, equal to the value stored in the Int.
105 | //
106 | // Note that the integer value is stored in 'self'.
107 | extension Int
108 | {
109 | func repititions(task: () -> ())
110 | {
111 | for i in 0.. Int
142 | {
143 | var decimalBase = 1
144 | for _ in 0 ..< digitIndex
145 | {
146 | decimalBase *= 10
147 | }
148 | return self / decimalBase % 10
149 | }
150 | }
151 |
152 | // And we can call our subscript directly on an Int, including a literal Int type:
153 | 123456789[0]
154 | 123456789[1]
155 | 123456789[2]
156 | 123456789[3]
157 | 123456789[4]
158 | 123456789[5]
159 | 123456789[6]
160 |
161 | // ------------------------------------------------------------------------------------------------
162 | // Nested types
163 | //
164 | // We can also add nested types to an existing type:
165 | extension Character
166 | {
167 | enum Kind
168 | {
169 | case Vowel, Consonant, Other
170 | }
171 | var kind: Kind
172 | {
173 | switch String(self)
174 | {
175 | case "a", "e", "i", "o", "u":
176 | return .Vowel
177 | case "b", "c", "d", "f", "g", "h", "j", "k", "l", "m",
178 | "n", "p", "q", "r", "s", "t", "v", "w", "x", "y", "z":
179 | return .Consonant
180 | default:
181 | return .Other
182 | }
183 | }
184 | }
185 |
186 | // Let's test out our new extension with nested types:
187 | Character("a").kind == .Vowel
188 | Character("h").kind == .Consonant
189 | Character("+").kind == .Other
190 |
--------------------------------------------------------------------------------
/20. Extensions.playground/timeline.xctimeline:
--------------------------------------------------------------------------------
1 |
2 |
4 |
5 |
7 |
8 |
9 |
10 |
--------------------------------------------------------------------------------
/21. Protocols.playground/contents.xcplayground:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/21. Protocols.playground/section-1.swift:
--------------------------------------------------------------------------------
1 | // ------------------------------------------------------------------------------------------------
2 | // Things to know:
3 | //
4 | // * Protocols define a required set of functionality (including methods and properties) for a
5 | // class, structure or enumeration.
6 | //
7 | // * Protocols are "adopted" by a class, structure or enumeration to provide the implementation.
8 | // This is called "conforming" to a protocol.
9 | //
10 | // * As you should already be aware, protocols are often used for delegation.
11 | //
12 | // * Protcol Syntax looks like this:
13 | //
14 | // protocol SomeProtocol
15 | // {
16 | // // protocol definition goes here
17 | // }
18 | //
19 | // * Protocol adoption looks like this:
20 | //
21 | // struct SomeStructure: FirstProtocol, AnotherProtocol
22 | // {
23 | // // structure definition goes here
24 | // }
25 | //
26 | // class SomeClass: SomeSuperclass, FirstProtocol, AnotherProtocol
27 | // {
28 | // // class definition goes here
29 | // }
30 | // ------------------------------------------------------------------------------------------------
31 |
32 | // ------------------------------------------------------------------------------------------------
33 | // Property requirements
34 | //
35 | // * A protocol can require the conforming type to provide an instance of a property or type
36 | // property.
37 | //
38 | // * The protocol can also specify if the property must be gettable or gettable and
39 | // settable. If a protocol only requires a gettable property, the conforming class can use
40 | // a stored property or a computed property. Also, the conforming class is allowed to add
41 | // a setter if it needs.
42 | //
43 | // * Property requirements are always declared as variable types with the 'var' introducer.
44 | //
45 | // Let's take a look at a simple protocol. As you'll see, we only need to define the properties
46 | // in terms of 'get' and 'set' and do not provide the actual functionality.
47 | protocol someProtocolForProperties
48 | {
49 | // A read/write property
50 | var mustBeSettable: Int { get set }
51 |
52 | // A read-only property
53 | var doesNotNeedToBeSettable: Int { get }
54 |
55 | // A type property always uses 'class'. This is the case even if adopted by a structure or
56 | // enumeration which will use 'static' when conforming to the protocol's property.
57 | class var someTypeProperty: Int { get set }
58 | }
59 |
60 | // Let's create a more practical protocol that we can actually conform to:
61 | protocol FullyNamed
62 | {
63 | var fullName: String { get }
64 | }
65 |
66 | // A structure that conforms to FullyNamed. We won't bother creating an explicit getter or setter
67 | // for the 'fullName' property because Swift's defaults will suffice.
68 | struct Person: FullyNamed
69 | {
70 | var fullName: String
71 | }
72 |
73 | let john = Person(fullName: "John Smith")
74 |
75 | // Let's try a more complex class
76 | class Starship: FullyNamed
77 | {
78 | var prefix: String?
79 | var name: String
80 | init(name: String, prefix: String? = nil)
81 | {
82 | self.name = name
83 | self.prefix = prefix
84 | }
85 |
86 | var fullName: String
87 | {
88 | return (prefix != .None ? prefix! + " " : "") + name
89 | }
90 | }
91 |
92 | // In the class above, we use a 'name' and an optional 'prefix' to represent the full name, then
93 | // provide a computed value to fulfill our 'fullName' requirement.
94 | //
95 | // Here it is in action:
96 | var ncc1701 = Starship(name: "Enterprise", prefix: "USS")
97 | ncc1701.fullName
98 |
99 | // ------------------------------------------------------------------------------------------------
100 | // Method Requirements
101 | //
102 | // Similar to property requirements, the protocol only defines the method type, not the
103 | // implementation.
104 | //
105 | // Variadic parameters are allowed, but default values are not.
106 | //
107 | // Method requirements can be mutating - simply use the 'mutating' keyword before the function
108 | // definition. Note that when conforming to the protocol, only classes are required to use the
109 | // 'mutating' keyword when conforming to the protocol, following the normal procedure.
110 | //
111 | // As with property requirements, type methods are always prefixed with the 'class' keyword.
112 | //
113 | // Here's a simple example that defines the requirements for a 'random' function which takes
114 | // no parameters and returns a Double:
115 | protocol RandomNumberGenerator
116 | {
117 | func random() -> Double
118 | }
119 |
120 | // Let's adopt this protocol:
121 | class LinearCongruentialGenerator: RandomNumberGenerator
122 | {
123 | var lastRandom = 42.0
124 | var m = 139956.0
125 | var a = 3877.0
126 | var c = 29573.0
127 | func random() -> Double
128 | {
129 | lastRandom = ((lastRandom * a + c) % m)
130 | return lastRandom / m
131 | }
132 | }
133 | let generator = LinearCongruentialGenerator()
134 | generator.random()
135 | generator.random()
136 | generator.random()
137 |
138 | // ------------------------------------------------------------------------------------------------
139 | // Protocols as Types
140 | //
141 | // Protocols are types, which means they can be used where many other types are allowed,
142 | // including:
143 | //
144 | // * As a parameter to a function, method or initializer
145 | // * As the type of a constant, variable or property
146 | // * As the type of items in an Array, Dictionary or other container
147 | //
148 | // Here's a protocol used as a type. We'll create a Dice class that stores a RandomNumberGenerator
149 | // as a constant so that it can be used to customize the randomization of the dice roll:
150 | class Dice
151 | {
152 | let sides: Int
153 | let generator: RandomNumberGenerator
154 | init(sides: Int, generator: RandomNumberGenerator)
155 | {
156 | self.sides = sides
157 | self.generator = generator
158 | }
159 |
160 | func roll() -> Int
161 | {
162 | return Int(generator.random() * Double(sides)) + 1
163 | }
164 | }
165 |
166 | // Just for fun, let's roll a few:
167 | let d6 = Dice(sides: 6, generator: generator)
168 | d6.roll()
169 | d6.roll()
170 | d6.roll()
171 | d6.roll()
172 | d6.roll()
173 | d6.roll()
174 |
175 | // ------------------------------------------------------------------------------------------------
176 | // Adding Protocol Conformance with an Extension
177 | //
178 | // Existing classes, structures and enumerations can be extended to conform to a given protocol.
179 | //
180 | // Let's start with a simple protocol we'll use for expirimentation:
181 | protocol TextRepresentable
182 | {
183 | func asText() -> String
184 | }
185 |
186 | // We'll extend our Dice class:
187 | extension Dice: TextRepresentable
188 | {
189 | func asText() -> String
190 | {
191 | return "A \(sides)-sided dice"
192 | }
193 | }
194 |
195 | // Existing instances (such as 'd6' will automatically adopt and conform to the new protocol, even
196 | // though it was declared prior to the extension.
197 | d6.asText()
198 |
199 | // ------------------------------------------------------------------------------------------------
200 | // Declaring Protocol Adoption with an Extension
201 | //
202 | // Some types may already adopt a protocol, but do not state so in their definition. Types do not
203 | // automatically adopt to protocols whose requirements they satisfy - protocol adoption must
204 | // always be stated explicitly.
205 | //
206 | // This can be resolved with by using an empty extension.
207 | //
208 | // To showcase this, let's create a structure that conforms to the TextRepresentable protocol, but
209 | // doesn't include this as part of the definition:
210 | struct Hamster
211 | {
212 | var name: String
213 | func asText() -> String
214 | {
215 | return "A hamster named \(name)"
216 | }
217 | }
218 |
219 | // Let's verify our work:
220 | let tedTheHamster = Hamster(name: "Ted")
221 | tedTheHamster.asText()
222 |
223 | // We can add TextRepresentable conformance to Hamster by using an empty extension.
224 | //
225 | // This works because the requirements are already met, so we don't need to include any additional
226 | // functionality within the extension definition.
227 | extension Hamster: TextRepresentable
228 | {
229 |
230 | }
231 |
232 | // ------------------------------------------------------------------------------------------------
233 | // Collections of Protocol Types
234 | //
235 | // Hamsters and Dice don't have much in common, but in our sample code above, they both conform
236 | // to the TextRepresentable protocol. Because of this, we can create an array of things that are
237 | // TextRepresentable which includes each:
238 | let textRepresentableThigns: [TextRepresentable] = [d6, tedTheHamster]
239 |
240 | // We can now loop through each and produce its text representation:
241 | for thing in textRepresentableThigns
242 | {
243 | thing.asText()
244 | }
245 |
246 | // ------------------------------------------------------------------------------------------------
247 | // Protocol Inheritance
248 | //
249 | // Protocols can inherit from other protocols in order to add further requirements. The syntax
250 | // for this is similar to a class ineriting from its superclass.
251 | //
252 | // Let's create a new text representable type, inherited from TextRepresentable:
253 | protocol PrettyTextRepresentable: TextRepresentable
254 | {
255 | func asPrettyText() -> String
256 | }
257 |
258 | // Let's make our Dice a little prettier.
259 | //
260 | // Note that the Dice already knows that it conforms to the TextRepresentable, which means we can
261 | // call asText() inside our asPrettyText() method.
262 | extension Dice: PrettyTextRepresentable
263 | {
264 | func asPrettyText() -> String
265 | {
266 | return "The pretty version of " + asText()
267 | }
268 | }
269 |
270 | // We can test our work:
271 | d6.asPrettyText()
272 |
273 | // ------------------------------------------------------------------------------------------------
274 | // Protocol Composition
275 | //
276 | // Protocols can be combined such that a type conforms to each of them. For example, a person can
277 | // be an aged person as well as a named person.
278 | //
279 | // Protocol Composition is a syntactic method of declaring that an instance conforms to a set
280 | // of protocols. It takes the form "protocol". There can be as
281 | // many protocols between the angle brackets as you need.
282 | //
283 | // Let's start by creating a couple of protocols for expirimentation:
284 | protocol Named
285 | {
286 | var name: String { get }
287 | }
288 | protocol Aged
289 | {
290 | var age: Int { get }
291 | }
292 |
293 | // Here, we declare an Individual that conforms to both Name and Age protocols:
294 | struct Individual: Named, Aged
295 | {
296 | var name: String
297 | var age: Int
298 | }
299 |
300 | // Here, we can see the protocol composition at work as the parameter into the wishHappyBirthday()
301 | // function:
302 | func wishHappyBirthday(celebrator: protocol) -> String
303 | {
304 | return "Happy Birthday \(celebrator.name) - you're \(celebrator.age)!"
305 | }
306 |
307 | // If we call the member, we can see the celebratory wish for this individual:
308 | wishHappyBirthday(Individual(name: "Bill", age: 31))
309 |
310 | // ------------------------------------------------------------------------------------------------
311 | // Checking for Protocol Conformance
312 | //
313 | // We can use 'is' and 'as' for testing for protocol conformance, just as we've seen in the
314 | // section on Type Casting.
315 | //
316 | // In order for this to work with protocols, they must be marked with an "@objc" attribute. See
317 | // further down in this playground for a special note about the @objc attribute.
318 | //
319 | // Let's create a new protocol with the proper prefix so that we can investigate:
320 | @objc protocol HasArea
321 | {
322 | var area: Double { get }
323 | }
324 |
325 | class Circle: HasArea
326 | {
327 | let pi = 3.14159
328 | var radius: Double
329 | var area: Double { return pi * radius * radius }
330 | init(radius: Double) { self.radius = radius }
331 | }
332 | class Country: HasArea
333 | {
334 | var area: Double
335 | init(area: Double) { self.area = area }
336 | }
337 | class Animal
338 | {
339 | var legs: Int
340 | init(legs: Int) { self.legs = legs }
341 | }
342 |
343 | // We can store our objects into an array of type AnyObject[]
344 | let objects: [AnyObject] =
345 | [
346 | Circle(radius: 3.0),
347 | Country(area: 4356947.0),
348 | Animal(legs: 4)
349 | ]
350 |
351 | // Then we can test each for conformance to HasArea:
352 | objects[0] is HasArea
353 | objects[1] is HasArea
354 | objects[2] is HasArea
355 |
356 | // ------------------------------------------------------------------------------------------------
357 | // Optional Protocol Requirements
358 | //
359 | // Sometimes it's convenient to declare protocols that have one or more requirements that are
360 | // optional. This is done by prefixing those requirements with the 'optional' keyword.
361 | //
362 | // The term "optional protocol" refers to protocols that are optional in a very similar since to
363 | // optionals we've seen in the past. However, rather than stored values that can be nil, they
364 | // can still use optional chaining and optional binding for determining if an optional requirement
365 | // has been satisfied and if so, using that requirement.
366 | //
367 | // As with Protocol Conformance, a protocol that uses optional requirements must also be prefixed
368 | // with the '@objc' attribute.
369 | //
370 | // A special note about @objc attribute:
371 | //
372 | // * In order to check if an instance of a class conforms to a given protocol, that protocol must
373 | // be declared with the @objc attribute.
374 | //
375 | // * This is also the case with optional requirements in protocols. In order to use the optional
376 | // declaration modifier, the protocol must be declared with the @objc attribute.
377 | //
378 | // * Additionally, any class, structure or enumeration that owns an instance that conforms to a
379 | // protocol declared with the @objc attribute must also be declared with the @objc attribute.
380 | //
381 | // Here's another simple protocol that uses optional requrements:
382 | @objc protocol CounterDataSource
383 | {
384 | optional func incrementForCount(count: Int) -> Int
385 | optional var fixedIncrement: Int { get }
386 | }
387 |
388 | // In the class below, we'll see that checking to see if an instance conforms to a specific
389 | // requirement is similar to checking for (and accessing) optionals. We'll use optional chaining
390 | // for these optional reqirements:
391 | @objc class Counter
392 | {
393 | var count = 0
394 | var dataSource: CounterDataSource?
395 | func increment()
396 | {
397 | // Does the dataSource conform to the incrementForCount method?
398 | if let amount = dataSource?.incrementForCount?(count)
399 | {
400 | count += amount
401 | }
402 | // If not, does it conform to the fixedIncrement variable requirement?
403 | else if let amount = dataSource?.fixedIncrement?
404 | {
405 | count += amount
406 | }
407 | }
408 | }
409 |
--------------------------------------------------------------------------------
/21. Protocols.playground/timeline.xctimeline:
--------------------------------------------------------------------------------
1 |
2 |
4 |
5 |
7 |
8 |
9 |
10 |
--------------------------------------------------------------------------------
/22. Generics.playground/contents.xcplayground:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/22. Generics.playground/section-1.swift:
--------------------------------------------------------------------------------
1 | // ------------------------------------------------------------------------------------------------
2 | // Things to know:
3 | //
4 | // * Generics allow flexible, reusable functions and types that can work with any type, subject
5 | // to restrictions that you define.
6 | //
7 | // * Swift's Array and Dictionary are both Generics.
8 | //
9 | // * Generics can be applied to Functions, Structures, Classes and Enumerations.
10 | // ------------------------------------------------------------------------------------------------
11 |
12 | // The problem that Generics solve
13 | //
14 | // Consider the following function which can swap two Ints.
15 | func swapTwoInts(inout a: Int, inout b: Int)
16 | {
17 | let tmp = a
18 | a = b
19 | b = tmp
20 | }
21 |
22 | // What if we wanted to swap Strings? Or any other type? We would need to write a lot of different
23 | // swap functions. Instead, let's use Generics. Consider the following generic function:
24 | func swapTwoValues(inout a: T, inout b: T)
25 | {
26 | let tmp = a
27 | a = b
28 | b = tmp
29 | }
30 |
31 | // The 'swapTwoValues()' function is a generic function in a sense that some or all of the types
32 | // that it works on are generic (i.e., specific only to the calls placed on that function.)
33 | //
34 | // Study the first line of the function and notice the use of and the type for 'a' and 'b' as
35 | // type T. In this case, T is just a placeholder for a type and by studying this function, we can
36 | // see that both 'a' and 'b' are the same type.
37 | //
38 | // If we call this function with two Integers, it will treat the function as one declared to accept
39 | // two Ints, but if we pass in two Strings, it will work on Strings.
40 | //
41 | // If we study the body of the function, we'll see that it is coded in such a way as to work with
42 | // any type passed in: The 'tmp' parameter's type is inferred by the value 'a' and 'a' and 'b' must
43 | // be assignable. If any of this criteria are not met, a compilation error will appear for the
44 | // function call the tries to call swapTwoValues() with a type that doesn't meet this criteria.
45 | //
46 | // Although we're using T as the name for our type placeholder, we can use any name we wish, though
47 | // T is a common placeholder for single type lists. If we were to create a new implementation of
48 | // the Dictionary class, we would want to use two type parameters and name them effectively, such
49 | // as .
50 | //
51 | // A type placholder can also be used to define the return type.
52 | //
53 | // Let's call it a few times to see it in action:
54 | var aInt = 3
55 | var bInt = 4
56 | swapTwoValues(&aInt, &bInt)
57 | aInt
58 | bInt
59 |
60 | var aDouble = 3.3
61 | var bDouble = 4.4
62 | swapTwoValues(&aDouble, &bDouble)
63 | aDouble
64 | bDouble
65 |
66 | var aString = "three"
67 | var bString = "four"
68 | swapTwoValues(&aString, &bString)
69 | aString
70 | bString
71 |
72 | // ------------------------------------------------------------------------------------------------
73 | // Generic Types
74 | //
75 | // So far we've seen how to apply Generics to a function, let's see how they can be applied to
76 | // a struct. We'll define a standard 'stack' implementation which works like an array that can
77 | // "push" an element to the end of the array, or "pop" an element off of the end of the array.
78 | //
79 | // As you can see, the type placeholder, once defined for a struct, can be used anywhere in that
80 | // struct to represent the given type. In the code below, the the type placeholder is used as the
81 | // type for a property, the input parameter for a method and the return value for a method.
82 | struct Stack
83 | {
84 | var items = [T]()
85 | mutating func push(item: T)
86 | {
87 | items.append(item)
88 | }
89 | mutating func pop() -> T
90 | {
91 | return items.removeLast()
92 | }
93 | }
94 |
95 | // Let's use our new Stack:
96 | var stackOfStrings = Stack()
97 |
98 | stackOfStrings.push("uno")
99 | stackOfStrings.push("dos")
100 | stackOfStrings.push("tres")
101 | stackOfStrings.push("cuatro")
102 |
103 | stackOfStrings.pop()
104 | stackOfStrings.pop()
105 | stackOfStrings.pop()
106 | stackOfStrings.pop()
107 |
108 | // ------------------------------------------------------------------------------------------------
109 | // Type constraints
110 | //
111 | // So far, our type parameters are completely Generic - they can represent any given type.
112 | // Sometimes we may want to apply constraints to those types. For example, the Swift Dictionary
113 | // type uses generics and places a constraint on the key's type that it must be hashable (i.e., it
114 | // must conform to the Hashable protocol, defined in the Swift standard library.)
115 | //
116 | // Constraints are defined with the following syntax:
117 | func doSomethingWithKeyValue(someKey: KeyType, someValue: ValueType)
118 | {
119 | // Our keyType is known to be a Hashable, so we can use the hashValue defined by that protocol
120 | // shown here:
121 | someKey.hashValue
122 |
123 | // 'someValue' is an unknown type to us, we'll just drop it here in case it's ever used so we
124 | // can see the value
125 | someValue
126 | }
127 |
128 | // Let's see type constraints in action. We'll create a function that finds a given value within
129 | // an array and returns an optional index into the array where the first element was found.
130 | //
131 | // Take notice the constraint "Equatable" on the type T, which is key to making this function
132 | // compile. Without it, we would get an error on the conditional statement used to compare each
133 | // element from the array with the value being searched for. By including the Equatable, we tell
134 | // the generic function that it is guaranteed to receive only values that meet that specific
135 | // criteria.
136 | func findIndex(array: [T], valueToFind: T) -> Int?
137 | {
138 | for (index, value) in enumerate(array)
139 | {
140 | if value == valueToFind
141 | {
142 | return index
143 | }
144 | }
145 | return nil
146 | }
147 |
148 | // Let's try a few different inputs
149 | let doubleIndex = findIndex([3.14159, 0.1, 0.25], 9.3)
150 | let stringIndex = findIndex(["Mike", "Malcolm", "Andrea"], "Andrea")
151 |
152 | // ------------------------------------------------------------------------------------------------
153 | // Associated types
154 | //
155 | // Protocols use a different method of defining generic types, called Associated Types, which use
156 | // type inference combined with Type Aliases.
157 | //
158 | // Let's jump right into some code:
159 | protocol Container
160 | {
161 | typealias ItemType
162 | mutating func append(item: ItemType)
163 | var count: Int { get }
164 | subscript(i: Int) -> ItemType { get }
165 | }
166 |
167 | // In the example above, we declare a Type Alias called ItemType, but because we're declaring
168 | // a protocol, we're only declaring the requirement for the conforming target to provide the
169 | // actual type alias.
170 | //
171 | // With Generics, the type of ItemType can actually be inferred, such that it provides the correct
172 | // types for the append() and the subscript implementations.
173 | //
174 | // Let's see this in action as we turn our Stack into a container:
175 |
176 | struct StackContainer : Container
177 | {
178 | // Here we find our original stack implementation, unmodified
179 |
180 | var items = [T]()
181 | mutating func push(item: T)
182 | {
183 | items.append(item)
184 | }
185 | mutating func pop() -> T
186 | {
187 | return items.removeLast()
188 | }
189 |
190 | // Below, we conform to the protocol
191 |
192 | mutating func append(item: T)
193 | {
194 | self.push(item)
195 | }
196 | var count: Int
197 | {
198 | return items.count
199 | }
200 | subscript(i: Int) -> T
201 | {
202 | return items[i]
203 | }
204 | }
205 |
206 | // The new StackContainer is now ready to go. You may notice that it does not include the
207 | // typealias that was required as part of the Container protocol. This is because the all of the
208 | // places where an ItemType would be used are using T. This allows Swift to perform a backwards
209 | // inferrence that ItemType must be a type T, and it allows this requirement to be met.
210 | //
211 | // Let's verify our work:
212 | var stringStack = StackContainer()
213 | stringStack.push("Albert")
214 | stringStack.push("Andrew")
215 | stringStack.push("Betty")
216 | stringStack.push("Jacob")
217 | stringStack.pop()
218 | stringStack.count
219 |
220 | var doubleStack = StackContainer()
221 | doubleStack.push(3.14159)
222 | doubleStack.push(42.0)
223 | doubleStack.push(1_000_000)
224 | doubleStack.pop()
225 | doubleStack.count
226 |
227 | // We can also extend an existing types to conform to our new generic protocol. As it turns out
228 | // Swift's built-in Array class already supports the requirements of our Container.
229 | //
230 | // Also, since the protocol's type inferrence method of implementing Generics, we can extend
231 | // String without the need to modify String other than to extend it to conform to the protocol:
232 | extension Array: Container {}
233 |
234 | // ------------------------------------------------------------------------------------------------
235 | // Where Clauses
236 | //
237 | // We can further extend our constraints on a type by including where clauses as part of a type
238 | // parameter list. Where clauses provide a means for more constraints on associated types and/or
239 | // one or more equality relationships between types and associated types.
240 | //
241 | // Let's take a look at a where clause in action. We'll define a function that works on two
242 | // different containers that that must contain the same type of item.
243 | func allItemsMatch
244 |
245 | (someContainer: C1, anotherContainer: C2) -> Bool
246 | {
247 | // Check that both containers contain the same number of items
248 | if someContainer.count != anotherContainer.count
249 | {
250 | return false
251 | }
252 |
253 | // Check each pair of items to see if they are equivalent
254 | for i in 0..
2 |
4 |
5 |
7 |
8 |
9 |
10 |
--------------------------------------------------------------------------------
/23. Advanced Operators.playground/contents.xcplayground:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/23. Advanced Operators.playground/section-1.swift:
--------------------------------------------------------------------------------
1 | // ------------------------------------------------------------------------------------------------
2 | // Things to know:
3 | //
4 | // * Arithmetic operators in Swift do not automatically overflow. Adding two values that overflow
5 | // their type (for example, storing 300 in a UInt8) will cause an error. There are special
6 | // operators that allow overflow, including dividing by zero.
7 | //
8 | // * Swift allows developers to define their own operators, including those that Swift doesn't
9 | // currently define. You can even specify the associativity and precedence for operators.
10 | // ------------------------------------------------------------------------------------------------
11 |
12 | // ------------------------------------------------------------------------------------------------
13 | // Bitwise Operators
14 | //
15 | // The Bitwise operators (AND, OR, XOR, etc.) in Swift effeectively mirror the functionality that
16 | // you're used to with C++ and Objective-C.
17 | //
18 | // We'll cover them briefly. The odd formatting is intended to help show the results:
19 | var andResult: UInt8 =
20 | 0b00101111 &
21 | 0b11110100
22 | // 0b00100100 <- result
23 |
24 | var notResult: UInt8 =
25 | ~0b11110000
26 | // 0b00001111 <- result
27 |
28 | var orResult: UInt8 =
29 | 0b01010101 |
30 | 0b11110000
31 | // 0b11110101 <- result
32 |
33 | var xorResult: UInt8 =
34 | 0b01010101 ^
35 | 0b11110000
36 | // 0b10100101 <- result
37 |
38 | // Shifting in Swift is slightly different than in C++.
39 | //
40 | // A lesser-known fact about C++ is that the signed right-shift of a signed value is
41 | // implementation specific. Most compilers do what most programmers expect, which is an arithmetic
42 | // shift (which maintains the sign bit and shifts 1's into the word from the right.)
43 | //
44 | // Swift defines this behavior to be what you expect, shifting signed values to the right will
45 | // perform an arithmetic shift using two's compilment.
46 | var leftShiftUnsignedResult: UInt8 = 32 << 1
47 | var leftShiftSignedResult: Int8 = 32 << 1
48 | var leftShiftSignedNegativeResult: Int8 = -32 << 1
49 |
50 | var rightShiftUnsignedResult: UInt8 = 32 >> 1
51 | var rightShiftSignedResult: Int8 = 32 >> 1
52 | var rightShiftSignedNegativeResult: Int8 = -32 >> 1
53 |
54 | // ------------------------------------------------------------------------------------------------
55 | // Overflow operators
56 | //
57 | // If an arithmetic operation (specifically addition (+), subtraction (-) and multiplication (*))
58 | // results in a value too large or too small for the constant or variable that the result is
59 | // intended for, Swift will produce an overflow/underflow error.
60 | //
61 | // The last two lines of this code block will trigger an overflow/underflow:
62 | //
63 | // var positive: Int8 = 120
64 | // var negative: Int8 = -120
65 | // var overflow: Int8 = positive + positive
66 | // var underflow: Int8 = negative + negative
67 | //
68 | // This is also true for division by zero, which can be caused with the division (/) or remainder
69 | // (%) operators.
70 | //
71 | // Sometimes, however, overflow and underflow behavior is exactly what the programmer may intend,
72 | // so Swift provides specific overflow/underflow operators which will not trigger an error and
73 | // allow the overflow/underflow to perform as we see in C++/Objective-C.
74 | //
75 | // Special operators for division by zero are also provided, which return 0 in the case of a
76 | // division by zero.
77 | //
78 | // Here they are, in all their glory:
79 | var someValue: Int8 = 120
80 | var aZero: Int8 = someValue - someValue
81 | var overflowAdd: Int8 = someValue &+ someValue
82 | var underflowSub: Int8 = -someValue &- someValue
83 | var overflowMul: Int8 = someValue &* someValue
84 | var divByZero: Int8 = 100 &/ aZero
85 | var remainderDivByZero: Int8 = 100 &% aZero
86 |
87 | // ------------------------------------------------------------------------------------------------
88 | // Operator Functions (a.k.a., Operator Overloading)
89 | //
90 | // Most C++ programmers should be familiar with the concept of operator overloading. Swift offers
91 | // the same kind of functionality as well as additional functionality of specifying the operator
92 | // precednce and associativity.
93 | //
94 | // The most common operators will usually take one of the following forms:
95 | //
96 | // * prefix: the operator appears before a single identifier as in "-a" or "++i"
97 | // * postfix: the operator appears after a single identifier as in "i++"
98 | // * infix: the operator appears between two identifiers as in "a + b" or "c / d"
99 | //
100 | // These are specified with the three attributes, @prefix, @postfix and @infix.
101 | //
102 | // There are more types of operators (which use different attributes, which we'll cover shortly.
103 | //
104 | // Let's define a Vector2D class to work with:
105 | struct Vector2D
106 | {
107 | var x = 0.0
108 | var y = 0.0
109 | }
110 |
111 | // Next, we'll define a simple vector addition (adding the individual x & y components to create
112 | // a new vector.)
113 | //
114 | // Since we're adding two Vector2D instances, we'll use operator "+". We want our addition to take
115 | // the form "vectorA + vectorB", which means we'll be defining an infix operator.
116 | //
117 | // Here's what that looks like:
118 | func + (left: Vector2D, right: Vector2D) -> Vector2D
119 | {
120 | return Vector2D(x: left.x + right.x, y: left.y + right.y)
121 | }
122 |
123 | // Let's verify our work:
124 | var a = Vector2D(x: 1.0, y: 2.0)
125 | var b = Vector2D(x: 3.0, y: 4.0)
126 | var c = a + b
127 |
128 | // We've seen the infix operator at work so let's move on to the prefix and postfix operators.
129 | // We'll define a prefix operator that negates the vector taking the form (result = -value):
130 | prefix func - (vector: Vector2D) -> Vector2D
131 | {
132 | return Vector2D(x: -vector.x, y: -vector.y)
133 | }
134 |
135 | // Check our work:
136 | c = -a
137 |
138 | // Next, let's consider the common prefix increment operator (++a) and postfix increment (a++)
139 | // operations. Each of these performs the operation on a single value whle also returning the
140 | // appropriate result (either the original value before the increment or the value after the
141 | // increment.)
142 | //
143 | // Each will either use the @prefix or @postfix attribute, but since they also modify the value
144 | // they are also @assigmnent operators (and make use of inout for the parameter.)
145 | //
146 | // Let's take a look:
147 | prefix func ++ (inout vector: Vector2D) -> Vector2D
148 | {
149 | vector = vector + Vector2D(x: 1.0, y: 1.0)
150 | return vector
151 | }
152 |
153 | postfix func ++ (inout vector: Vector2D) -> Vector2D
154 | {
155 | var previous = vector;
156 | vector = vector + Vector2D(x: 1.0, y: 1.0)
157 | return previous
158 | }
159 |
160 | // And we can check our work:
161 | ++c
162 | c++
163 | c
164 |
165 | // Equivalence Operators allow us to define a means for checking if two values are the same
166 | // or equivalent. They can be "equal to" (==) or "not equal to" (!=). These are simple infix
167 | // opertors that return a Bool result.
168 | //
169 | // Let's also take a moment to make sure we do this right. When comparing floating point values
170 | // you can either check for exact bit-wise equality (a == b) or you can compare values that are
171 | // "very close" by using an epsilon. It's important to recognize the difference, as there are
172 | // cases when IEEE floating point values should be equal, but are actually represented differently
173 | // in their bit-wise format because they were calculated differently. In these cases, a simple
174 | // equality comparison will not suffice.
175 | //
176 | // So here are our more robust equivalence operators:
177 | let Epsilon = 0.1e-7
178 |
179 | func == (left: Vector2D, right: Vector2D) -> Bool
180 | {
181 | if abs(left.x - right.x) > Epsilon { return false }
182 | if abs(left.y - right.y) > Epsilon { return false }
183 | return true
184 | }
185 | func != (left: Vector2D, right: Vector2D) -> Bool
186 | {
187 | // Here, we'll use the inverted result of the "==" operator:
188 | return !(left == right)
189 | }
190 |
191 | // ------------------------------------------------------------------------------------------------
192 | // Custom Operators
193 | //
194 | // So far, we've been defining operator functions for operators that Swift understands and
195 | // for which Swift provides defined behaviors. We can also define our own custom operators for
196 | // doing other interestig things.
197 | //
198 | // For example, Swift doesn't support the concept of a "vector normalization" or "cross product"
199 | // because this functionality doesn't apply to any of the types Swift offers.
200 | //
201 | // Let's keep it simple, though. How about an operator that adds a vector to itself. Let's make
202 | // this a prefix operator that takes the form "+++value"
203 | //
204 | // First, we we must introduce Swift to our new operator. The syntax takes the form of the
205 | // 'operator' keyword, folowed by either 'prefix', 'postfix' or 'infix':
206 | //
207 | // Swift meet operator, operator meet swift:
208 | prefix operator +++ {}
209 |
210 | // Now we can declare our new operator:
211 | prefix func +++ (inout vector: Vector2D) -> Vector2D
212 | {
213 | vector = vector + vector
214 | return vector
215 | }
216 |
217 | // Let's check our work:
218 | var someVector = Vector2D(x: 5.0, y: 9.0)
219 | +++someVector
220 |
221 | // ------------------------------------------------------------------------------------------------
222 | // Precedence and Associativity for Custom Infix Operators
223 | //
224 | // Custom infix operators can define their own associativity (left-to-right or right-to-left or
225 | // none) as well as a precedence for determining the order of operations.
226 | //
227 | // Associativity values are 'left' or 'right' or 'none'.
228 | //
229 | // Precedence values are ranked with a numeric value. If not specified, the default value is 100.
230 | // Operations are performed in order of precedence, with higher values being performed first. The
231 | // precedence values are relative to all other precedence values for other operators. The following
232 | // are the default values for operator precedence in the Swift standard library:
233 | //
234 | // 160 (none): Operators << >>
235 | // 150 (left): Operators * / % &* &/ &% &
236 | // 140 (left): Operators + - &+ &- | ^
237 | // 135 (none): Operators .. ...
238 | // 132 (none): Operators is as
239 | // 130 (none): Operators < <= > >= == != === !== ~=
240 | // 120 (left): Operators &&
241 | // 110 (left): Operators ||
242 | // 100 (right): Operators ?:
243 | // 90 (right): Operators = *= /= %= += -= <<= >>= &= ^= |= &&= ||=
244 | //
245 | // Let's take a look at how we define a new custom infix operator with left associativity and a
246 | // precedence of 140.
247 | //
248 | // We'll define a function that adds the 'x' components of two vectors, but subtracts the 'y'
249 | // components. We'll call this the "+-" operator:
250 | infix operator +- { associativity left precedence 140 }
251 | func +- (left: Vector2D, right: Vector2D) -> Vector2D
252 | {
253 | return Vector2D(x: left.x + right.x, y: left.y - right.y)
254 | }
255 |
256 | // Check our work. Let's setup a couple vectors that result in a value of (0, 0):
257 | var first = Vector2D(x: 5.0, y: 5.0)
258 | var second = Vector2D(x: -5.0, y: 5.0)
259 | first +- second
260 |
--------------------------------------------------------------------------------
/23. Advanced Operators.playground/timeline.xctimeline:
--------------------------------------------------------------------------------
1 |
2 |
4 |
5 |
7 |
8 |
9 |
10 |
--------------------------------------------------------------------------------
/3. Strings and Characters.playground/contents.xcplayground:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/3. Strings and Characters.playground/section-1.swift:
--------------------------------------------------------------------------------
1 | // ------------------------------------------------------------------------------------------------
2 | // Things to know:
3 | //
4 | // * Strings are bridged perfectly with NSString class
5 | //
6 | // * All Strings are Unicode compliant
7 | // ------------------------------------------------------------------------------------------------
8 |
9 | // Here's a string
10 | var str: String = "Albatross! Get your albatross here!"
11 |
12 | // Strings have some special character constants. They are:
13 | "\0" // Null character
14 | "\\" // Backslash
15 | "\t" // Tab
16 | "\n" // Newline
17 | "\r" // Carriage return
18 | "\"" // Double quote
19 | "\'" // Single quote
20 | "\u{24}" // Single-byte Unicode
21 | "\u{2665}" // Double-byte unicode
22 | "\u{0001F49c}" // Four-byte unicode
23 |
24 | // Initializing an empty string - these are equivalent to each other
25 | var emptyString = ""
26 | var anotherEmptyString = String()
27 |
28 | // Use 'isEmpty' to check for empty String
29 | if emptyString.isEmpty
30 | {
31 | "Yep, it's empty"
32 | }
33 |
34 | // Strings are VALUE TYPES, but they're referenced for performance so they are only copied on
35 | // modification.
36 | func somefunc(a: String)
37 | {
38 | var b = a
39 | b = "Changed!"
40 | }
41 |
42 | var originalString = "Original"
43 | somefunc(originalString)
44 | originalString // not modified
45 |
46 | // You can iterate over a string like this:
47 | for character in originalString
48 | {
49 | character
50 | }
51 |
52 | // Characters use double-quotes to specify them, so you must be explicit if you want a Character
53 | // instead of a String:
54 | var notAString: Character = "t"
55 |
56 | // There is no length or count member of string, you have to use the global function,
57 | // countElements()
58 | //
59 | // This is much like calling strlen in which it iterates over the Unicode string and counts
60 | // characters. Note that Unicode chars are different lenghts, so this is a non-trivial process.
61 | //
62 | // “Note also that the character count returned by countElements is not always the same as the
63 | // length property of an NSString that contains the same characters. The length of an NSString is
64 | // based on the number of 16-bit code units within the string’s UTF-16 representation and not the
65 | // number of Unicode characters within the string. To reflect this fact, the length property from
66 | // NSString is called utf16count when it is accessed on a Swift String value.”
67 | countElements(originalString)
68 |
69 | // Strings can be concatenated with strings and characters
70 | var helloworld = "hello, " + "world"
71 |
72 | // ------------------------------------------------------------------------------------------------
73 | // String interpolation
74 | //
75 | // String interpolation refers to referencing values inside of a String. This works different from
76 | // printf-style functions, in that String interpolation allows you to insert the values directly
77 | // in-line with the string, rather than as additional parameters to a formatting function.
78 | let multiplier = 3
79 | let message = "\(multiplier) times 2.5 is \(Double(multiplier) * 2.5)"
80 |
81 | // ------------------------------------------------------------------------------------------------
82 | // String comparison
83 | //
84 | // String comparison is case-sensitive and can be compared for equality
85 | var str1 = "We're a lot alike, you and I."
86 | var str2 = "We're a lot alike, you and I."
87 | str1 == str2
88 |
89 | // You can also compare prefix and suffix equality:
90 | str1.hasPrefix("We're")
91 | str2.hasSuffix("I.")
92 | str1.hasPrefix("I.")
93 |
94 |
--------------------------------------------------------------------------------
/3. Strings and Characters.playground/timeline.xctimeline:
--------------------------------------------------------------------------------
1 |
2 |
4 |
5 |
7 |
8 |
9 |
10 |
--------------------------------------------------------------------------------
/4a. Arrays.playground/contents.xcplayground:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/4a. Arrays.playground/section-1.swift:
--------------------------------------------------------------------------------
1 | // ------------------------------------------------------------------------------------------------
2 | // Things to know:
3 | //
4 | // * Arrays are ordered lists of elements
5 | //
6 | // * The types of values that can be stored in an array must always be made clear either through
7 | // explicit type annotation or through type inference and does not have to be a base class type.
8 | //
9 | // * Arrays are type-safe and always clear about what they contain.
10 | //
11 | // * Arrays are value types, but Swift is smart about only copying when necessary to improve
12 | // performance.
13 | //
14 | // * Immutable arrays are immutable in terms of the array itself and the contents of the array.
15 | // This means you can't add/remove an element nor can you modify an element of an immutable
16 | // array.
17 | // ------------------------------------------------------------------------------------------------
18 |
19 | // Create an array of Strings
20 | var someArray = Array()
21 |
22 | // Shorter, more common way to define an array of Strings
23 | var shorter: [String]
24 |
25 | // This is an array literal. Since all members are of type String, this will create a String array.
26 | //
27 | // If all members are not the same type (or cannot be inferred to a homogenized type) then you
28 | // would get a compiler error.
29 | ["Eggs", "Milk"]
30 |
31 | // Let's create an array with some stuff in it. We'll use an explicit String type:
32 | var commonPets: [String] = ["Cats", "Dogs"]
33 |
34 | // We can also let Swift infer the type of the Array based on the type of the initializer members.
35 | //
36 | // The folowing is an array of Strings
37 | var shoppingList = ["Eggs", "Milk"]
38 |
39 | // ------------------------------------------------------------------------------------------------
40 | // Accessing and modifying an Array
41 | //
42 | // We can get the number of elements
43 | shoppingList.count
44 |
45 | // We can check to see if it's empty
46 | if !shoppingList.isEmpty { "it's not empty" }
47 |
48 | // We can append to the end
49 | shoppingList.append("Flour")
50 | shoppingList.append("Baking Powder")
51 | shoppingList.count
52 |
53 | // We can append another array of same type
54 | shoppingList += ["Chocolate Spread", "Cheese", "Butter"]
55 | shoppingList.count
56 |
57 | // We can get elements from the array by indexing them
58 | shoppingList[0]
59 | shoppingList[1]
60 |
61 | // We can modify an existing item
62 | shoppingList[0] = "Six Eggs"
63 |
64 | // We can use a range operator to modify existing items. This operation modifies a range with
65 | // a target range. If the target range has more or fewer elements in it, the size of the array
66 | // will be adjusted.
67 | //
68 | // Here, we replace 3 items with only two, removing an item:
69 | shoppingList[4...6] = ["Banannas", "Apples"]
70 |
71 | // Or we can replace two items with three, inserting a new item:
72 | shoppingList[4..<6] = ["Limes", "Mint leaves", "Sugar"]
73 |
74 | // We can insert an item at a given index
75 | shoppingList.insert("Maple Syrup", atIndex: 3)
76 |
77 | // We can remove the last element. During this, we can preserve the value of what was removed
78 | // into a stored value
79 | let apples = shoppingList.removeLast()
80 |
81 | // ------------------------------------------------------------------------------------------------
82 | // Enumeration
83 | //
84 | // We can iterate over the the array using a for-in loop
85 | for item in shoppingList
86 | {
87 | item
88 | }
89 |
90 | // We can also use the the enumerate() method to return a tuple containing the index and value
91 | // for each element:
92 | for (index, value) in enumerate(shoppingList)
93 | {
94 | index
95 | value
96 | }
97 |
98 | // ------------------------------------------------------------------------------------------------
99 | // Creating and initializing an array
100 | //
101 | // Earlier, we saw how to declare an array of a given type. Here, we see how to declare an array
102 | // type and then assign it to a stored value, which gets its type by inference:
103 | var someInts = [Int]()
104 |
105 | // Add the number '3' to the array
106 | someInts.append(3)
107 | someInts
108 |
109 | // We can assign it to an empty array, but we don't modify the type, since someInts is already
110 | // an Int[] type.
111 | someInts = []
112 |
113 | // We can initialize an array and and fill it with default values
114 | var threeDoubles = [Double](count: 3, repeatedValue: 3.3)
115 |
116 | // We can also use the Array initializer to fill it with default values. Note that we don't need to
117 | // specify type since it is inferred:
118 | var anotherThreeDoubles = Array(count: 3, repeatedValue: 2.5)
119 |
120 | // If you store an array in a constant, it is considered "Immutable"
121 | let immutableArray = ["a", "b"]
122 |
123 | // In terms of immutability, it's important to consider that the array and its contents are treated
124 | // separately. Therefore, you can change the contents of an immutable array, but you can't change
125 | // the array itself.
126 | //
127 | // We can't change the contents of an immutable array:
128 | //
129 | // immutableArray[0] = "b"
130 | //
131 | // Nor can we change the size or add an element, you will get a compiler error:
132 | //
133 | // immutableArray += "c"
134 |
135 | // ------------------------------------------------------------------------------------------------
136 | // Arrays are Value Types
137 | //
138 | // Arrays are value types that only copy when necessary, which is only when the array itself
139 | // changes (not the contents.)
140 | //
141 | // Here are three copies of an array:
142 | var a = [1, 2, 3]
143 | var b = a
144 | var c = a
145 |
146 | // However, if we change the contents of one array (mutating it), then it is copied and becomes its
147 | // own unique entity:
148 | a[0] = 42
149 | b[0]
150 | c[0]
151 |
152 | // Now that we've changed a, it should have been copied to its own instance. Let's double-check
153 | // that only b & c are the same:
154 | a
155 | b
156 | c
157 |
158 | // The same is true if we mutate the array in other ways (mofify the array's size)...
159 | b.append(4)
160 |
161 | // Now, we have three different arrays...
162 | a
163 | b
164 | c
165 |
166 |
--------------------------------------------------------------------------------
/4a. Arrays.playground/timeline.xctimeline:
--------------------------------------------------------------------------------
1 |
2 |
4 |
5 |
7 |
8 |
9 |
10 |
--------------------------------------------------------------------------------
/4b. Dictionaries.playground/contents.xcplayground:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/4b. Dictionaries.playground/section-1.swift:
--------------------------------------------------------------------------------
1 | // ------------------------------------------------------------------------------------------------
2 | // Things to know:
3 | //
4 | // * Dictionaries store multiple values of the same type, each associated with a key which acts as
5 | // an identifier for that value within the dictionary.
6 | //
7 | // * Dictionaries are type-safe and always clear about what they contain.
8 | //
9 | // * The types of values that can be stored in a dictionary must always be made clear either
10 | // through explicit type annotation or through type inference.
11 | // ------------------------------------------------------------------------------------------------
12 |
13 | // ------------------------------------------------------------------------------------------------
14 | // Creating a dictionary
15 | //
16 | // This is a Dictionary literal. They contain a comma-separated list of key:value pairs:
17 | ["TYO": "Tokyo", "DUB": "Dublin"]
18 |
19 | // Let's use that literal to define and initialize a Dictionary.
20 | //
21 | // In this case, we use type annotation to explicitly declare a Dictionary containing String keys
22 | // and String values. This uses the syntactic sugar "[ KeyType: ValueType ]" to declare the
23 | // dictionary.
24 | var airports: [String : String] = ["TYO": "Tokyo", "DUB": "Dublin", "APL": "Apple Intl"]
25 |
26 | // The declaration for airports above could also have been declared in this way:
27 | var players: Dictionary = ["Who" : "First", "What" : "Second"]
28 |
29 | // In the case below, the literal contains only Strings for all keys and only Strings for all
30 | // values, so type inference works in our favor allowing us to avoid the type annotation:
31 | let inferredDictionary = ["TYO": "Tokyo", "DUB": "Dublin"]
32 |
33 | // ------------------------------------------------------------------------------------------------
34 | // Accessing and modifying a Dictionary
35 | //
36 | // Let's get a value from the dictionary for the TYO airport:
37 | airports["TYO"]
38 |
39 | // What happens if we try to get a value that doesn't exist?
40 | //
41 | // Since this can happen, the Dictionary always retuns an optional, so in the next line of code,
42 | // notFound will be nil. It will also be of type String?
43 | var notFound = airports["FOO"]
44 |
45 | // We can get the number of elements in the dictionary:
46 | airports.count
47 |
48 | // We can add an element by accessing a key that doesn't exist:
49 | airports["LHR"] = "London"
50 |
51 | // Here's another way to set/update a value. This lets us get the previous value before we set
52 | // the new one. The returned value is optional, in case the new value doesn't replace an existing
53 | // one:
54 | var previousValue = airports.updateValue("Dublin International", forKey: "DUB")
55 |
56 | // We can remove an entry by setting the value for a key to nil:
57 | airports["APL"] = nil
58 |
59 | // Here's another way to remove a value. The returned value is set to the value that was removed.
60 | // Again, this is optional in case there was no value to remove. In this case, the APL airport
61 | // was already removed, so the return value will be a nil optional:
62 | var removedValue = airports.removeValueForKey("APL")
63 |
64 | // ------------------------------------------------------------------------------------------------
65 | // Iterating over a Dictionary
66 | //
67 | // We can iterating over key/value pairs with a for-in loop, which uses a Tuple to hold the
68 | // key/value pair for each entry in the Dictionary:
69 | for (airportCode, airportName) in airports
70 | {
71 | airportCode
72 | airportName
73 | }
74 |
75 | // We can iterate over just the keys
76 | for airportCode in airports.keys
77 | {
78 | airportCode
79 | }
80 |
81 | // We can iterate over jsut the values
82 | for airportName in airports.values
83 | {
84 | airportName
85 | }
86 |
87 | // We can create an array from the keys or values
88 | //
89 | // Note that when doing this, the use of Array() is needed to convert the keys or values into
90 | // an array.
91 | var airportCodes = Array(airports.keys)
92 | var airportNames = Array(airports.values)
93 |
94 | // ------------------------------------------------------------------------------------------------
95 | // Creating an empty Dictionary
96 | //
97 | // Here, we create an empty Dictionary of Int keys and String values:
98 | var namesOfIntegers = Dictionary()
99 |
100 | // Let's set one of the values
101 | namesOfIntegers[16] = "Sixteen"
102 |
103 | // We can empty a dictionary using an empty dictionary literal:
104 | namesOfIntegers = [:]
105 |
106 | // An immutable dictionary is a constant.
107 | let immutableDict = ["a": "one", "b": "two"]
108 |
109 | // Similar to arrays, we cannot modify the contents of an immutable dictionary. The following lines
110 | // will not compile:
111 | //
112 | // immutableDict["a"] = "b" // You cannot modify an element
113 | // immutableDict["c"] = "three" // You cannot add a new entry or change the size
114 |
115 | // Dictionaries are value types, which means they are copied on assignment.
116 | //
117 | // Let's create a Dictionary and copy it:
118 | var ages = ["Peter": 23, "Wei": 35, "Anish": 65, "Katya": 19]
119 | var copiedAges = ages
120 |
121 | // Next, we'll modify the copy:
122 | copiedAges["Peter"] = 24
123 |
124 | // And we can see that the original is not changed:
125 | ages["Peter"]
126 |
127 |
--------------------------------------------------------------------------------
/4b. Dictionaries.playground/timeline.xctimeline:
--------------------------------------------------------------------------------
1 |
2 |
4 |
5 |
7 |
8 |
10 |
11 |
12 |
13 |
--------------------------------------------------------------------------------
/5. Control Flow.playground/contents.xcplayground:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/5. Control Flow.playground/section-1.swift:
--------------------------------------------------------------------------------
1 | // ------------------------------------------------------------------------------------------------
2 | // Things to know:
3 | //
4 | // * Much of the control flow in Swift is similar to C-like languages, but there are some key
5 | // differences. For example, switch-case constructs are much more flexible and powerful as well
6 | // as extensions to break and continue statements.
7 | // ------------------------------------------------------------------------------------------------
8 |
9 | // ------------------------------------------------------------------------------------------------
10 | // For loops
11 | //
12 | // We can loop through ranges using the closed-range operator ("...").
13 | //
14 | // In the loop below, 'index' is a constant that is automatically declared.
15 | for index in 1...5
16 | {
17 | "This will print 5 times"
18 |
19 | // Being a constant, the following line won't compile:
20 | //
21 | // index = 99
22 | }
23 |
24 | // The constant 'index' from the previous loop is scoped only to the loop. As a result, you cannot
25 | // access it beyond the loop. The following line will not compile:
26 | //
27 | // index = 0
28 |
29 | // We can loop through ranges using the half-closed range operator ("..<")
30 | //
31 | // We can also reuse the name 'index' because of the scoping noted previously.
32 | for index in 1 ..< 5
33 | {
34 | "This will print 4 times"
35 | }
36 |
37 | // Apple's "Swift Programming Language" book states the following, which I find in practice to be
38 | // incorrect:
39 | //
40 | // “The index constant exists only within the scope of the loop. If you want to check the value of
41 | // index after the loop completes, or if you want to work with its value as a variable rather than
42 | // a constant, you must declare it yourself before its use in the loop.”
43 | //
44 | // In practice, I find that the loop constant overrides any local variable/constant and maintains
45 | // its scope to the loop and does not alter the locally defined value:
46 | var indx = 3999
47 | for indx in 1...5
48 | {
49 | indx // This ranges from 1 to 5, inclusive
50 |
51 | // 'indx' is still acting like a constant, so this line won't compile:
52 | //
53 | // indx++
54 | }
55 |
56 | // After the loop, we find that 'indx' still contains the original value of 3999
57 | indx
58 |
59 | // We can use an underscore if you don't need access to the loop constant:
60 | for _ in 1...10
61 | {
62 | println("do something")
63 | }
64 |
65 | // We can iterate over arrays
66 | let names = ["Anna", "Alex", "Brian", "Jack"]
67 | for name in names
68 | {
69 | name
70 | }
71 |
72 | // We can iterate over a Dictionary's key/value pairs
73 | let numberOfLegs = ["Spider":8, "Ant":6, "Cat":4]
74 | for (animalName, legs) in numberOfLegs
75 | {
76 | animalName
77 | legs
78 | }
79 |
80 | // We can iterate over characters in a String
81 | for character in "Hello"
82 | {
83 | character
84 | }
85 |
86 | // We can use the For-Condition-Increment loop construct, which resembles the C-like variant
87 | //
88 | // Note that the loop value is a variable, not a constant. In fact, they cannot be constant
89 | // because of the increment statement (++index)
90 | for (var index = 0; index < 3; ++index)
91 | {
92 | index
93 | }
94 |
95 | // The parenthesis are optional for the For-Condition-Increment loop:
96 | for var index = 0; index < 3; ++index
97 | {
98 | index
99 | }
100 |
101 | // Variables are scoped to the For-Condition-Increment construct. To alter this, pre-declare index
102 | var index = 3000
103 | for index = 0; index < 3; ++index
104 | {
105 | index
106 | }
107 | index // Index holds 3 after running through the loop
108 |
109 | // ------------------------------------------------------------------------------------------------
110 | // While loops
111 | //
112 | // While loops resemble other C-like languages. They perform the condition before each iteration
113 | // through the loop:
114 | while index > 0
115 | {
116 | --index
117 | }
118 |
119 | // Do-While loops also resemble their C-like language counterparts. They perform the condition
120 | // after each iteration through the loop. As a result, they always execute the code inside the
121 | // loop at least once:
122 | do
123 | {
124 | ++index
125 | } while (index < 3)
126 |
127 | // ------------------------------------------------------------------------------------------------
128 | // Conditional Statements
129 | //
130 | // The if statement is very similar to C-like languages, except that the parenthesis are optional.
131 | // You can also chain multiple conditions with 'else' and 'else if' statements:
132 | if (index > 0)
133 | {
134 | "Index is positive"
135 | }
136 | else if index == 0
137 | {
138 | "index is zero"
139 | }
140 | else
141 | {
142 | "index is negative"
143 | }
144 |
145 | // Switch statements are more powerful than their C-like counterparts. Here are a few of those
146 | // differences to get us started:
147 | //
148 | // Unlike C-like languages, switch statements do not require a "break" statement to prevent falling
149 | // through to the next case.
150 | //
151 | // Additionally, multiple conditions can be separated by a comma for a single case to match
152 | // multiple conditions.
153 | //
154 | // Switch statements must also be exhaustive and include all possible values, or the compiler will
155 | // generate an error.
156 | //
157 | // There are many more differences, but let's start with a simple switch statement to get our feet
158 | // wet:
159 | let someCharacter: Character = "e"
160 | switch someCharacter
161 | {
162 | case "a", "e", "i", "o", "u":
163 | "a vowel"
164 |
165 | case "b", "c", "d", "f", "g", "h", "j", "k", "l", "m", "n", "p", "q", "r", "s", "t", "v", "w", "x", "u", "z":
166 | "a consonant"
167 |
168 | // Necessary because switch statements must be exhaustive in order to capture all Characters
169 | default:
170 | "not a vowel or consonant"
171 | }
172 |
173 | // Each case clause must have a statement of some kind. A comment will not suffice.
174 | //
175 | // Otherwise you will get a compilation error. The following won't compile because there is an
176 | // empty case statement:
177 | //
178 | // let anotherCharacter: Character = "a"
179 | // switch anotherCharacter
180 | // {
181 | // case "a":
182 | // case "A":
183 | // "the letter a"
184 | // default:
185 | // "not the letter a"
186 | // }
187 |
188 | // We can perform range matching for cases:
189 | let count = 3_000_000_000_000
190 | switch count
191 | {
192 | case 0:
193 | "no"
194 | case 1...3:
195 | "a few"
196 | case 4...9:
197 | "several"
198 | case 10...99:
199 | "tens of"
200 | case 100...999:
201 | "hundreds of"
202 | case 1000...999999:
203 | "thousands of"
204 | default:
205 | "millions and millions of"
206 | }
207 |
208 | // Matching against tuples
209 | //
210 | // In addition to matching Tuples, we can also use ranges inside Tuple values and even match
211 | // against partial Tuple values by using an "_" to ignore matches against a specific value within
212 | // the Tuple.
213 | let somePoint = (1,1)
214 | switch somePoint
215 | {
216 | case (0,0):
217 | "origin"
218 |
219 | // Match only against y=0
220 | case (_, 0):
221 | "On the X axis"
222 |
223 | // Match only against x=0
224 | case (0, _):
225 | "On the y axis"
226 |
227 | // Match x and y from -2 to +2 (inclusive)
228 | case (-2...2, -2...2):
229 | "On or inside the 2x2 box"
230 |
231 | // Everything else
232 | default:
233 | "Outisde the 2x2 box"
234 | }
235 |
236 | // Value bindings in switch statements
237 | //
238 | var anotherPoint = (2, 8)
239 | switch anotherPoint
240 | {
241 | // Bind 'x' to the first value (matching any x) of the tuple and match on y=0
242 | case (let x, 0):
243 | "On the x axis with an x value of \(x)"
244 |
245 | // Bind 'y' to the second value (matching any y) of the tuple and match against x=0
246 | case (0, let y):
247 | "On the y axis with an y value of \(y)"
248 |
249 | // Bind both values of the tuple, matching any x or y. Note the shorthand of the 'let'
250 | // outside of the parenthesis. This works with 'var' as well.
251 | //
252 | // Also notice that since this matches any x or y, we fulfill the requirement for an exhaustive
253 | // switch.
254 | case let (x, y):
255 | "Somewhere else on \(x), \(y)"
256 | }
257 |
258 | // We can also mix let/var for case statements. The following code block is the same as the
259 | // previous except that the final case statement, which mixes variable and constants for the x and
260 | // y components of the Tuple.
261 | switch anotherPoint
262 | {
263 | case (let x, 0):
264 | "On the x axis with an x value of \(x)"
265 |
266 | case (0, let y):
267 | "On the y axis with an y value of \(y)"
268 |
269 | case (var x, let y):
270 | ++x // We can modify the variable 'x', but not the constant 'y'
271 | "Somewhere else on \(x), \(y)"
272 | }
273 |
274 | // Where clauses allow us to perform more detailed conditions on case conditions. The where clauses
275 | // work on the values declared on the case line:
276 | let yetAnotherPoint = (1, -1)
277 | switch yetAnotherPoint
278 | {
279 | case let (x, y) where x == y:
280 | "On the line of x == y"
281 |
282 | case let (x, y) where x == -y:
283 | "On the line of x == -y"
284 |
285 | case let (x, y):
286 | "Just some arbitrary point"
287 | }
288 |
289 | // ------------------------------------------------------------------------------------------------
290 | // Control transfer statements
291 | //
292 | // Swift supports extended versions of continue and break as well as an additional 'fallthrough'
293 | // statement for switch-case constructs.
294 | //
295 | // Since swift doesn't require a break statement to avoid falling through to the next case, we can
296 | // still use them to early-out of the current case without continuing work. The first statement
297 | // after the 'break' will be the next statement following the entire switch construct.
298 | let someValue = 9000
299 | switch someValue
300 | {
301 | case let x where (x & 1) == 1:
302 | if someValue < 100
303 | {
304 | "Odd number less than 100"
305 | break
306 | }
307 | "Odd number greater or equal to 100"
308 |
309 | case let x where (x & 1) == 0:
310 | if someValue < 100
311 | {
312 | "Even number less than 100"
313 | break
314 | }
315 | "Even number greater or equal to 100"
316 |
317 | default:
318 | "Unknown value"
319 | }
320 |
321 | // Since each case must have a statement and since we must have an exhaustive switch, we can use
322 | // the break statement to effectively nullify the use of a case:
323 | switch someValue
324 | {
325 | case Int.min...100:
326 | "Small number"
327 |
328 | case 101...1000:
329 | break // We don't care about medium numbers
330 |
331 | case 1001...100_00:
332 | "Big number"
333 |
334 | default:
335 | break // We don't care about the rest, either
336 | }
337 |
338 | // Since we don't need to break out of cases to avoid falling through automatically, we must
339 | // specifically express our intention to fall through using the 'fallthrough' keyword
340 | let integerToDescribe = 5
341 | var integerDescription = "\(integerToDescribe) is"
342 | switch integerToDescribe
343 | {
344 | case 2, 3, 5, 7, 11, 13, 17, 19:
345 | integerDescription += " a prime number, and also"
346 | fallthrough
347 |
348 | default:
349 | integerDescription += " an integer."
350 | }
351 |
352 | // Continue and Break statements have been extended in Swift to allow each to specify which
353 | // switch or loop construct to break out of, or continue to.
354 | //
355 | // To enable this, labels are used, similar to labels used by C's goto statement.
356 | //
357 | // The following will print each name until it reaches the letter 'a' then skip to the next name
358 | var result = ""
359 | nameLoop: for name in names
360 | {
361 | characterLoop: for character in name
362 | {
363 | theSwitch: switch character
364 | {
365 | case "a":
366 | // Break out of the theSwitch and characterLoop
367 | break characterLoop
368 |
369 | default:
370 | result += String(character)
371 | }
372 | }
373 | }
374 | result
375 |
376 | // Similarly, this prints all names without the letter 'a' in them:
377 | result = ""
378 | nameLoop: for name in names
379 | {
380 | characterLoop: for character in name
381 | {
382 | theSwitch: switch character
383 | {
384 | case "a":
385 | // Continue directly to the character loop, bypassing this character in this name
386 | continue characterLoop
387 |
388 | default:
389 | result += String(character)
390 | }
391 | }
392 | }
393 | result
394 |
395 | // Similarly, this prints all names until the letter 'x' is found, then aborts all processing by
396 | // breaking out of the outer loop:
397 | result = ""
398 | nameLoop: for name in names
399 | {
400 | characterLoop: for character in name
401 | {
402 | theSwitch: switch character
403 | {
404 | case "x":
405 | // Break completely out of the outer name loop
406 | break nameLoop
407 |
408 | default:
409 | result += String(character)
410 | }
411 | }
412 | }
413 | result
414 |
--------------------------------------------------------------------------------
/5. Control Flow.playground/timeline.xctimeline:
--------------------------------------------------------------------------------
1 |
2 |
4 |
5 |
7 |
8 |
9 |
10 |
--------------------------------------------------------------------------------
/6. Functions.playground/contents.xcplayground:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/6. Functions.playground/section-1.swift:
--------------------------------------------------------------------------------
1 | // ------------------------------------------------------------------------------------------------
2 | // Things to know:
3 | //
4 | // * Like most other languages, functions contain units of code to perform a task, but there are
5 | // a few key featurs of functions in Swift.
6 | //
7 | // * Functions are types in Swift. This means that a function can receive another function as an
8 | // input parameter or return a function as a result. You can even declare variables that are
9 | // functions.
10 | //
11 | // * Functions can be nested, and the inner function can be returned by the outer function. To
12 | // enable this, the current state of any local variables (local to the outer function) that are
13 | // used by the inner function are "captured" for use by the inner function. Any time that inner
14 | // function is called, it uses this captured state. This is the case even when a nested function
15 | // is returned by the parent function.
16 | // ------------------------------------------------------------------------------------------------
17 |
18 | // Here's a simple function that receives a Single string and returns a String
19 | //
20 | // Note that each parameter has a local name (for use within the function) and a standard type
21 | // annotation. The return value's type is at the end of the function, following the ->.
22 | func sayHello(personName: String) -> String
23 | {
24 | return "Hello, \(personName)"
25 | }
26 |
27 | // If we call the function, we'll receive the greeting
28 | sayHello("Peter Parker")
29 |
30 | // Multiple input parameters are separated by a comma
31 | func halfOpenRangeLength(start: Int, end: Int) -> Int
32 | {
33 | return end - start
34 | }
35 |
36 | // A function with no parameters simply has an empty set of parenthesis following the function
37 | // name:
38 | func sayHelloWorld() -> String
39 | {
40 | return "Hello, world"
41 | }
42 |
43 | // A funciton with no return value can be expressed in two different ways. The first is to replace
44 | // the return type with a set of empty parenthesis, which can be thought of as an empty Tuple.
45 | func sayGoodbye(name: String) -> ()
46 | {
47 | "Goodbye, \(name)"
48 | }
49 |
50 | // We can also remove the return type (and the -> delimiter) alltogether:
51 | func sayGoodbyeToMyLittleFriend(name: String)
52 | {
53 | "Goodbye, \(name)"
54 | }
55 |
56 | // Functions can return Tuples, which enable them to return multiple values.
57 | //
58 | // The following function simply returns two hard-coded strings.
59 | func getApplicationNameAndVersion() -> (String, String)
60 | {
61 | return ("Modaferator", "v1.0")
62 | }
63 |
64 | // Since the return value is a Tuple, we can use the Tuple's naming feature to name the values
65 | // being returned:
66 | func getApplicationInfo() -> (name: String, version: String)
67 | {
68 | return ("Modaferator", "v1.0")
69 | }
70 | var appInfo = getApplicationInfo()
71 | appInfo.name
72 | appInfo.version
73 |
74 | // ------------------------------------------------------------------------------------------------
75 | // External Parameter Names
76 | //
77 | // We can use Objective-C-like external parameter names so the caller must name the external
78 | // parameters when they call the function. The extenal name appears before the local name.
79 | func addSeventeen(toNumber value: Int) -> Int
80 | {
81 | return value + 17
82 | }
83 | addSeventeen(toNumber: 42)
84 |
85 | // If your internal and external names are the same, you can use a shorthand #name syntax to create
86 | // both names at once.
87 | //
88 | // The following declaration creates an internal parameter named "action" as well as an external
89 | // parameter named "action":
90 | func kangaroosCan(#action: String) -> String
91 | {
92 | return "A Kangaroo can \(action)"
93 | }
94 |
95 | // We can now use the external name ("action") to make the call:
96 | kangaroosCan(action: "jump")
97 | kangaroosCan(action: "carry children in their pouches")
98 |
99 | // We can also have default parameter values. Default parameter values must be placed at the end
100 | // of the parameter list.
101 | //
102 | // In the addMul routine, we'll add two numbers and multiply the result by an optional multiplier
103 | // value. We will default the multiplier to 1 so that if the parameter is not specified, the
104 | // multiplication won't affect the result.
105 | func addMul(firstAdder: Int, secondAdder: Int, multiplier: Int = 1) -> Int
106 | {
107 | return (firstAdder + secondAdder) * multiplier
108 | }
109 |
110 | // We can call with just two parameters to add them together
111 | addMul(1, 2)
112 |
113 | // Default parameter values and external names
114 | //
115 | // Swift automatically creates external parameter names for those parameters that have default
116 | // values. Since our declaration of addMul did not specify an external name (either explicitly or
117 | // using the shorthand method), Swift created one for us based on the name of the internal
118 | // parameter name. This acts as if we had defined the third parameter using the "#" shorthand.
119 | //
120 | // Therefore, when calling the function and specifying a value for the defaulted parameter, we
121 | // must provide the default parameter's external name:
122 | addMul(1, 2, multiplier: 9)
123 |
124 | // We can opt out of the automatic external name for default parameter values by specify an
125 | // external name of "_" like so:
126 | func anotherAddMul(firstAdder: Int, secondAdder: Int, _ multiplier: Int = 1) -> Int
127 | {
128 | return (firstAdder + secondAdder) * multiplier
129 | }
130 |
131 | // Here, we call without the third parameter as before:
132 | anotherAddMul(1, 2)
133 |
134 | // And now we can call with an un-named third parameter:
135 | anotherAddMul(1, 2, 9)
136 |
137 | // ------------------------------------------------------------------------------------------------
138 | // Variadic Parameters
139 | //
140 | // Variadic parameters allow you to call functions with zero or more values of a specified type.
141 | //
142 | // Variadic parameters appear within the receiving function as an array of the given type.
143 | //
144 | // A function may only have at most one variadic and it must appear as the last parameter.
145 | func arithmeticMean(numbers: Double...) -> Double
146 | {
147 | var total = 0.0
148 |
149 | // The variadic, numbers, looks like an array to the internal function so we can just loop
150 | // through them
151 | for number in numbers
152 | {
153 | total += number
154 | }
155 | return numbers.count == 0 ? total : total / Double(numbers.count)
156 | }
157 |
158 | // Let's call it with a few parameter lengths. Note that we can call with no parameters, since that
159 | // meets the criteria of a variadic parameter (zero or more).
160 | arithmeticMean()
161 | arithmeticMean(1)
162 | arithmeticMean(1, 2)
163 | arithmeticMean(1, 2, 3)
164 | arithmeticMean(1, 2, 3, 4)
165 | arithmeticMean(1, 2, 3, 4, 5)
166 | arithmeticMean(1, 2, 3, 4, 5, 6)
167 |
168 | // If we want to use variadic parameters and default parameter values, we can do so by making sure
169 | // that the default parameters come before the variadic, at the end of the parameter list:
170 | func anotherArithmeticMean(initialTotal: Double = 0, numbers: Double...) -> Double
171 | {
172 | var total = initialTotal
173 | for number in numbers
174 | {
175 | total += number
176 | }
177 | return numbers.count == 0 ? total : total / Double(numbers.count)
178 | }
179 |
180 | // Here, we can still call with no parameters because of the default parameter
181 | anotherArithmeticMean()
182 |
183 | // Going forward, we must specify the default parameter's external name (because we didn't opt-out
184 | // of it.) Also, you can see why Swift attempts to enforce the use of external parameter names for
185 | // default parameter values. In this case, it helps us recognize where the defalt parameters leave
186 | // off and the variadic parameters begin:
187 | anotherArithmeticMean(initialTotal: 1)
188 | anotherArithmeticMean(initialTotal: 1, 2)
189 | anotherArithmeticMean(initialTotal: 1, 2, 3)
190 | anotherArithmeticMean(initialTotal: 1, 2, 3, 4)
191 | anotherArithmeticMean(initialTotal: 1, 2, 3, 4, 5)
192 | anotherArithmeticMean(initialTotal: 1, 2, 3, 4, 5, 6)
193 |
194 | // Variadic parameters with external parameter names only apply their external name to the first
195 | // variadic parameter specified in the function call (if present.)
196 | func yetAnotherArithmeticMean(initialTotal: Double = 0, values numbers: Double...) -> Double
197 | {
198 | var total = initialTotal
199 | for number in numbers
200 | {
201 | total += number
202 | }
203 | return numbers.count == 0 ? total : total / Double(numbers.count)
204 | }
205 |
206 | // And here we can see the impact on the function call of adding the external name "values" to the
207 | // variadic parameter:
208 | yetAnotherArithmeticMean()
209 | yetAnotherArithmeticMean(initialTotal: 1)
210 | yetAnotherArithmeticMean(initialTotal: 1, values: 2)
211 | yetAnotherArithmeticMean(initialTotal: 1, values: 2, 3)
212 | yetAnotherArithmeticMean(initialTotal: 1, values: 2, 3, 4)
213 | yetAnotherArithmeticMean(initialTotal: 1, values: 2, 3, 4, 5)
214 | yetAnotherArithmeticMean(initialTotal: 1, values: 2, 3, 4, 5, 6)
215 |
216 | // ------------------------------------------------------------------------------------------------
217 | // Constant and variable parameters
218 | //
219 | // All function parameters are constant by default. To make them variable, add the var introducer:
220 | func padString(var str: String, pad: Character, count: Int) -> String
221 | {
222 | str = Array(count: count, repeatedValue: pad) + str
223 | return str
224 | }
225 |
226 | var paddedString = "padded with dots"
227 | padString(paddedString, ".", 10)
228 |
229 | // Note that the function does not modify the caller's copy of the string that was passed in
230 | // because the value is still passed by value:
231 | paddedString
232 |
233 | // ------------------------------------------------------------------------------------------------
234 | // In-Out Parameters
235 | //
236 | // In-Out parameters allow us to force a parameter to be passed by reference so those changes can
237 | // persist after the function call.
238 | //
239 | // Note that inout parameters cannot be variadic or have default parameter values.
240 | //
241 | // We'll write a standard swap function to exercise this:
242 | func swap(inout a: Int, inout b: Int)
243 | {
244 | let tmp = a
245 | a = b
246 | b = tmp
247 | }
248 |
249 | // Let's call the swap function to see what happens.
250 | //
251 | // To ensure that the caller is aware that the parameter is an inout (and hence, can modify their
252 | // variable), we must prefix the parameter name with "&".
253 | //
254 | // Note that the variables that will be passed as references may not be defined as constants,
255 | // because it is expected that their values will change:
256 | var one = 1, two = 2
257 | swap(&one, &two)
258 |
259 | // And we can see that 'one' contains a 2 and 'two' contains a 1:
260 | one
261 | two
262 |
263 | // ------------------------------------------------------------------------------------------------
264 | // Function types
265 | //
266 | // The type of a function is comprised of the specific parameter list (the number of parameters as
267 | // well as their type) and the return value's type.
268 | //
269 | // The following two functions have the same type.
270 | //
271 | // It's important to note that their type is described as: (Int, Int) -> Int
272 | func add(a: Int, b: Int) -> Int {return a+b}
273 | func mul(a: Int, b: Int) -> Int {return a*b}
274 |
275 | // A function that has no parameters or return value would have the type: () -> ()
276 | func nop() -> ()
277 | {
278 | return
279 | }
280 |
281 | // The type of the function below is the same as the one above: () -> ()
282 | //
283 | // It is written in a shorter form, eliminating the return type entirely, but this syntactic
284 | // simplification doesn't alter the way that the function's type is notated:
285 | func doNothing()
286 | {
287 | return
288 | }
289 |
290 | // Using what we know about funciton types, we can define variables that are the type of a function
291 | let doMul: (Int, Int) -> Int = mul
292 |
293 | // We can now use the variable to perform the function:
294 | doMul(4, 5)
295 |
296 | // We can also name our parameters when assigning them to a variable, as well as taking advantage
297 | // of un-named parameters ("_").
298 | //
299 | // This additional syntactic decoration has a purpose, but it doesn't affect the underlying
300 | // function type, which remains: (Int, Int) -> Int
301 | let doAddMul: (a: Int, b: Int, Int) -> Int = addMul
302 |
303 | // Calling the function now requires external names for the first two parameters
304 | doAddMul(a: 4, b: 2, 55)
305 |
306 | // We can pass function types as parameters to funcions, too.
307 | //
308 | // Here, our first parameter named 'doMulFunc' has the type "(Int, Int) -> Int" followed by two
309 | // parameters, 'a' and 'b' that are used as parameters to the 'doMulFunc' function:
310 | func doDoMul(doMulFunc: (Int, Int) -> Int, a: Int, b: Int) -> Int
311 | {
312 | return doMulFunc(a, b)
313 | }
314 |
315 | // We can now pass the function (along with a couple parameters to call it with) to another
316 | // function:
317 | doDoMul(doMul, 5, 5)
318 |
319 | // We can also return function types.
320 | //
321 | // The syntax looks a little wird because of the two sets of arrows. To read this, understand that
322 | // the first arrow specifies the return type, and the second arrow is simply part of the function
323 | // type being returned:
324 | func getDoMul() -> (Int, Int) -> Int
325 | {
326 | return doMul
327 | }
328 | let newDoMul = getDoMul()
329 | newDoMul(9, 5)
330 |
331 | // Or, an even shorter version that avoids the additional stored value:
332 | getDoMul()(5, 5)
333 |
334 | // Earlier we discussed how functions that do not return values still have a return type that
335 | // includes: -> ()
336 | //
337 | // Here, we'll see this in action by returning a function that doesn't return a value:
338 | func getNop() -> () -> ()
339 | {
340 | return nop
341 | }
342 |
343 | // And we'll call nop (note the second set of parenthesis to make the actual call to nop())
344 | getNop()()
345 |
346 | // We can nest functions inside of other functions:
347 | func getFive() -> Int
348 | {
349 | func returnFive() -> Int
350 | {
351 | return 5
352 | }
353 |
354 | // Call returnFive() and return the result of that function
355 | return returnFive()
356 | }
357 |
358 | // Calling getFive will return the Int value 5:
359 | getFive()
360 |
361 | // You can return nested functions, too:
362 | func getReturnFive() -> () -> Int
363 | {
364 | func returnFive() -> Int
365 | {
366 | return 5
367 | }
368 | return returnFive
369 | }
370 |
371 | // Calling outerFunc2 will return a function capable of returning the Int value 5:
372 | let returnFive = getReturnFive()
373 |
374 | // Here we call the nested function:
375 | returnFive()
376 |
--------------------------------------------------------------------------------
/6. Functions.playground/timeline.xctimeline:
--------------------------------------------------------------------------------
1 |
2 |
4 |
5 |
7 |
8 |
9 |
10 |
--------------------------------------------------------------------------------
/7. Closures.playground/contents.xcplayground:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/7. Closures.playground/section-1.swift:
--------------------------------------------------------------------------------
1 | // ------------------------------------------------------------------------------------------------
2 | // Things to know:
3 | //
4 | // * Closures are blocks of code.
5 | //
6 | // * The can be passed as parameters to functions much like Function Types. In fact, functions
7 | // are a special case of closures.
8 | //
9 | // * Closures of all types (including nested functions) employ a method of capturing the surrounding
10 | // context in which is is defined, allowing it to access constants and variables from that
11 | // context.
12 | // ------------------------------------------------------------------------------------------------
13 |
14 | // Closures can use constant, variable, inout, variadics, tuples for their parameters. They can
15 | // return any value, including Tuples. They cannot, however, have default parameters.
16 | //
17 | // The basic syntax is:
18 | //
19 | // { (parameters) -> return_type in
20 | // ... statements ...
21 | // }
22 | //
23 | // Here's an example of a simple String comparison closure that might be used for sorting Strings:
24 | //
25 | // { (s1: String, s2: String) -> Bool in
26 | // return s1 < s2
27 | // }
28 | //
29 | // Here's an example using Swift's 'sorted' member function. It's important to note that this
30 | // function receives a single closure.
31 | //
32 | // These can be a little tricky to read if you're not used to them. To understand the syntax, pay
33 | // special attention to the curly braces that encapsulate the closure and the parenthesis just
34 | // outside of those curly braces:
35 | let names = ["Chris", "Alex", "Ewa", "Barry", "Daniella"]
36 | var reversed = [String]()
37 | reversed = names.sorted({
38 | (s1: String, s2: String) -> Bool in
39 | return s1 > s2
40 | })
41 |
42 | // ------------------------------------------------------------------------------------------------
43 | // Inferring Type from Context
44 | //
45 | // Like functions, closures have a type.
46 | //
47 | // If the type is known (as is always the case when passing a closure as a parameter to a function)
48 | // then the return type of the closure can be inferred, allowing us to simplify the syntax of our
49 | // call to sort.
50 | //
51 | // The following call is identical to the one above with the exception that "-> Bool" was removed:
52 | reversed = names.sorted({
53 | (s1: String, s2: String) in
54 | return s1 > s2
55 | })
56 |
57 | // Just as the return type can be inferred, so can the parameter types. This allows us to simplify
58 | // the syntax a bit further by removing the type annotations from the closure's parameters.
59 | //
60 | // The following call is identical to the one above with the exception that the parameter type
61 | // annotations (": String") have been removed:
62 | reversed = names.sorted({
63 | (s1, s2) in
64 | return s1 > s2
65 | })
66 |
67 | // Since all types can be inferred and we're not using any type annotation on the parameters,
68 | // we can simplify a bit further by removing the paranthesis around the parameters. We'll also put
69 | // it all on a single line, since it's a bit more clear now:
70 | reversed = names.sorted({ s1, s2 in return s1 > s2 })
71 |
72 | // If the closuere has only a single expression, then the return statement is also inferred. When
73 | // this is the case, the closure returns the value of the single expression:
74 | reversed = names.sorted({ s1, s2 in s1 > s2 })
75 |
76 | // We're not done simplifying yet. It turns out we can get rid of the parameters as well. If we
77 | // remove the parameters, we can still access them because Swift provides shorthand names to
78 | // parameters passed to inline closures. To access the first parameter, use $0. The second
79 | // parameter would be $1 and so on.
80 | //
81 | // Here's what that would might like (this will not compile - yet):
82 | //
83 | // reversed = names.sorted({ s1, s2 in $0 > $1 })
84 | //
85 | // This won't compile because you're not allowed to use shorthand names if you specify the
86 | // parameter list. Therefore, we need to remove those in order to get it to compile. This makes
87 | // for a very short inline closure:
88 | reversed = names.sorted({ $0 > $1 })
89 |
90 | // Interestingly enough, the operator < for String types is defined as:
91 | //
92 | // (String, String) -> Bool
93 | //
94 | // Notice how this is the same as the closure's type for the sorted() routine? Wouldn't it be
95 | // nice if we could just pass in this operator? It turns out that for inline closures, Swift allows
96 | // exactly this.
97 | //
98 | // Here's what that looks like:
99 | reversed = names.sorted(>)
100 |
101 | // If you want to just sort a mutable copy of an array (in place) you can use the sort() method
102 | var mutableCopyOfNames = names
103 |
104 | mutableCopyOfNames.sort(>)
105 |
106 | mutableCopyOfNames
107 |
108 | // ------------------------------------------------------------------------------------------------
109 | // Trailing Closures
110 | //
111 | // Trailing Closures refer to closures that are the last parameter to a function. This special-case
112 | // syntax allows a few other syntactic simplifications. In essence, you can move trailing closures
113 | // just outside of the parameter list. Swift's sorted() member function uses a trailing closure for
114 | // just this reason.
115 | //
116 | // Let's go back to our original call to sort with a fully-formed closure and move the closure
117 | // outside of the parameter list. This resembles a function definition, but it's a function call.
118 | reversed = names.sorted {
119 | (s1: String, s2: String) -> Bool in
120 | return s1 > s2
121 | }
122 |
123 | // Note that the opening brace for the closure must be on the same line as the function call's
124 | // ending paranthesis. This is the same functinon call with the starting brace for the closure
125 | // moved to the next line. This will not compile:
126 | //
127 | // reversed = sort(names)
128 | // {
129 | // (s1: String, s2: String) -> Bool in
130 | // return s1 > s2
131 | // }
132 |
133 | // Let's jump back to our simplified closure ({$0 > $1}) and apply the trailing closure principle:
134 | reversed = names.sorted {$0 > $1}
135 |
136 | // Another simplification: if a function receives just one closure as the only parameter, you can
137 | // remove the () from the function call. First, we'll need a function that receives just one
138 | // parameter, a closure:
139 | func returnValue(f: () -> Int) -> Int
140 | {
141 | // Simply return the value that the closure 'f' returns
142 | return f()
143 | }
144 |
145 | // Now let's call the function with the parenthesis removed and a trailing closure:
146 | returnValue {return 6}
147 |
148 | // And if we apply the simplification described earlier that implies the return statement for
149 | // single-expresssion closures, it simplifies to this oddly-looking line of code:
150 | returnValue {6}
151 |
152 | // ------------------------------------------------------------------------------------------------
153 | // Capturing Values
154 | //
155 | // The idea of capturing is to allow a closure to access the variables and constants in their
156 | // surrounding context.
157 | //
158 | // For example, a nested function can access contstans and variables from the function in which
159 | // it is defined. If this nested function is returned, each time it is called, it will work within
160 | // that "captured" context.
161 | //
162 | // Here's an example that should help clear this up:
163 | func makeIncrementor(forIncrement amount: Int) -> () -> Int
164 | {
165 | var runningTotal = 0
166 |
167 | // runningTotal and amount are 'captured' for the nested function incrementor()
168 | func incrementor() -> Int
169 | {
170 | runningTotal += amount
171 | return runningTotal
172 | }
173 |
174 | // We return the nested function, which has captured it's environment
175 | return incrementor
176 | }
177 |
178 | // Let's get a copy of the incrementor:
179 | var incrementBy10 = makeIncrementor(forIncrement: 10)
180 |
181 | // Whenever we call this function, it will return a value incremented by 10:
182 | incrementBy10() // returns 10
183 | incrementBy10() // returns 20
184 |
185 | // We can get another copy of incrementor that works on increments of 3.
186 | var incrementBy3 = makeIncrementor(forIncrement: 3)
187 | incrementBy3() // returns 3
188 | incrementBy3() // returns 6
189 |
190 | // 'incrementBy10' and 'incrementBy3' each has its own captured context, so they work independently
191 | // of each other.
192 | incrementBy10() // returns 30
193 |
194 | // Closures are reference types, which allows us to assign them to a variable. When this happens,
195 | // the captured context comes along for the ride.
196 | var copyIncrementBy10 = incrementBy10
197 | copyIncrementBy10() // returns 40
198 |
199 | // If we request a new incremntor that increments by 10, it will have a separate and unique captured
200 | // context:
201 | var anotherIncrementBy10 = makeIncrementor(forIncrement: 10)
202 | anotherIncrementBy10() // returns 10
203 |
204 | // Our first incrementor is still using its own context:
205 | incrementBy10() // returns 50
206 |
--------------------------------------------------------------------------------
/7. Closures.playground/timeline.xctimeline:
--------------------------------------------------------------------------------
1 |
2 |
4 |
5 |
7 |
8 |
9 |
10 |
--------------------------------------------------------------------------------
/8. Enumerations.playground/contents.xcplayground:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/8. Enumerations.playground/section-1.swift:
--------------------------------------------------------------------------------
1 | // ------------------------------------------------------------------------------------------------
2 | // Things to know:
3 | //
4 | // * Enumerations in Swift are different from their popular counterparts in C-like languages.
5 | // Rather that containing "one of a set of integer values" like most C-like languages, Swift's
6 | // enumerations can be thought of as holding one named type of a given set of named types.
7 | //
8 | // To clarify: Rather than holding an integer value that has been pre-defined integer value
9 | // (Error = -1, Success = 0) an enumeration in Swift only associates a name with a type (like
10 | // Int, String, Tuple, etc.) These elements of the enumeration can then be assigned "Associated
11 | // Values." For example, an enumeration can store an "Error" which is a Tuple with an Int value
12 | // for the error code and a String containing the message. Each time a function or piece of code
13 | // assignes or returns an Error(Int, String), it can set populate the Tuple with Int/String par
14 | // for the specific error condition.
15 | //
16 | // * Alternative to the enumeration storing named types, Swift enumerations can have a type. If
17 | // that type is Int, then they will behave more like their C-style counterparts.
18 | // ------------------------------------------------------------------------------------------------
19 |
20 | // Here is the simple enumeration.
21 | //
22 | // Unlike their C counterparts, the members of the enumeration below are not integer values (0,
23 | // 1, 2, etc.) Instead, each member is a fully-fledged value in its own right.
24 | enum Planet
25 | {
26 | case Mercury
27 | case Venus
28 | case Earth
29 | case Mars
30 | case Jupiter
31 | case Saturn
32 | case Uranus
33 | case Neptune
34 | }
35 |
36 | // You can also combine members onto a single line if you prefer, or mix them up. This has no
37 | // effect on the enumeration itself.
38 | enum CompassPoint
39 | {
40 | case North, South
41 | case East, West
42 | }
43 |
44 | // Let's store an enumeration value into a variable. We'll let the compiler infer the type:
45 | var directionToHead = CompassPoint.West
46 |
47 | // Now that directionToHead has a CompassPoint type (which was inferred) we can set it to a
48 | // different CompassPoint value using a shorter syntax:
49 | directionToHead = .East
50 |
51 | // We can use a switch to match values from an enumeration.
52 | //
53 | // Remember that switches have to be exhaustive. But in this case, Swift knows that the CompassType
54 | // enumeration only has 4 values, so as long as we cover all 4, we don't need the default case.
55 | switch directionToHead
56 | {
57 | case .North:
58 | "North"
59 | case .South:
60 | "South"
61 | case .East:
62 | "East"
63 | case .West:
64 | "West"
65 | }
66 |
67 | // ------------------------------------------------------------------------------------------------
68 | // Associated Values
69 | //
70 | // Associated values allows us to store information with each member of the switch using a Tuple.
71 | //
72 | // The following enumeration will store not only the type of a barcode (UPCA, QR Code) but also
73 | // the data of the barcode (this is likely a foreign concept for most.)
74 | enum Barcode
75 | {
76 | case UPCA(Int, Int, Int) // UPCA with associated value type (Int, Int, Int)
77 | case QRCode(String) // QRCode with associated value type of String
78 | }
79 |
80 | // Let's specify a UPCA code (letting the compiler infer the enum type of Barcode):
81 | var productBarcode = Barcode.UPCA(0, 8590951226, 3)
82 |
83 | // Let's change that to a QR code (still of a Barcode type)
84 | productBarcode = .QRCode("ABCDEFGHIJKLMNOP")
85 |
86 | // We use a switch to check the value and extract the associated value:
87 | switch productBarcode
88 | {
89 | case .UPCA(let numberSystem, let identifier, let check):
90 | "UPCA: \(numberSystem), \(identifier), \(check)"
91 | case .QRCode(let productCode):
92 | "QR: \(productCode)"
93 | }
94 |
95 | // Using the switch statement simplification (see the Switch statement section) to reduce the
96 | // number of occurrances of the 'let' introducer:
97 | switch productBarcode
98 | {
99 | // All constants
100 | case let .UPCA(numberSystem, identifier, check):
101 | "UPCA: \(numberSystem), \(identifier), \(check)"
102 |
103 | // All variables
104 | case var .QRCode(productCode):
105 | "QR: \(productCode)"
106 | }
107 |
108 | // ------------------------------------------------------------------------------------------------
109 | // Raw values
110 | //
111 | // We can assign a type to an enumeration. If we use Int as the type, then we are effectively
112 | // making an enumeration that functions like its C counterpart:
113 | enum StatusCode: Int
114 | {
115 | case Error = -1
116 | case Success = 9
117 | case OtherResult = 1
118 | case YetAnotherResult // Unspecified values are auto-incremented from the previous value
119 | }
120 |
121 | // We can get the raw value of an enumeration value with the rawValue member:
122 | StatusCode.OtherResult.rawValue
123 |
124 | // We can give enumerations many types. Here's one of type Character:
125 | enum ASCIIControlCharacter: Character
126 | {
127 | case Tab = "\t"
128 | case LineFeed = "\n"
129 | case CarriageReturn = "\r"
130 |
131 | // Note that only Int type enumerations can auto-increment. Since this is a Character type,
132 | // the following line of code won't compile:
133 | //
134 | // case VerticalTab
135 | }
136 |
137 | // Alternatively, we could also use Strings
138 | enum FamilyPet: String
139 | {
140 | case Cat = "Cat"
141 | case Dog = "Dog"
142 | case Ferret = "Ferret"
143 | }
144 |
145 | // And we can get their raw value as well:
146 | FamilyPet.Ferret.rawValue
147 |
148 | // We can also generate the enumeration value from the raw value. Note that this is an optional
149 | // because not all raw values will have a matching enumeration:
150 | var pet = FamilyPet(rawValue: "Ferret")
151 |
152 | // Let's verify this:
153 | if pet != .None { "We have a pet!" }
154 | else { "No pet :(" }
155 |
156 | // An example of when a raw doesn't translate to an enum, leaving us with a nil optional:
157 | pet = FamilyPet(rawValue: "Snake")
158 | if pet != .None { "We have a pet" }
159 | else { "No pet :(" }
160 |
161 |
--------------------------------------------------------------------------------
/8. Enumerations.playground/timeline.xctimeline:
--------------------------------------------------------------------------------
1 |
2 |
4 |
5 |
7 |
8 |
9 |
10 |
--------------------------------------------------------------------------------
/9. Classes and Structures.playground/contents.xcplayground:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/9. Classes and Structures.playground/section-1.swift:
--------------------------------------------------------------------------------
1 | // ------------------------------------------------------------------------------------------------
2 | // Things to know:
3 | //
4 | // Classes and structures can both:
5 | //
6 | // * Define properties to store values
7 | // * Define methods to provide functionality
8 | // * Define subscripts to provide access to their values using subscript syntax
9 | // * Define initializers to set up their initial state
10 | // * Be extended to expand their functionality beyond a default implementation
11 | // * Conform to protocols to provide standard functionality of a certain kind
12 | //
13 | // Only classes have:
14 | //
15 | // * Inheritance enables one class to inherit the characteristics of another.
16 | // * Type casting enables you to check and interpret the type of a class instance at runtime.
17 | // * Deinitializers enable an instance of a class to free up any resources it has assigned.
18 | // * Reference counting allows more than one reference to a class instance.
19 | // ------------------------------------------------------------------------------------------------
20 |
21 | // First, let's create a basic structure with a couple of simple properties.
22 | //
23 | // Our structure must have all of its properties initialized, either with default values or through
24 | // initialization (described later.) For now, we'll just ensure they're all initialized with
25 | // default values.
26 | struct Resolution
27 | {
28 | var width = 1280
29 | var height = 1024
30 | }
31 |
32 | // Similarly, a basic class with a few properties, fully initialized. Notice that the first
33 | // property is an instance of the Resolution structure.
34 | //
35 | // Also, note that the final member, the 'name' property, does not need to be initialized because
36 | // optionals are initalized to nil by default.
37 | class VideoMode
38 | {
39 | var resolution = Resolution()
40 | var interlaced = false
41 | var frameRate = 0.0
42 | var name: String?
43 | }
44 |
45 | // Here are some instances of our structure and class:
46 | var someResolution = Resolution()
47 | var someVideoMode = VideoMode()
48 |
49 | // ------------------------------------------------------------------------------------------------
50 | // Accessing properties
51 | //
52 | // We can access members of the class or structure using the dot operator:
53 | someResolution.width
54 | someVideoMode.resolution.width
55 |
56 | // In Objective-C, if an object contained a structure, the sub-properties (the properties of the
57 | // structure inside the object) could not be modified directly. Instead the entire structure would
58 | // have to be replaced completely. This is not the case for Swift.
59 | someVideoMode.resolution.width = 2880
60 | someVideoMode.resolution.height = 1800
61 |
62 | // ------------------------------------------------------------------------------------------------
63 | // Structures and Enumerations are Value Types
64 | //
65 | // This means that when passing an instance of a structure or enumeration to a function (or
66 | // assigning an instance of a structure or enumeration to another value), the structure or
67 | // enumeration is copied.
68 | //
69 | // Let's create two independent copies of a Resolution structure
70 | let constantResolution = Resolution()
71 | var variableResolution = constantResolution
72 |
73 | // We can modify the variable resolution:
74 | variableResolution.width = 320
75 | variableResolution.height = 200
76 |
77 | // We can see that the original (from where the variable copy originated) is unchanged:
78 | constantResolution
79 |
80 | // Note that since structures and enumerations are value types, we are unable to modify the
81 | // contents of constant intances.
82 | //
83 | // The following will not compile:
84 | //
85 | // constantResolution.width = 320
86 |
87 | // ------------------------------------------------------------------------------------------------
88 | // Classes are Reference Types:
89 | //
90 | // This means that when passing an instance of an object to a function (or assigning an instance
91 | // of an object to another value), the new value will hold a reference to the original object.
92 | //
93 | // Let's create an object and assign it's instance to another variable:
94 | let constantVideoMode = VideoMode()
95 | var variableVideoMode = constantVideoMode
96 |
97 | // If we modify the variable..
98 | variableVideoMode.frameRate = 240
99 |
100 | // ...we can see that the other instance is also modified:
101 | constantVideoMode.frameRate
102 |
103 | // In addition to this, we can even modify the 'constantVideoMode' instance. This is the case
104 | // because it is a reference type and modifing the contents do not modify the reference itself.
105 | constantVideoMode.frameRate = 24
106 |
107 | // We cannot, however, modify the instance variable.
108 | //
109 | // This line of code will not compile:
110 | //
111 | // constantVideoMode = VideoMode
112 |
113 | // ------------------------------------------------------------------------------------------------
114 | // Memberwise Initializers for Structure Types
115 | //
116 | // We can set the properties without the need to create a specialiized init routine. If a struct
117 | // (not a class) does not have any initializers, then Swift will provide a "Memberwise Initializer"
118 | // for us automatically.
119 | //
120 | // Here's what tha memberwise initializer looks like. It's pretty self-explanatory in that it is
121 | // an initializer that includes one externally named parameter for each property in the structure.
122 | let vga = Resolution(width: 640, height: 480)
123 |
124 | // ------------------------------------------------------------------------------------------------
125 | // Identity operators
126 | //
127 | // Since classes are reference types, we can check to see if they are 'identical' with the
128 | // Identity (===) operator:
129 | someVideoMode === variableVideoMode
130 | constantVideoMode === variableVideoMode
131 |
132 | // Identical-to is not the same as equal to:
133 | //
134 | // The following line will not compile as it uses the equality operator and VideoMode hasn't
135 | // defined an equality operator:
136 | //
137 | // constantVideoMode == variableVideoMode
138 |
--------------------------------------------------------------------------------
/9. Classes and Structures.playground/timeline.xctimeline:
--------------------------------------------------------------------------------
1 |
2 |
4 |
5 |
7 |
8 |
9 |
10 |
--------------------------------------------------------------------------------
/99. Not The End.playground/contents.xcplayground:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/99. Not The End.playground/section-1.swift:
--------------------------------------------------------------------------------
1 | // ------------------------------------------------------------------------------------------------
2 | // ____ _ _
3 | // / ___|___ _ __ __ _ _ __ __ _| |_ ___| |
4 | // | | / _ \| '_ \ / _` | '__/ _` | __/ __| |
5 | // | |__| (_) | | | | (_| | | | (_| | |_\__ \_|
6 | // \____\___/|_| |_|\__, |_| \__,_|\__|___(_)
7 | // |___/
8 | //
9 | // You've made it to the end!
10 | //
11 | // ------------------------------------------------------------------------------------------------
12 |
13 | // ------------------------------------------------------------------------------------------------
14 | // There's Still More To Learn
15 | //
16 | // These playgrounds are taken directly from the Language Guide section of Apple's book titled
17 | // "The Swift Programming Language".
18 | //
19 | // The book also includes a Language Reference, which discusses the language in a more terse form
20 | // with greater detail (including the grammar.) It's not as dry as reading the C++ Ansi Spec, but
21 | // it's still rather detailed. In fact, some of the information from these playgrounds came from
22 | // the Language Reference section.
23 | //
24 | // The good news is that having managed to get through these playgrounds, you'll probably find
25 | // the Language Reference to be rather quick reading, chock full of additional goodies that you
26 | // never knew about (because the Language Guide and these Playgrounds don't touch on.)
27 | //
28 | // For example, how would you code the assert function such that the first parameter is executed
29 | // and evaluated to a Bool for use in determining an error condition?
30 | var pi = 3.14159
31 | assert(pi > 3.14, "Pi is too small")
32 |
33 | // Do you know why this compiles?
34 | func doSomeMagic(#a: Int)(b: Int) -> Int
35 | {
36 | return a + b
37 | }
38 |
39 | // ...or why it can be executed like this?
40 | doSomeMagic(a: 10)(b: 10)
41 |
42 | // You'll also learn about Metatypes and did you know that Swift's operator precedence is slightly
43 | // different than C?
44 | //
45 | // This is clearly stuff you should know before submitting your forehead to the wall.
46 | //
47 | // You'll learn about these constants:
48 | __FILE__ + "(" + String(__LINE__) + "): " + __FUNCTION__ + ":" + String(__COLUMN__)
49 |
50 | // Furthermore, don't let somebody else's code confuse you when you see something like this in
51 | // their code and realize that it actually compiles!
52 | var ohrly = pi.dynamicType.infinity
53 |
54 | // Most importantly, you'll solidify your understanding of the concepts that were presented in
55 | // these playgrounds.
56 | //
57 | // Happy coding!
58 | //
59 | // - Paul Nettle
60 |
--------------------------------------------------------------------------------
/99. Not The End.playground/timeline.xctimeline:
--------------------------------------------------------------------------------
1 |
2 |
4 |
5 |
7 |
8 |
9 |
10 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | learn-swift
2 | ===========
3 |
4 | Learn Apple's Swift programming language interactively through these playgrounds.
5 |
6 | ###Target Audience
7 |
8 | Programmers familiar with C++ and/or Objective-C.
9 |
10 | ###What you'll need
11 |
12 | You will need XCode 6.0 GM (or later) or 6.1 Beta 2 (or later) and probably
13 | a Mac to run it on.
14 |
15 | ###Purpose & Goal
16 |
17 | More than a primer, these playgrounds are intended to get programmers up to
18 | speed on Swift as fast as possible so they can begin using Swift productively.
19 |
20 | These playgrounds only cover the language. They do not dig into the Swift
21 | Standard Library or other APIs.
22 |
23 | To increase your understanding, you are encouraged to experiment with them
24 | along the way. Play the 'what-if' game. These are live playgrounds which offer
25 | near-realtime feedback of edits.
26 |
27 | ###Source of Content
28 |
29 | I created these while working my way through the "Language Guide" section of
30 | Apple's book, "The Swift Programming Language". I feel the information from
31 | that section is represented here fairly completely and very concisely. Many
32 | of the samples come directly from the book. Some portions from the book's
33 | "Language Reference" section were also included where I felt a bit more
34 | information was helpful.
35 |
36 | If you don't already have the book, it's free. You should probably get it.
37 |
38 | ###Contributors
39 |
40 | Thanks to Rafał Wójcik for his quick work to update these playgrounds to
41 | incorporate the Swift language changes that came with XCode Beta 3.
42 |
--------------------------------------------------------------------------------