├── .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 | --------------------------------------------------------------------------------