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